Как развернуть Puppeteer на AWS Lambda: сравнение 3 способов

Последнее обновление: August 11, 2026
Deploying a headless browser to serverless infrastructure
AI-сводка
Гайд по деплою, в котором сравниваются Lambda Layers, container images и ZIP-пакеты для Puppeteer, с актуальными рекомендациями по упаковке, совместимости версий и устранению ошибок.

Chromium весит примерно 280 МБ. Лимит AWS Lambda для распакованного пакета — 250 МБ. Если вы хоть раз пробовали выполнить npm install puppeteer и сразу же задеплоить всё в Lambda, вы уже понимаете, почему эта математика не сходится.

Я провёл достаточно времени, разбирая ошибки вроде "Failed to launch the browser process" в 2 часа ночи, чтобы понять: этой теме нужен нормальный разбор, а не очередной туториал, который показывает один способ и заставляет гадать про остальные два. Поэтому здесь будет всё по делу: сравнение Layers, Container Images и прямой загрузки ZIP, актуальная на 2026 год матрица совместимости версий и раздел по отладке пяти ошибок, с которыми вы с высокой вероятностью столкнётесь.

Что такое Puppeteer на AWS Lambda и зачем он вообще нужен

Puppeteer — это библиотека Node.js, которая управляет headless Chromium через Chrome DevTools Protocol. Lambda — это серверлесс-вычисления AWS: вы платите за каждый запуск, масштабирование происходит автоматически, и вам не нужно трогать серверы. Вместе они дают связку для браузерной автоматизации, которая может запускать сотни параллельных задач без единого поднятого EC2-инстанса.

Сценарии использования у команд обычно одни и те же: веб-скрейпинг, генерация скриншотов и PDF, синтетический мониторинг, предрендеринг одностраничных приложений для SEO и автоматизированное UI-тестирование. Проблема всегда одна — размер Chromium по сравнению с лимитами Lambda. Именно поэтому никто не выкладывает в Lambda полный puppeteer (он тащит за собой собственную версию Chromium). Вместо этого используют puppeteer-core без встроенного браузера вместе с оптимизированным под Lambda бинарником Chromium, чаще всего @sparticuz/chromium.

Одна эта замена — puppeteer-core вместо puppeteer — закрывает около 80% проблемы с размером ещё до того, как вы напишете хоть один файл конфигурации деплоя.

Layers, Container Image или ZIP: сначала выберите свой путь

Вот что меня раздражало во время исследования: почти каждый существующий гайд описывает только один способ деплоя. Туториалы по AWS SAM используют Layers. Примеры на CDK идут через Docker. Какой-нибудь пост на Substack показывает сырой ZIP с Chromium-бинарником, лежащим в S3. Никто не ставит их рядом, а значит, первый и главный вопрос — какой метод деплоя подходит именно вам — вообще выпадает.

Давайте это исправим.

КритерийLambda LayersContainer Image (Docker)Прямая загрузка ZIP
Максимальный размер250 МБ после распаковки (суммарно для всех слоёв)10 ГБ на образ250 МБ после распаковки
Сложность деплояСредняя (управление ARN слоя)Выше (Dockerfile + push в ECR)Самая низкая (zip и upload)
Влияние на cold startУмеренноеНемного выше (более крупный образ дольше загружается)Умеренное
Обновление ChromiumОпубликовать новую версию слояПересобрать образПерезагрузить ZIP
Лучше всего подходит дляБыстрых прототипов, пользователей Serverless FrameworkПродакшн-нагрузок, команд с Docker CIПростых одноразовых функций
Поддержка IaCSAM, Serverless FrameworkCDK, SAM, TerraformConsole, любой IaC

И лимит 250 МБ, и лимит 10 ГБ взяты напрямую из официальной документации AWS по квотам Lambda — это не число, которое часто менялось, но именно оно заранее определяет всю стратегию деплоя.

Моя практическая эвристика такая: если вы только прототипируете или уже используете Serverless Framework, начинайте с Layers. Если вы выкатываете это в прод и у команды уже есть Docker CI/CD, берите Container Image — потолок в 10 ГБ даёт заметный запас. Если вам нужна всего одна функция для периодических скриншотов, прямая загрузка ZIP — это минимум лишней возни.

Во всех трёх вариантах базовая связка одна и та же: puppeteer-core + @sparticuz/chromium. Меняется способ упаковки, а не набор зависимостей.

Three abstract packaging choices for a browser workload

Матрица совместимости версий на 2026 год: хватит гадать

Вот что реально отнимает у людей месяцы, а не часы. Самая частая жалоба на Stack Overflow и GitHub issues — не "как это задеплоить", а "почему рабочий деплой молча сломался после обновления npm". Виновник почти всегда один и тот же: несовместимость между @sparticuz/chromium, puppeteer-core и Node.js runtime.

Сначала важное предупреждение: chrome-aws-lambda (оригинальный пакет alixaxel) устарел и больше не поддерживается. На Node 18+ он ломается и не успевает за релизами Chromium. Если в туториале указан этот пакет, можно смело закрывать вкладку — материал устарел. Все актуальные гайды должны ссылаться на @sparticuz/chromium.

Вместо подбора major-версий на глаз используйте эту логику совместимости:

КомпонентПравило по версииЧто проверить перед деплоем
puppeteer-coreБерите ту версию Puppeteer, которая нужна вашему приложениюПосмотрите, какая версия Chromium поддерживается этим релизом Puppeteer
@sparticuz/chromiumЕго major-версия соответствует major-версии Chromium, а не PuppeteerСверьте её с версией Chromium из таблицы поддержки Puppeteer и прочитайте release notes Sparticuz
AWS Lambda Node.js runtimeИспользуйте актуально поддерживаемый runtime LambdaПрогоняйте тестовый вызов после каждого обновления runtime или пакета
Архитектураnpm-пакет содержит x64-бинарники; поддержка arm64 начинается с Chromium v135 через arm64 layer или remote packТочно сопоставьте архитектуру Lambda, артефакт слоя/pack и версию Chromium

Я специально не закрепляю здесь конкретную пару пакетов, потому что @sparticuz/chromium следует циклу релизов Chromium и не использует обычное семантическое версионирование. Начните с официальной страницы Puppeteer Chromium Support, определите major-версию Chromium, которую поддерживает выбранный вами релиз Puppeteer, а затем берите соответствующую major-версию @sparticuz/chromium. После этого обязательно прочитайте release notes Sparticuz — там бывают важные изменения на уровне патчей и нюансы по архитектуре. Не ставьте одинаковый номер major у обоих пакетов просто по совпадению, если это не подтверждено этими двумя источниками.

Abstract serverless browser deployment architecture

Как задеплоить Puppeteer на AWS Lambda через Lambda Layers

Lambda Layer позволяет упаковать Chromium отдельно от кода функции, благодаря чему сам обработчик остаётся компактным, а один и тот же Chromium layer можно переиспользовать в нескольких функциях. Это максимально близко к режиму "быстрого старта" в этой теме.

Шаг 1: установите puppeteer-core и пакет -min

Если файлы Chromium лежат в Lambda Layer, держите пакет функции маленьким и используйте @sparticuz/chromium-min. Подставьте вместо плейсхолдеров совместимые версии, которые вы подтвердили выше:

npm install puppeteer-core@$PUPPETEER_VERSION \
  @sparticuz/chromium-min@$CHROMIUM_VERSION

Вы ставите puppeteer-core, а не puppeteer, потому что он не скачивает браузер автоматически. Пакет -min даёт только вспомогательные функции для запуска, а слой предоставляет Brotli-сжатые файлы Chromium в /opt/chromium.

Шаг 2: создайте или подключите Chromium Lambda Layer

Используйте архив слоя под нужную архитектуру из официального релиза Sparticuz либо соберите архив из официального репозитория. Для Lambda под x86_64 документированная сборка выглядит так:

git clone --depth=1 https://github.com/sparticuz/chromium.git
cd chromium
make chromium.x64.zip

В результате получится chromium.x64.zip. Загрузите его в S3 и опубликуйте как Lambda Layer с тем runtime и той архитектурой, которые вы реально используете. Для arm64 берите соответствующий arm64-артефакт релиза или target сборки; не подключайте x64-архив к функции arm64.

Если вы используете SAM, подключите ARN слоя прямо в template.yaml:

Resources:
  PuppeteerFunction:
    Type: AWS::Serverless::Function
    Properties:
      Layers:
        - arn:aws:lambda:us-east-1:XXXXXXXXXXXX:layer:chromium-layer:1

Шаг 3: напишите Lambda Handler

Вот рабочий шаблон handler'а, который открывает URL и возвращает заголовок страницы:

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();
  }
};

Обратите внимание на блок finally. Всегда закрывайте браузер именно там — если этого не сделать, тёплые Lambda-окружения начнут накапливать "зомби"-процессы браузера между вызовами, и в итоге вы упрётесь в странные ошибки по памяти, которые вообще не связаны с вашим кодом.

Шаг 4: настройте память, таймаут и архитектуру

Поставьте память минимум 1024 МБ — для чего-то сложнее простого скриншота я бы советовал 1536–2048 МБ. Таймаут задайте не меньше 60 секунд. Архитектуру фиксируйте на x86_64, если только вы не подтвердили поддержку arm64 именно для вашей версии Chromium (это зависит от релиза к релизу).

Шаг 5: задеплойте и проверьте

sam build && sam deploy --guided

Запустите тестовый event, а если что-то пошло не так — сразу смотрите CloudWatch Logs. 90% ошибок из раздела ниже там видны довольно быстро и достаточно понятно.

Как задеплоить Puppeteer на AWS Lambda через Container Images (Docker)

Container images полностью снимают головную боль с лимитом 250 МБ, потому что вместо него вы получаете потолок в 10 ГБ. Это обычно лучший выбор для продакшн-нагрузок, особенно если у команды Docker уже есть в CI-пайплайне.

Шаг 1: создайте Dockerfile

Начните с официального AWS Lambda base image для Node.js, установите зависимости и задайте handler:

FROM public.ecr.aws/lambda/nodejs:20

COPY package*.json ./
RUN npm install --production

COPY . .

CMD ["index.handler"]

В зависимости от вашего пакета Chromium, может понадобиться yum install нескольких shared libraries (об этом подробнее в разделе troubleshooting). @sparticuz/chromium уже включает большую часть нужного, поэтому по сравнению с ручной установкой полноценного Chrome это намного менее болезненно.

Шаг 2: соберите образ и отправьте его в 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

Храните образ в том же регионе, где находится ваша Lambda-функция — кросс-региональная загрузка образа только добавляет лишнюю задержку.

Шаг 3: создайте Lambda-функцию из container image

Укажите вашей функции URI образа из ECR через CLI или CDK и настройте память (1536–2048 МБ) и timeout (60–120 секунд) так же, как это делается при деплое через layer.

Шаг 4: задеплойте и проверьте

Запустите тестовый event и проверьте результат. Главное отличие от Layers: cold start обычно чуть выше из-за более крупной загрузки образа, зато у вас намного больше пространства для зависимостей.

Как задеплоить Puppeteer на AWS Lambda через прямую загрузку ZIP

Это вариант без лишней сложности: никаких layers, никакой сборки Docker. Хорошо подходит для прототипов или одной функции, которой не нужно вырастать в полноценную платформу браузерной автоматизации.

Шаг 1: установите зависимости локально

Для самодостаточного ZIP используйте puppeteer-core + @sparticuz/chromium и зафиксируйте обе версии. Полный пакет содержит сжатые файлы Chromium и распаковывает их в /tmp во время выполнения. @sparticuz/chromium-min используйте только тогда, когда эти файлы приходят отдельно через Lambda Layer или через быстрый remote pack URL; сам пакет -min Brotli-файлы не включает.

Шаг 2: соберите проект и упакуйте функцию в ZIP

npm install --production
zip -r function.zip . -x "*.git*"

Флаг --production здесь важен — dev-зависимости просто съедают ваш бюджет 250 МБ без какой-либо пользы.

Шаг 3: загрузите архив и настройте Lambda

aws lambda update-function-code --function-name my-puppeteer-fn --zip-file fileb://function.zip

Если ваш ZIP больше 50 МБ, через консоль или простую CLI-команду его напрямую уже не загрузить — сначала придётся отправить файл в S3 и затем указать S3 URI. Память, таймаут и архитектуру настраивайте так же, как и для двух предыдущих способов.

Шаг 4: задеплойте и проверьте

Используйте тот же цикл: запуск, проверка, логи. В полном пакете chromium.executablePath() не требует аргумента. В chromium-min передавайте точный путь к каталогу слоя или remote pack URL, например chromium.executablePath("/opt/chromium") для описанной выше схемы слоя. Remote pack добавляет загрузку в первый cold start, поэтому держите его ближе к функции и обязательно проверяйте версию артефакта и архитектуру.

Аргументы для puppeteer.launch(), которые реально работают на Lambda

Это тот самый сниппет, который все копируют, так что давайте сделаем его правильно. В среде исполнения Lambda нет /dev/shm, нет GPU и ограничены права доступа — а значит, стандартный puppeteer.launch(), который отлично работает на ноутбуке, здесь просто... не взлетит.

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,
});

Массив chromium.args из @sparticuz/chromium уже содержит флаги, которые важны для серверлесс-среды: --no-sandbox, --disable-gpu, --disable-dev-shm-usage и другие. В этом и смысл пакета — не собирать флаги руками, а использовать набор, который уже учитывает требования Chromium.

A calm diagnostic view of browser and cloud workload health

Troubleshooting: 5 ошибок, с которыми сталкивается почти каждый

Ни один найденный мной гайд не включает нормальный раздел по устранению ошибок, и это странно, потому что ошибки — это, скорее всего, именно причина, по которой вы открыли эту статью.

"Failed to launch the browser process"

Причина: не хватает shared libraries (libnss3.so, libatk и т. п.) либо указан неправильный executablePath.

Решение: @sparticuz/chromium поставляет большую часть нужных зависимостей, поэтому его и рекомендуют вместо самодельной сборки Chromium. Для Docker-деплоя, если ошибка остаётся, явно установите недостающие библиотеки через yum install в Dockerfile.

"Unzipped size must be smaller than 262144000 bytes"

Причина: вы поставили полный пакет puppeteer, который тащит собственную загрузку Chromium (~400 МБ).

Решение: переключитесь на puppeteer-core + @sparticuz/chromium. Если вам действительно нужно больше места, переходите на вариант с Container Image и лимитом 10 ГБ.

"Browser disconnected" или таймаут на browser.newPage()

Причина: недостаточно памяти в Lambda, либо в launch-аргументах отсутствуют флаги вроде --disable-gpu.

Решение: выделите минимум 1024 МБ памяти (я бы рекомендовал больше — см. бенчмарки ниже) и убедитесь, что передаёте chromium.args, а не урезанный кастомный список.

Рабочий код ломается после обновления Lambda runtime

Причина: AWS периодически патчит базовый runtime, и это может менять версии shared libraries или патч-версии Node.js под вами.

Решение: явно закрепите версию @sparticuz/chromium, закрепите версию Node runtime в конфиге функции и — это как раз тот шаг, который многие пропускают — тестируйте после каждого анонса обновления runtime от AWS, а не только когда что-то уже сломалось.

"Protocol error: Connection closed" примерно через 30 секунд

Причина: timeout Lambda меньше, чем нужно странице на загрузку и отрисовку.

Решение: увеличьте timeout до 60–120 секунд, явно задайте page.setDefaultNavigationTimeout(), а если не нужно ждать полного завершения всех сетевых запросов, замените waitUntil: 'networkidle0' на waitUntil: 'domcontentloaded'.

Продакшн-укрепление: память, cold starts и стоимость

Большинство гайдов советуют просто "увеличить память" и на этом заканчивают. Это бесполезный совет — ниже то, что реально меняется при росте объёма памяти.

Память vs. производительность

Lambda выделяет CPU пропорционально памяти, и именно это часто сбивает людей с толку. Больше памяти — это не просто больше RAM, а ещё и более быстрый CPU, а значит, и более быстрое рендеринг Chromium. На практике команды, которые гоняли бенчмарки Puppeteer, обычно получают заметно более быстрые выполнения при росте с 512 МБ до диапазона 1536–2048 МБ, хотя точные цифры сильно зависят от страниц, которые вы рендерите. Вместо того чтобы цитировать таблицу бенчмарков, которая устареет раньше, чем вы её прочитаете, запустите собственный тест на 512 МБ, 1024 МБ, 1536 МБ и 2048 МБ на ваших целевых страницах — это займёт минут десять и точно покажет, где у вас оптимум по цене и скорости.

Provisioned Concurrency для cold starts

Если у вас чувствительный к задержке сценарий — например, синтетический мониторинг или API для скриншотов в реальном времени — cold starts становятся врагом. Provisioned Concurrency держит заданное число окружений тёплыми и готовыми к работе, убирая задержку старта, но вы платите за этот простой резерв. Это оправдано именно тогда, когда важнее задержка, а не максимальная экономия.

arm64 (Graviton) ради экономии

Lambda-функции на Graviton обычно примерно на 20% дешевле, чем x86_64-аналоги. Но есть нюанс: поддержка arm64 у @sparticuz/chromium исторически была ограниченнее, чем у x86_64, поэтому перед переходом на Graviton в проде обязательно проверьте совместимость именно для вашей зафиксированной версии.

VPC против no-VPC

Раньше размещение функции в VPC заметно увеличивало cold-start задержку; AWS за последние годы сильно сократил этот разрыв, но он всё равно не стал нулевым. Помещайте функцию в VPC только если ей действительно нужен доступ к приватным ресурсам вроде RDS или ElastiCache. Если нет — не усложняйте.

Когда пора уходить с Lambda

Если ваши browser-задачи регулярно длятся дольше 15 минут, требуют больше 10 ГБ памяти или нуждаются в постоянных браузерных сессиях между запросами, Lambda уже начинает мешать. Именно для этого и существует ECS Fargate: длительные задачи, настраиваемые ресурсы, оплата по секундам. Lambda отлично подходит для коротких, всплесковых и хорошо параллелящихся задач с браузером, но это плохой инструмент, когда нагрузка больше похожа на постоянный сервис.

Когда Puppeteer на Lambda — не лучший выбор

Есть важная мысль, которую стоит честно принять: большая часть разработчиков, которые приходят к гайдам про "Puppeteer + Lambda", на самом деле решают задачу извлечения данных, а не браузерной автоматизации. Если вам нужны структурированные данные со страниц — список товаров, контакты, контент страницы — вся упаковка Chromium, фиксация версий и управление слоями выше становятся лишней сложностью.

Оставайтесь на Lambda + Puppeteer, если вам нужен настоящий контроль над браузером: взаимодействие с формами, генерация скриншотов/PDF, синтетический мониторинг или browser-based testing, где вы действительно программно управляете DOM.

Рассмотрите scraping API, если ваша конечная цель — получить структурированный JSON со страницы, а не управлять браузерной сессией самостоятельно. Thunderbit Open API обрабатывает JS-рендеринг, антибот-защиту и CAPTCHA через один HTTP-запрос — POST /extract со схемой JSON даёт вам структурированные данные, а POST /distill — чистый Markdown. Есть и MCP-сервер (thunderbit_extract, thunderbit_distill), если вы строите AI-агента, которому нужно подтягивать данные прямо во время рабочего процесса без запуска собственного браузера.

ФакторLambda + Puppeteer (самостоятельно)Extraction API (например, Thunderbit)
Время на запускЧасы (упаковка, слои, отладка)Минуты (API-ключ + HTTP-запрос)
ПоддержкаПостоянно ваша (фиксация версий, обновления runtime)На стороне провайдера
Антибот-защитаВручную (stealth-плагины, прокси)Встроена
Формат результатаСырые HTML/скриншоты, которые нужно парсить самомуСтруктурированный JSON по схеме
Лучше всего дляПолной browser automation, тестирования, кастомных потоковИзвлечения данных, скрейпинга, ingestion контента

Скажу прямо: если вы часами отлаживаете Chromium-бинарники только ради того, чтобы вытащить JSON из страниц товаров, это признак того, что вы решаете не ту задачу. Оставляйте DIY-Lambda-путь для случаев, когда вам действительно нужно управлять браузером — для извлечения данных есть более прямой вариант. Если вы выбираете между этими подходами для конкретного проекта, наш гайд по AI web scraper подробнее разбирает рынок, а расширение Thunderbit для Chrome стоит посмотреть, если хотите сначала протестировать подход "сначала извлечение", а уже потом выбирать окончательную архитектуру.

Итоги

Три способа деплоя, одна повторяющаяся мысль: фиксируйте версии, давайте Chromium достаточно памяти и выбирайте метод деплоя под реальные ограничения, а не под первый попавшийся туториал. Layers — для быстрой итерации, Container Images — для продакшн-масштаба, ZIP — для простых разовых задач. А если на самом деле вы не автоматизируете браузер, а извлекаете данные, возможно, вам выгоднее сразу использовать специализированный API для extraction и вообще не тратить время на упаковку.

FAQ

Можно ли запускать Puppeteer на AWS Lambda в 2026 году? Да — если использовать puppeteer-core вместе с @sparticuz/chromium и деплоить через Layers, Container Image или прямой ZIP. Полный пакет puppeteer и устаревший chrome-aws-lambda уже не работают стабильно на актуальных runtime Lambda.

Какой максимальный размер пакета для AWS Lambda? 250 МБ после распаковки для Layers и ZIP-деплоев; 10 ГБ для Container Image, согласно квотам AWS Lambda.

Поддерживается ли chrome-aws-lambda до сих пор? Нет. Оригинальный пакет chrome-aws-lambda от alixaxel устарел и ломается на Node 18+. Используйте @sparticuz/chromium — сейчас это поддерживаемый стандарт.

Сколько памяти нужно Puppeteer на AWS Lambda? 1024 МБ — практический минимум; комфортная производительность обычно начинается в диапазоне 1536–2048 МБ. Ниже 1024 МБ выполнение заметно замедляется, потому что Lambda привязывает CPU к объёму памяти.

Как уменьшить cold start для Puppeteer на Lambda? Выделите больше памяти, чтобы получить и больше CPU, рассмотрите Provisioned Concurrency, если задержка критична, и держите пакет деплоя максимально компактным — каждая лишняя зависимость увеличивает время холодного старта.

Узнать больше

Ke
Ke
Технический директор в Thunderbit | Senior Data Scientist и эксперт по ML Имея почти десятилетний опыт в машинном обучении и data science, Кэ Шэнь — выпускник Колумбийского университета и бывший Senior Data Scientist в Walmart Labs. Обладая глубокой, признанной коллегами экспертизой в Python, R, Java и статистике, он делится проверенными на практике наблюдениями о том, как переводить сложные AI-алгоритмы из теории в production-grade архитектуру.
Topics
Puppeteer AWS LambdaServerless browser automationHeadless Chromium
Содержание
Thunderbit · AI-агент для веб-данных

Извлекай данные с любой страницы за 1 клик

Нам доверяют более 250 000 пользователей
доступен бесплатный тариф
От веб-страницы к таблице
Опиши, что тебе нужно, — AI-агент Thunderbit соберет это и экспортирует в Excel, Google Sheets, Airtable или Notion. Старт бесплатный.
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week