Chromium pèse environ 280 Mo. La limite d’un package décompressé sur AWS Lambda est de 250 Mo. Si tu as déjà essayé de lancer npm install puppeteer puis de déployer directement sur Lambda, tu sais déjà à quoi ressemble le calcul : ça ne passe pas.
J’ai passé assez de nuits à déboguer des erreurs du genre « Failed to launch the browser process » à 2 h du matin pour savoir qu’ici, il faut une vraie comparaison, pas un tuto qui te montre une seule méthode en te laissant deviner les deux autres. C’est exactement ce que tu vas trouver ici : Layers vs images de conteneur vs upload ZIP direct, une matrice de compatibilité vraiment à jour pour 2026, et une section dépannage sur les cinq erreurs les plus probables.
Qu’est-ce que Puppeteer sur AWS Lambda, et pourquoi s’en servir ?
Puppeteer est une bibliothèque Node.js qui pilote Chromium en mode headless via le Chrome DevTools Protocol. Lambda est le service de calcul serverless d’AWS : tu payes à l’exécution, la plateforme s’adapte toute seule, et tu n’as jamais à gérer de serveur. En les combinant, tu obtiens une solution d’automatisation de navigateur capable de lancer des centaines d’exécutions en parallèle sans provisionner une seule instance EC2.
Les cas d’usage reviennent souvent d’une équipe à l’autre : scraping web, génération de captures d’écran et de PDF, monitoring synthétique, pré-rendu de SPA pour le SEO, et tests UI automatisés. Le vrai problème reste toujours le même que celui évoqué plus haut : la taille de Chromium face aux limites de package de Lambda. C’est pour ça que personne ne déploie le paquet puppeteer complet (qui embarque son propre téléchargement de Chromium) sur Lambda. À la place, on utilise puppeteer-core (sans navigateur intégré) avec un binaire Chromium optimisé pour Lambda, le plus souvent @sparticuz/chromium.
Ce simple remplacement — puppeteer-core à la place de puppeteer — règle déjà 80 % du problème de taille avant même d’écrire une ligne de configuration de déploiement.
Layers vs image de conteneur vs ZIP : choisis d’abord ton approche
Un point qui m’a vraiment agacé pendant mes recherches : presque tous les guides existants ne couvrent qu’une seule méthode de déploiement. Les tutoriels AWS SAM utilisent les Layers. Les exemples CDK utilisent Docker. Un post Substack pris au hasard utilise un ZIP brut avec un binaire Chromium hébergé sur S3. Personne ne met les trois options côte à côte, ce qui fait qu’on saute complètement la première vraie décision à prendre : quelle méthode convient à mon cas ?
Corrigeons ça.
| Critère | Lambda Layers | Image de conteneur (Docker) | Upload ZIP direct |
|---|---|---|---|
| Taille max du package | 250 Mo décompressés (tous layers confondus) | Image de 10 Go | 250 Mo décompressés |
| Complexité de déploiement | Moyenne (gestion des ARN de layer) | Plus élevée (Dockerfile + push ECR) | La plus faible (zip puis upload) |
| Impact sur le cold start | Modéré | Légèrement plus élevé (pull d’image plus volumineux) | Modéré |
| Mise à jour de Chromium | Republier une version du layer | Reconstruire l’image | Réuploader le zip |
| Idéal pour | Prototypes rapides, utilisateurs de Serverless Framework | Charges de production, équipes avec CI Docker | Petites fonctions ponctuelles |
| Support IaC | SAM, Serverless Framework | CDK, SAM, Terraform | Console, n’importe quel IaC |
Les limites de 250 Mo et 10 Go viennent directement de la documentation des quotas Lambda d’AWS — ce n’est pas un chiffre qui change souvent, mais c’est la contrainte qui détermine toute ta stratégie de déploiement dès le départ.
Mon conseil, pour ce qu’il vaut : si tu es en phase de prototypage ou déjà sur Serverless Framework, commence avec les Layers. Si tu pars en production et que ton équipe utilise déjà Docker en CI/CD, opte pour l’image de conteneur : le plafond de 10 Go te laisse de la marge. Si tu as juste besoin d’une fonction pour faire quelques captures d’écran de temps en temps, le ZIP direct est la solution la plus simple.
Les trois approches reposent sur la même base technique : puppeteer-core + @sparticuz/chromium. La méthode de déploiement change seulement la manière d’emballer l’ensemble, pas ce que tu embarques.

La matrice de compatibilité 2026 (arrête de deviner)
C’est ici que les gens perdent des mois, pas des heures. Sur Stack Overflow et dans les issues GitHub, la plainte la plus fréquente n’est pas « comment je déploie ça », mais « pourquoi mon déploiement qui fonctionnait s’est cassé sans bruit après une mise à jour npm ». Le coupable est presque toujours un décalage entre @sparticuz/chromium, puppeteer-core et le runtime Node.js.
D’abord, point essentiel : chrome-aws-lambda (le paquet original d’alixaxel) est obsolète. Il ne fonctionne plus correctement sur Node 18+ et n’a pas suivi le rythme des versions de Chromium. Si tu tombes sur un tutoriel qui le mentionne, ferme l’onglet : il est dépassé. Tous les guides actuels devraient plutôt t’orienter vers @sparticuz/chromium.
Utilise cette règle de compatibilité au lieu d’essayer d’aligner les versions à l’œil :
| Composant | Règle de version | Ce qu’il faut vérifier avant le déploiement |
|---|---|---|
puppeteer-core | Choisis la version de Puppeteer dont ton application a besoin | Releve la version de Chromium prise en charge par cette version de Puppeteer |
@sparticuz/chromium | Son numéro majeur suit le majeur de Chromium, pas celui de Puppeteer | Fais correspondre la version au build Chromium indiqué dans le tableau de compatibilité de Puppeteer, puis lis les notes de version Sparticuz |
| Runtime Node.js de AWS Lambda | Utilise un runtime Lambda encore supporté | Lance un test d’invocation après chaque mise à jour du runtime ou d’un package |
| Architecture | Le paquet npm contient des binaires x64 ; la prise en charge arm64 commence avec Chromium v135 via un layer ou un pack distant | Fais correspondre exactement l’architecture Lambda, l’artefact layer/pack et la version de Chromium |
Je ne fige volontairement pas ici une paire de versions précise, parce que @sparticuz/chromium suit le cycle de publication de Chromium et n’utilise pas le versioning sémantique classique. Commence par la page officielle de compatibilité Chromium de Puppeteer, note le majeur Chromium pris en charge par la version de Puppeteer que tu as choisie, puis sélectionne le même majeur pour @sparticuz/chromium. Enfin, consulte les notes de version Sparticuz pour les changements cassants au niveau patch et les détails d’architecture. N’installe pas le même numéro majeur pour les deux paquets sauf si ce mapping est explicitement confirmé par ces deux sources.

Comment déployer Puppeteer sur AWS Lambda avec Lambda Layers
Un Lambda Layer permet d’emballer Chromium séparément du code de ta fonction, ce qui garde ton handler léger et te permet de réutiliser le même layer Chromium sur plusieurs fonctions. C’est l’option la plus proche d’un « démarrage rapide » dans tout cet univers.
Étape 1 : installer puppeteer-core et le package -min
Quand les fichiers Chromium vivent dans un Lambda Layer, garde le package de la fonction aussi léger que possible en utilisant @sparticuz/chromium-min. Remplace les placeholders par les versions compatibles que tu as validées plus haut :
npm install puppeteer-core@$PUPPETEER_VERSION \
@sparticuz/chromium-min@$CHROMIUM_VERSION
Tu installes puppeteer-core — pas puppeteer — parce qu’il évite le téléchargement automatique du navigateur. Le package -min fournit les aides au lancement, tandis que le layer fournit les fichiers Chromium compressés en Brotli sous /opt/chromium.
Étape 2 : créer ou référencer un Lambda Layer Chromium
Utilise l’archive de layer spécifique à l’architecture fournie avec une release officielle Sparticuz, ou construis l’archive depuis le dépôt officiel. Pour Lambda x86_64, la procédure documentée est :
git clone --depth=1 https://github.com/sparticuz/chromium.git
cd chromium
make chromium.x64.zip
Ça produit chromium.x64.zip. Téléverse-le sur S3 puis publie-le comme Lambda Layer avec le runtime et l’architecture réellement utilisés. Pour arm64, utilise l’artefact de release arm64 correspondant ou la cible de build adaptée ; n’attache jamais une archive x64 à une fonction arm64.
Si tu utilises SAM, rattache directement l’ARN du layer dans ton template.yaml :
Resources:
PuppeteerFunction:
Type: AWS::Serverless::Function
Properties:
Layers:
- arn:aws:lambda:us-east-1:XXXXXXXXXXXX:layer:chromium-layer:1
Étape 3 : écrire le handler Lambda
Voici un handler fonctionnel qui ouvre une URL et renvoie le titre de la page :
import puppeteer from "puppeteer-core";
import chromium from "@sparticuz/chromium-min";
export const handler = async () => {
const browser = await puppeteer.launch({
args: puppeteer.defaultArgs({ args: chromium.args, headless: "shell" }),
executablePath: await chromium.executablePath("/opt/chromium"),
headless: "shell",
});
try {
const page = await browser.newPage();
await page.goto("https://example.com", { waitUntil: "domcontentloaded" });
return { title: await page.title() };
} finally {
await browser.close();
}
};
Note le bloc finally. Ferme toujours le navigateur à cet endroit : sinon, les environnements Lambda réutilisés accumulent des processus navigateur zombies d’une invocation à l’autre, et tu finiras par tomber sur d’étranges erreurs mémoire sans rapport avec ton code.
Étape 4 : configurer la mémoire, le timeout et l’architecture
Régle la mémoire à au moins 1024 Mo — je recommanderais 1536 à 2048 Mo pour tout ce qui dépasse une simple capture d’écran. Définis le timeout à au moins 60 secondes. Verrouille l’architecture sur x86_64, sauf si tu as explicitement confirmé le support arm64 pour ta version exacte de Chromium (ça varie selon les releases).
Étape 5 : déployer et tester
sam build && sam deploy --guided
Déclenche ensuite une invocation avec un événement de test, puis consulte immédiatement CloudWatch Logs si quelque chose déraille : 90 % des erreurs de la section dépannage ci-dessous y apparaissent clairement.
Comment déployer Puppeteer sur AWS Lambda avec des images de conteneur (Docker)
Les images de conteneur résolvent totalement le problème des 250 Mo en offrant un plafond de 10 Go à la place. C’est généralement le meilleur choix pour les charges de production, surtout si ton équipe a déjà Docker dans sa chaîne CI.
Étape 1 : créer le Dockerfile
Pars d’une image de base AWS Lambda officielle pour Node.js, installe tes dépendances et définis le handler :
FROM public.ecr.aws/lambda/nodejs:20
COPY package*.json ./
RUN npm install --production
COPY . .
CMD ["index.handler"]
Selon ton package Chromium, tu devras peut-être installer quelques bibliothèques partagées avec yum install (j’y reviens dans la section dépannage) — @sparticuz/chromium embarque la plupart de ce qu’il lui faut, ce qui réduit nettement cette friction par rapport à une installation manuelle de Chrome complet.
Étape 2 : construire puis pousser dans Amazon ECR
aws ecr create-repository --repository-name puppeteer-lambda
docker build -t puppeteer-lambda .
docker tag puppeteer-lambda:latest <account-id>.dkr.ecr.<region>.amazonaws.com/puppeteer-lambda:latest
aws ecr get-login-password | docker login --username AWS --password-stdin <account-id>.dkr.ecr.<region>.amazonaws.com
docker push <account-id>.dkr.ecr.<region>.amazonaws.com/puppeteer-lambda:latest
Garde l’image dans la même région que ta fonction Lambda : les pulls inter-régions ajoutent une latence dont tu n’as pas besoin.
Étape 3 : créer la fonction Lambda à partir de l’image de conteneur
Pointe ta fonction vers l’URI de l’image ECR via la CLI ou CDK, puis règle la mémoire (1536 à 2048 Mo) et le timeout (60 à 120 secondes) de la même façon que pour un déploiement basé sur un layer.
Étape 4 : déployer et tester
Lance une invocation de test et vérifie la sortie. Le principal compromis par rapport aux Layers : des cold starts légèrement plus élevés à cause du pull d’image plus gros, mais beaucoup plus de marge pour les dépendances.
Comment déployer Puppeteer sur AWS Lambda avec un upload ZIP direct
C’est l’option la plus simple : pas de layer à gérer, pas de Docker à construire. Idéal pour les prototypes ou pour une fonction isolée qui n’a pas vocation à devenir toute une plateforme d’automatisation de navigateur.
Étape 1 : installer les dépendances en local
Pour un ZIP autonome, utilise puppeteer-core + @sparticuz/chromium et fige les deux versions. Le package complet contient les fichiers Chromium compressés et les extrait dans /tmp à l’exécution. Utilise @sparticuz/chromium-min uniquement lorsque ces fichiers sont fournis séparément via un Lambda Layer ou une URL de pack distant rapide ; le package -min n’embarque pas lui-même les fichiers Brotli.
Étape 2 : regrouper puis zipper la fonction
npm install --production
zip -r function.zip . -x "*.git*"
L’option --production compte ici — les dépendances de développement grignotent ton budget de 250 Mo pour rien.
Étape 3 : téléverser et configurer la fonction Lambda
aws lambda update-function-code --function-name my-puppeteer-fn --zip-file fileb://function.zip
Si ton ZIP dépasse 50 Mo, tu ne pourras pas le téléverser directement via la console ou une simple commande CLI : il faudra d’abord le mettre sur S3 puis référencer l’URI S3. Régle la mémoire, le timeout et l’architecture comme pour les deux méthodes précédentes.
Étape 4 : déployer et tester
Utilise le même enchaînement invocation puis vérification des logs. Avec le package complet, chromium.executablePath() n’a pas besoin d’argument. Avec chromium-min, passe le chemin exact du layer ou l’URL du pack distant, par exemple chromium.executablePath("/opt/chromium") pour la structure de layer ci-dessus. Un pack distant ajoute un téléchargement au premier cold start, donc héberge-le au plus près de la fonction et vérifie bien la version de l’artefact et l’architecture.
Les paramètres de puppeteer.launch() qui fonctionnent vraiment sur Lambda
C’est le snippet que tout le monde copie-colle, donc autant le faire correctement. L’environnement d’exécution Lambda n’a pas /dev/shm, n’accorde pas d’accès GPU et fonctionne avec des permissions restreintes — ce qui veut dire que l’appel puppeteer.launch() par défaut, qui marche très bien sur ton ordinateur, ne fonctionnera tout simplement pas ici.
const viewport = {
width: 1920,
height: 1080,
deviceScaleFactor: 1,
isMobile: false,
hasTouch: false,
isLandscape: true,
};
const browser = await puppeteer.launch({
args: await puppeteer.defaultArgs({ args: chromium.args, headless: "shell" }),
executablePath: await chromium.executablePath(),
headless: "shell",
defaultViewport: viewport,
});
Le tableau chromium.args fourni par @sparticuz/chromium inclut déjà les flags essentiels pour un environnement serverless — --no-sandbox, --disable-gpu, --disable-dev-shm-usage, et leurs équivalents. C’est précisément l’intérêt d’utiliser ce package plutôt que de gérer toi-même la liste des options : il suit les exigences de Chromium à ta place.

Dépannage : 5 erreurs que tout développeur finit par rencontrer
Aucun guide que j’ai trouvé n’inclut une vraie section dépannage, ce qui est assez surprenant vu que les erreurs sont presque certainement la raison pour laquelle tu lis cet article.
« Failed to launch the browser process »
Cause racine : bibliothèques partagées manquantes (libnss3.so, libatk, etc.) ou executablePath incorrect.
Correction : @sparticuz/chromium regroupe la plupart des dépendances requises, c’est pourquoi il est recommandé plutôt que de construire ton propre binaire Chromium. Pour un déploiement Docker, si le problème persiste, ajoute explicitement les bibliothèques manquantes avec yum install dans ton Dockerfile.
« Unzipped size must be smaller than 262144000 bytes »
Cause racine : tu as installé le package complet puppeteer, qui embarque son propre téléchargement Chromium (~400 Mo).
Correction : passe à puppeteer-core + @sparticuz/chromium. Si tu as vraiment besoin de plus d’espace, adopte l’approche image de conteneur et son plafond de 10 Go.
« Browser disconnected » ou timeout sur browser.newPage()
Cause racine : mémoire Lambda insuffisante, ou absence de flags comme --disable-gpu dans les options de lancement.
Correction : monte la mémoire à au moins 1024 Mo (je viserais plus haut — voir les benchmarks plus bas) et assure-toi de passer chromium.args plutôt qu’une liste personnalisée allégée.
Le code fonctionne puis casse après une mise à jour du runtime Lambda
Cause racine : AWS corrige périodiquement le runtime sous-jacent, ce qui peut faire bouger les versions de bibliothèques partagées ou les patchs Node.js.
Correction : fige explicitement la version de @sparticuz/chromium, verrouille la version du runtime Node dans la configuration de la fonction et — c’est le point que les gens sautent souvent — reteste après chaque annonce de runtime AWS, pas seulement quand quelque chose casse.
« Protocol error: Connection closed » après ~30 secondes
Cause racine : le timeout Lambda est plus court que le temps nécessaire au chargement et au rendu de la page.
Correction : augmente le timeout à 60–120 secondes, définis explicitement page.setDefaultNavigationTimeout(), et remplace waitUntil: 'networkidle0' par waitUntil: 'domcontentloaded' si tu n’as pas besoin d’attendre la fin de chaque requête réseau avant de continuer.
Durcissement production : mémoire, cold starts et coût
La plupart des guides se contentent de dire « augmente la mémoire » et s’arrêtent là. Ce n’est pas exploitable — voilà ce qui change vraiment quand on monte la mémoire.
Mémoire vs performance
Lambda alloue du CPU proportionnellement à la mémoire, et c’est ce détail qui piège beaucoup de monde. Plus de mémoire, ce n’est pas seulement « plus de RAM » : c’est aussi plus de CPU, ce qui accélère directement le rendu Chromium. En pratique, les équipes qui mesurent Puppeteer constatent des gains nets en passant de 512 Mo vers la zone 1536–2048 Mo, même si tes chiffres exacts dépendront fortement des pages rendues. Plutôt que de sortir un tableau de benchmark qui sera déjà obsolète quand tu le liras, fais tes propres tests à 512 Mo, 1024 Mo, 1536 Mo et 2048 Mo sur tes vraies pages cibles : dix minutes suffisent pour trouver ton point d’équilibre coût/performance.
Provisioned Concurrency pour réduire les cold starts
Si tu lances un service sensible à la latence — monitoring synthétique, API de captures d’écran en temps réel — les cold starts sont ton ennemi. La Provisioned Concurrency maintient un certain nombre d’environnements d’exécution chauds et prêts à servir, ce qui élimine la pénalité de cold start en contrepartie d’un coût lié à cette capacité idle. Ça vaut particulièrement le coup quand la latence compte plus que l’optimisation pure des coûts.
arm64 (Graviton) pour réduire les coûts
Les fonctions Lambda basées sur Graviton coûtent environ 20 % de moins que leurs équivalents x86_64. Le bémol : la prise en charge arm64 de @sparticuz/chromium a historiquement été plus limitée que x86_64, donc vérifie explicitement ta version figée avant de basculer en production sur Graviton.
VPC ou pas de VPC
Placer ta fonction dans un VPC ajoutait autrefois une latence de cold start significative ; AWS a beaucoup réduit cet écart ces dernières années, mais il n’est pas nul. N’ajoute un VPC que si ta fonction a vraiment besoin d’accéder à des ressources privées comme RDS ou ElastiCache — sinon, évite-le.
Quand sortir complètement de Lambda
Si tes tâches navigateur dépassent régulièrement 15 minutes, nécessitent plus de 10 Go de mémoire ou exigent des sessions navigateur persistantes entre les requêtes, Lambda n’est plus l’outil adapté. ECS Fargate est conçu pour ça : ressources configurables, exécution longue, facturation à la seconde. Lambda est excellent pour des tâches navigateur courtes, en rafale et parallélisables ; c’est le mauvais choix dès que ta charge ressemble davantage à un service persistant.
Quand déployer Puppeteer sur Lambda n’est pas la bonne approche
Voici un point important à considérer honnêtement : une grande partie des développeurs qui arrivent sur des guides « Puppeteer + Lambda » essaient en réalité de résoudre un problème d’extraction de données, pas d’automatisation de navigateur. Si ce dont tu as besoin, c’est de données structurées depuis des pages web — fiches produits, coordonnées, contenu de page — alors tout l’emballage Chromium, le verrouillage de versions et la gestion des layers ci-dessus sont une surcharge inutile.
Reste sur Lambda + Puppeteer si tu as besoin d’un vrai contrôle du navigateur : interactions personnalisées avec des formulaires, capture d’images/PDF, monitoring synthétique ou tests navigateur où tu manipules réellement le DOM de manière programmatique.
Envisage une API d’extraction si ton objectif final est d’obtenir du JSON structuré à partir d’une page web, pas de piloter toi-même une session navigateur. L’Open API de Thunderbit gère le rendu JavaScript, les mesures anti-bot et les CAPTCHA via un simple appel HTTP : POST /extract avec un schéma JSON te renvoie des données structurées, POST /distill produit du Markdown propre. Il existe aussi un serveur MCP (thunderbit_extract, thunderbit_distill) si tu construis un agent IA qui doit extraire des données au milieu d’un workflow sans lancer son propre navigateur.
| Facteur | Lambda + Puppeteer (fait maison) | API d’extraction (ex. Thunderbit) | |---|---|---|---| | Temps de configuration | Plusieurs heures (packaging, layers, débogage) | Quelques minutes (clé API + appel HTTP) | | Maintenance | Continue (verrouillage des versions, mises à jour runtime) | Gérée par le fournisseur | | Gestion anti-bot | Manuelle (stealth plugins, proxies) | Intégrée | | Format de sortie | HTML brut / captures à parser soi-même | JSON structuré via schéma | | Idéal pour | Automatisation complète du navigateur, tests, flux sur mesure | Extraction de données, scraping, ingestion de contenu |
Je vais être direct : si tu passes des heures à déboguer des binaires Chromium juste pour extraire du JSON depuis des pages produits, c’est souvent le signe que tu résous le mauvais problème. Garde la route DIY Lambda pour les cas où tu dois vraiment piloter un navigateur ; pour l’extraction, il existe une voie plus directe. Si tu hésites entre ces options pour un projet précis, notre guide des meilleurs extracteurs web IA détaille davantage le paysage, et l’extension Chrome Thunderbit vaut le coup d’œil si tu veux tester l’approche « extraction d’abord » avant de t’engager.
Conclusion
Trois méthodes de déploiement, un principe récurrent : fige tes versions, donne à Chromium assez de mémoire pour respirer, et choisis la méthode de déploiement en fonction de tes vraies contraintes, pas du premier tutoriel trouvé. Layers pour itérer vite, images de conteneur pour la production, ZIP pour le cas ponctuel simple. Et si ton vrai besoin est l’extraction de données plutôt que l’automatisation de navigateur, il peut être utile de vérifier si une API d’extraction dédiée ne t’évite pas complètement le casse-tête du packaging.
FAQ
Peut-on faire tourner Puppeteer sur AWS Lambda en 2026 ?
Oui — avec puppeteer-core associé à @sparticuz/chromium, déployé via Layers, image de conteneur ou ZIP direct. Le package complet puppeteer et le package obsolète chrome-aws-lambda ne fonctionnent plus de façon fiable sur les runtimes Lambda actuels.
Quelle est la taille maximale d’un package AWS Lambda ? 250 Mo décompressés pour les déploiements via Layers et ZIP ; 10 Go pour les images de conteneur, selon les quotas Lambda d’AWS.
chrome-aws-lambda est-il encore maintenu ?
Non. Le paquet original chrome-aws-lambda (d’alixaxel) est obsolète et casse sur Node 18+. Utilise plutôt @sparticuz/chromium — c’est aujourd’hui le standard maintenu activement.
Combien de mémoire Puppeteer demande-t-il sur AWS Lambda ? 1024 Mo est le minimum pratique ; 1536 à 2048 Mo est la zone où les performances deviennent vraiment confortables. En dessous de 1024 Mo, attends-toi à une exécution nettement plus lente, car Lambda lie l’allocation CPU à la mémoire.
Comment réduire les cold starts de Puppeteer sur Lambda ? Alloue plus de mémoire (ce qui te donne aussi plus de CPU), envisage la Provisioned Concurrency si la latence est critique pour ton cas d’usage, et garde ton package de déploiement aussi léger que possible : chaque dépendance en plus rallonge le cold start.


