O Chromium tem cerca de 280 MB. O limite do pacote descompactado do AWS Lambda é 250 MB. Se você já tentou fazer npm install puppeteer e enviar direto para o Lambda, já sabe como essa conta termina: não fecha.
Já perdi tempo demais depurando erros de "Failed to launch the browser process" às 2 da manhã para achar que esse tema merece uma comparação de verdade — e não mais um tutorial que mostra só um caminho e deixa você adivinhando os outros dois. Então é isso que você vai encontrar aqui: Layers vs. Container Images vs. upload direto de ZIP, uma matriz de compatibilidade realmente atualizada para 2026 e uma seção de solução de problemas com os cinco erros que, estatisticamente, você tem mais chance de enfrentar.
O que é Puppeteer no AWS Lambda (e por que usar)
Puppeteer é uma biblioteca Node.js que controla o Chromium sem interface gráfica via Chrome DevTools Protocol. O Lambda é o serviço de computação sem servidor da AWS — você paga por execução, escala automaticamente e não precisa administrar servidores. Juntando os dois, você ganha uma estrutura de automação de navegador capaz de distribuir centenas de execuções em paralelo sem provisionar uma única instância EC2.
Os casos de uso costumam ser parecidos entre as equipes: scraping de sites, geração de capturas de tela e PDFs, monitoramento sintético, pré-renderização de aplicações single-page para SEO e testes automatizados de interface. O problema é sempre o mesmo que mencionei acima — o tamanho do Chromium versus os limites de pacote do Lambda. Por isso ninguém faz deploy do puppeteer completo (que inclui seu próprio download do Chromium) no Lambda. Em vez disso, usa-se puppeteer-core (sem navegador embutido) junto com um binário do Chromium otimizado para Lambda, normalmente @sparticuz/chromium.
Essa simples troca — puppeteer-core em vez de puppeteer — resolve 80% do problema de tamanho antes mesmo de você escrever uma linha da configuração de deploy.
Layers vs. imagem de container vs. ZIP: escolha o caminho primeiro
Uma coisa que me incomodou na pesquisa foi esta: quase todos os guias existentes cobrem exatamente um método de implantação. Os tutoriais do AWS SAM usam Layers. Os exemplos do CDK usam Docker. Um post aleatório no Substack usa um ZIP bruto com um binário do Chromium hospedado no S3. Ninguém coloca tudo lado a lado — o que faz a primeira decisão realmente importante, isto é, qual método de deploy faz mais sentido para o seu cenário, ser simplesmente pulada.
Vamos corrigir isso.
| Critério | Lambda Layers | Imagem de container (Docker) | Upload direto de ZIP |
|---|---|---|---|
| Tamanho máximo do pacote | 250 MB descompactado (somando todas as layers) | Imagem de 10 GB | 250 MB descompactado |
| Complexidade de deploy | Média (gestão de ARN da layer) | Mais alta (Dockerfile + push para ECR) | A mais baixa (compactar e enviar) |
| Impacto no cold start | Moderado | Um pouco mais alto (pull da imagem maior) | Moderado |
| Fluxo de atualização do Chromium | Republicar a versão da layer | Rebuild da imagem | Reenviar o ZIP |
| Melhor para | Protótipos rápidos, usuários do Serverless Framework | Cargas de produção, times com CI em Docker | Funções simples e pontuais |
| Suporte a IaC | SAM, Serverless Framework | CDK, SAM, Terraform | Console, qualquer IaC |
Os limites de 250 MB e 10 GB vêm diretamente da documentação oficial de quotas do Lambda da AWS — não é um número que tenha mudado muito, mas é a restrição que define toda a sua estratégia de implantação logo de início.
Minha regra prática, se servir de referência: se você está prototipando ou já usa Serverless Framework, comece com Layers. Se vai para produção e seu time já tem CI/CD com Docker, vá de imagem de container — o teto de 10 GB dá bem mais folga. Se você só precisa de uma função para tirar screenshots de vez em quando, o ZIP direto é o caminho com menos burocracia.
Os três caminhos usam a mesma combinação principal de dependências: puppeteer-core + @sparticuz/chromium. O método de implantação muda como você empacota essa dupla, não o que está empacotando.

A matriz de compatibilidade de versão para 2026 (pare de adivinhar)
Esta é a parte que realmente faz as pessoas perderem meses, não horas. A reclamação mais alta no Stack Overflow e nos issues do GitHub não é "como faço o deploy disso" — é "por que meu deploy que funcionava quebrou silenciosamente depois de um npm update". O culpado quase sempre é uma incompatibilidade entre @sparticuz/chromium, puppeteer-core e o runtime do Node.js.
Primeiro, o aviso importante: chrome-aws-lambda (o pacote original da alixaxel) está descontinuado. Ele não funciona bem no Node 18+ e não acompanhou as versões do Chromium. Se você encontrar um tutorial mencionando esse pacote, pode fechar a aba — o conteúdo está desatualizado. Todo guia atual deve apontar para @sparticuz/chromium.
Use esta regra de compatibilidade em vez de tentar combinar majors no olho:
| Componente | Regra de versão | O que verificar antes do deploy |
|---|---|---|
puppeteer-core | Escolha a versão do Puppeteer que sua aplicação precisa | Consulte qual build do Chromium é compatível com essa versão do Puppeteer |
@sparticuz/chromium | O major acompanha o major do Chromium, não do Puppeteer | Compare com o build do Chromium na tabela de suporte do Puppeteer e leia as notas de release do Sparticuz |
| Runtime Node.js do AWS Lambda | Use um runtime de Lambda atualmente suportado | Execute um teste de invocação após cada atualização de runtime ou pacote |
| Arquitetura | O pacote npm inclui binários x64; suporte a arm64 começa com Chromium v135 via layer ou pack remoto | Faça corresponder exatamente a arquitetura do Lambda, o artefato da layer/pack e a versão do Chromium |
Estou deixando de propósito de fixar aqui um par específico de pacotes porque @sparticuz/chromium segue o ciclo de releases do Chromium e não usa versionamento semântico tradicional. Comece pela página oficial de suporte do Puppeteer ao Chromium, veja qual major do Chromium é compatível com a versão de Puppeteer escolhida e então selecione esse major de @sparticuz/chromium. Por fim, leia as notas de release do Sparticuz para detalhes de arquitetura e possíveis mudanças que quebram compatibilidade em patch releases. Não instale o mesmo número major para os dois pacotes, a menos que esse mapeamento esteja confirmado nessas duas fontes.

Como implantar o Puppeteer no AWS Lambda com Lambda Layers
Uma Lambda Layer permite empacotar o Chromium separadamente do código da função. Isso mantém o handler leve e permite reutilizar a mesma layer de Chromium em várias funções. É o mais próximo de um "comece rápido" dentro desse universo.
Passo 1: instale o puppeteer-core e o pacote -min
Quando os arquivos do Chromium ficam em uma Lambda Layer, mantenha o pacote da função pequeno usando @sparticuz/chromium-min. Substitua os placeholders pelas versões compatíveis que você confirmou acima:
npm install puppeteer-core@$PUPPETEER_VERSION \
@sparticuz/chromium-min@$CHROMIUM_VERSION
Você está instalando puppeteer-core — e não puppeteer — porque ele não baixa o navegador automaticamente. O pacote -min fornece os helpers de inicialização, enquanto a layer fornece os arquivos Brotli do Chromium em /opt/chromium.
Passo 2: crie ou referencie uma Lambda Layer de Chromium
Use o arquivo de layer específico da arquitetura incluído em um release oficial do Sparticuz ou gere o pacote a partir do repositório oficial. Para Lambda x86_64, o build documentado é:
git clone --depth=1 https://github.com/sparticuz/chromium.git
cd chromium
make chromium.x64.zip
Isso gera chromium.x64.zip. Envie para o S3 e publique como uma Lambda Layer com o runtime e a arquitetura que você realmente usa. Para arm64, use o artefato arm64 correspondente ou o target de build equivalente; não anexe um arquivo x64 a uma função arm64.
Se você usa SAM, adicione o ARN da layer diretamente no template.yaml:
Resources:
PuppeteerFunction:
Type: AWS::Serverless::Function
Properties:
Layers:
- arn:aws:lambda:us-east-1:XXXXXXXXXXXX:layer:chromium-layer:1
Passo 3: escreva o handler do Lambda
Aqui vai um padrão de handler funcional que acessa uma URL e retorna o título da página:
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();
}
};
Repare no bloco finally. Sempre feche o navegador ali — se não fizer isso, os ambientes quentes do Lambda acumulam processos de navegador zumbis entre execuções, e você acaba esbarrando em erros estranhos de memória que não têm nada a ver com o seu código.
Passo 4: configure memória, timeout e arquitetura
Defina a memória para pelo menos 1024 MB — eu recomendaria 1536–2048 MB para qualquer coisa além de uma captura de tela trivial. Ajuste o timeout para pelo menos 60 segundos. Fixe a arquitetura em x86_64, a menos que você tenha confirmado explicitamente o suporte a arm64 para a versão exata do Chromium que está usando (isso varia de release para release).
Passo 5: faça o deploy e teste
sam build && sam deploy --guided
Dispare com um evento de teste e confira os CloudWatch Logs imediatamente se algo der errado — 90% dos erros da seção de troubleshooting abaixo aparecem claramente nesses logs.
Como implantar o Puppeteer no AWS Lambda com Container Images (Docker)
As imagens de container resolvem de vez a dor do limite de 250 MB, porque oferecem um teto de 10 GB. Em geral, é a melhor escolha para workloads de produção, especialmente se o seu time já usa Docker no pipeline de CI.
Passo 1: crie o Dockerfile
Comece com uma imagem base oficial do AWS Lambda para Node.js, instale as dependências e defina o handler:
FROM public.ecr.aws/lambda/nodejs:20
COPY package*.json ./
RUN npm install --production
COPY . .
CMD ["index.handler"]
Dependendo do seu pacote de Chromium, talvez você precise rodar yum install para algumas bibliotecas compartilhadas (mais sobre isso na seção de troubleshooting) — @sparticuz/chromium já empacota a maior parte do que precisa, o que reduz bastante esse atrito em comparação com instalar o Chrome completo manualmente.
Passo 2: faça build e envie para o 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
Mantenha a imagem na mesma região da sua função Lambda — pulls entre regiões adicionam latência desnecessária.
Passo 3: crie a função Lambda a partir da imagem de container
Aponte a função para a URI da imagem no ECR via CLI ou CDK e defina memória (1536–2048 MB) e timeout (60–120 segundos) do mesmo jeito que faria com um deploy baseado em layer.
Passo 4: faça deploy e teste
Execute com um evento de teste e valide a saída. O principal trade-off em relação às Layers: o cold start tende a ser um pouco maior por causa do pull da imagem mais pesada, mas você ganha muito mais espaço para dependências.
Como implantar o Puppeteer no AWS Lambda com upload direto de ZIP
Esta é a opção sem firulas — sem layers para gerenciar, sem Docker para construir. Boa para protótipos ou para uma função única que não precisa escalar para uma plataforma inteira de automação de navegador.
Passo 1: instale as dependências localmente
Para um ZIP autocontido, use puppeteer-core + @sparticuz/chromium e mantenha as versões fixadas. O pacote completo contém os arquivos compactados do Chromium e os extrai para /tmp em runtime. Use @sparticuz/chromium-min somente quando esses arquivos forem fornecidos separadamente por uma Lambda Layer ou por uma URL rápida de pack remoto; o pacote -min não inclui os arquivos Brotli por conta própria.
Passo 2: empacote e compacte a função
npm install --production
zip -r function.zip . -x "*.git*"
O --production importa aqui — dependências de desenvolvimento consomem seu orçamento de 250 MB sem motivo.
Passo 3: envie e configure a função Lambda
aws lambda update-function-code --function-name my-puppeteer-fn --zip-file fileb://function.zip
Se o seu ZIP passar de 50 MB, você não consegue enviá-lo diretamente pela console ou com um simples comando CLI — será preciso subir para o S3 primeiro e então informar a URI do S3. Configure memória, timeout e arquitetura da mesma forma que nos dois métodos anteriores.
Passo 4: faça deploy e teste
Use o mesmo fluxo de invocar e checar logs. Com o pacote completo, chromium.executablePath() não precisa de argumento. Com chromium-min, passe o diretório exato da layer ou a URL do pack remoto, por exemplo chromium.executablePath("/opt/chromium") para a estrutura de layer mostrada acima. Um pack remoto adiciona trabalho de download ao primeiro cold start, então hospede-o próximo da função e valide a versão e a arquitetura do artefato.
Os args de puppeteer.launch() que realmente funcionam no Lambda
Este é o trecho que todo mundo copia e cola, então vamos acertar. O ambiente de execução do Lambda não tem /dev/shm, não oferece acesso à GPU e roda com permissões restritas — o que significa que a chamada padrão de puppeteer.launch() que funciona no seu notebook simplesmente... não vai funcionar aqui.
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,
});
O array chromium.args do @sparticuz/chromium já traz as flags que importam para um ambiente sem servidor — --no-sandbox, --disable-gpu, --disable-dev-shm-usage e similares. Esse é justamente o motivo de usar o pacote em vez de montar sua própria lista de flags: ele acompanha os requisitos do Chromium para que você não precise fazer isso manualmente.

Troubleshooting: 5 erros que todo desenvolvedor enfrenta
Nenhum guia que encontrei traz uma seção de troubleshooting decente, o que é meio estranho considerando que os erros são quase certamente o motivo de você estar lendo este artigo.
"Failed to launch the browser process"
Causa raiz: bibliotecas compartilhadas ausentes (libnss3.so, libatk, etc.) ou executablePath incorreto.
Correção: @sparticuz/chromium inclui a maior parte das dependências necessárias, e por isso é o pacote recomendado em vez de você montar seu próprio binário do Chromium. Em deploys com Docker, se isso ainda acontecer, instale explicitamente as bibliotecas faltantes no seu Dockerfile com yum install.
"Unzipped size must be smaller than 262144000 bytes"
Causa raiz: você instalou o pacote completo puppeteer, que traz seu próprio download do Chromium (~400 MB).
Correção: troque para puppeteer-core + @sparticuz/chromium. Se você realmente precisar de mais espaço, migre para a abordagem de imagem de container e seu teto de 10 GB.
"Browser disconnected" ou timeout em browser.newPage()
Causa raiz: memória insuficiente no Lambda ou ausência de flags como --disable-gpu nos argumentos de inicialização.
Correção: defina pelo menos 1024 MB de memória (eu iria mais alto — veja os benchmarks abaixo) e garanta que você está passando chromium.args em vez de uma lista customizada enxuta demais.
O código funciona e quebra depois de uma atualização do runtime do Lambda
Causa raiz: a AWS faz patches periódicos no runtime subjacente, o que pode alterar versões de bibliotecas compartilhadas ou patch versions do Node.js sem que você perceba.
Correção: fixe explicitamente a versão do @sparticuz/chromium, fixe a versão do runtime Node na configuração da função e — esta é a parte que muita gente pula — teste novamente após cada anúncio de atualização do runtime da AWS, não apenas quando algo quebra.
"Protocol error: Connection closed" depois de ~30 segundos
Causa raiz: o timeout do Lambda é menor que o tempo necessário para a página carregar e renderizar de fato.
Correção: aumente o timeout para 60–120 segundos, defina page.setDefaultNavigationTimeout() explicitamente e troque waitUntil: 'networkidle0' por waitUntil: 'domcontentloaded' se você não precisa esperar que todas as requisições de rede terminem antes de seguir adiante.
Endurecimento para produção: memória, cold starts e custo
A maioria dos guias diz apenas para "aumentar a memória" e para por aí. Isso não é orientação acionável — aqui está o que realmente muda quando você escala a memória.
Memória vs. desempenho
O Lambda aloca CPU proporcionalmente à memória, e esse é o detalhe que costuma confundir as pessoas. Mais memória não significa só "mais RAM disponível" — significa também mais CPU, o que acelera diretamente a renderização no Chromium. Na prática, equipes que fazem benchmarks com Puppeteer relatam ganhos relevantes ao sair de 512 MB e subir para a faixa de 1536–2048 MB, embora os números exatos dependam bastante das páginas que você renderiza. Em vez de citar uma tabela de benchmark que já vai estar desatualizada quando você ler este texto, faça seu próprio teste com 512 MB, 1024 MB, 1536 MB e 2048 MB nas páginas reais que você usa — é um exercício de dez minutos que mostra exatamente onde está o seu ponto ideal entre custo e desempenho.
Provisioned Concurrency para reduzir cold starts
Se o seu caso é sensível à latência — monitoramento sintético, uma API de screenshots em tempo real — os cold starts são seu inimigo. A Provisioned Concurrency mantém um número definido de ambientes de execução aquecidos e prontos, eliminando a penalidade de cold start ao custo de pagar por essa capacidade ociosa. Vale a pena especialmente quando latência importa mais do que eficiência máxima de custo.
arm64 (Graviton) para economizar
Funções Lambda baseadas em Graviton costumam sair cerca de 20% mais baratas que as equivalentes x86_64. O porém: o suporte a arm64 do @sparticuz/chromium historicamente foi mais limitado do que o de x86_64, então confirme explicitamente para a versão fixada antes de apostar no Graviton em produção.
VPC vs. sem VPC
Colocar a função dentro de uma VPC costumava adicionar uma latência de cold start significativa; a AWS reduziu bastante esse gap nos últimos anos, mas ele ainda não é zero. Só coloque a função em uma VPC se ela realmente precisar acessar recursos privados como RDS ou ElastiCache — fora isso, evite.
Quando sair do Lambda por completo
Se suas tarefas de navegador passam regularmente de 15 minutos, exigem mais de 10 GB de memória ou precisam manter sessões persistentes entre requisições, o Lambda deixa de ser uma boa escolha. O ECS Fargate foi feito exatamente para isso — computação de longa duração, recursos configuráveis e cobrança por segundo. O Lambda é excelente para tarefas de navegador curtas, em rajadas e paralelizáveis; é a ferramenta errada quando o seu workload começa a parecer um serviço persistente.
Quando implantar Puppeteer no Lambda é a abordagem errada
Vale encarar isso com honestidade: uma grande parte dos desenvolvedores que caem em guias de "Puppeteer + Lambda" está, na verdade, tentando resolver um problema de extração de dados, não de automação de navegador. Se o que você realmente precisa é de dados estruturados de páginas web — listas de produtos, informações de contato, conteúdo de páginas — todo o empacotamento do Chromium, o bloqueio de versões e a gestão de layers acima viram uma sobrecarga que você não precisava assumir.
Fique com Lambda + Puppeteer se você precisa de controle real do navegador: interações personalizadas com formulários, pipelines de captura de tela/PDF, monitoramento sintético ou testes baseados em navegador em que você realmente manipula o DOM programaticamente.
Considere uma API de scraping se o seu objetivo final é obter JSON estruturado a partir de uma página, e não uma sessão de navegador controlada por você. A Thunderbit Open API lida com renderização JavaScript, medidas anti-bot e CAPTCHAs em uma única chamada HTTP — POST /extract com um JSON Schema gera dados estruturados, e POST /distill entrega Markdown limpo. Também há um servidor MCP (thunderbit_extract, thunderbit_distill) se você estiver criando um agente de IA que precisa puxar dados no meio do fluxo sem abrir o próprio navegador.
| Fator | Lambda + Puppeteer (faça você mesmo) | API de extração (ex.: Thunderbit) |
|---|---|---|
| Tempo de configuração | Horas (empacotamento, layers, depuração) | Minutos (chave de API + chamada HTTP) |
| Manutenção | Contínua (fixação de versões, atualizações de runtime) | Gerenciada pelo provedor |
| Tratamento anti-bot | Manual (plugins stealth, proxies) | Embutido |
| Formato de saída | HTML bruto/capturas, que você precisa processar | JSON estruturado via schema |
| Melhor para | Automação completa de navegador, testes, fluxos personalizados | Extração de dados, scraping, ingestão de conteúdo |
Vou falar sem rodeios: se você está gastando horas depurando binários do Chromium só para extrair JSON de páginas de produto, isso é sinal de que está resolvendo o problema errado. Deixe a rota DIY com Lambda para quando você realmente precisar dirigir um navegador — para extração, existe um caminho mais direto. Se você estiver avaliando esse trade-off para um projeto específico, nosso guia de AI web scrapers explica o cenário com mais profundidade, e a extensão do Thunderbit para Chrome vale uma olhada se você quiser testar a abordagem orientada a extração antes de decidir por qualquer um dos caminhos.
Conclusão
Três métodos de implantação, um tema recorrente: fixe as versões, dê memória suficiente para o Chromium respirar e combine o método de deploy com suas restrições reais — não com o primeiro tutorial que você encontrou. Layers para iteração rápida, Container Images para escala em produção, ZIP para o caso simples e pontual. E se o que você está fazendo for extração de dados, e não automação de navegador, talvez valha ver se uma API de extração pronta não elimina toda essa dor de empacotamento.
FAQs
É possível rodar Puppeteer no AWS Lambda em 2026?
Sim — usando puppeteer-core combinado com @sparticuz/chromium, implantado via Layers, Container Image ou ZIP direto. O pacote completo puppeteer e o pacote descontinuado chrome-aws-lambda já não funcionam com confiabilidade nos runtimes atuais do Lambda.
Qual é o tamanho máximo de pacote no AWS Lambda? 250 MB descompactado para deploys com Layers e ZIP; 10 GB para deploys com Container Image, conforme as quotas do Lambda da AWS.
O chrome-aws-lambda ainda é mantido?
Não. O pacote original chrome-aws-lambda (da alixaxel) está descontinuado e quebra no Node 18+. Use @sparticuz/chromium em seu lugar — hoje ele é o padrão mantido ativamente.
Quanta memória o Puppeteer precisa no AWS Lambda? 1024 MB é o mínimo prático; 1536–2048 MB é a faixa em que o desempenho começa a ficar realmente confortável. Abaixo de 1024 MB, espere execução visivelmente lenta, já que o Lambda vincula a alocação de CPU à memória.
Como reduzir o tempo de cold start do Puppeteer no Lambda? Aumente a memória alocada (o que também aumenta a CPU), considere Provisioned Concurrency se a latência for crítica para o seu caso e mantenha o pacote de deploy o mais enxuto possível — cada dependência extra adiciona tempo de cold start.


