Skip to main content
Version: Next

Running in Docker

Running headless browsers in Docker requires a lot of setup to do it right. But there's no need to worry about that, because we already created base images that you can freely use. We use them every day on the Apify Platform.

All images can be found in their GitHub repo and in our DockerHub.

Overview

Browsers are pretty big, so we try to provide a wide variety of images to suit the specific needs. Here's a full list of our Docker images.

Every image is published in two flavours. The full image preinstalls apify, crawlee and typescript. The -slim variant (e.g. 24-slim, 24-1.60.0-slim, 24-beta-slim) only ships the browser automation library the image is built around (for example puppeteer, playwright, or camoufox-js with impit), and apify/actor-node:24-slim ships no npm packages at all. Slim images are smaller and faster to pull, and your package.json is the single source of truth for dependency versions.

Use the slim variant unless you have a reason not to. Reach for the full image when you want to run something quickly without maintaining a package.json, or when you rely on the exact preinstalled versions of apify and crawlee.

# No preinstalled packages, bring your own dependencies.
FROM apify/actor-node:24-slim
# Only Playwright (Chromium) is preinstalled, pinned to match the bundled browser.
FROM apify/actor-node-playwright-chrome:24-1.60.0-slim

Since nothing but the automation library is preinstalled, make sure crawlee and any other runtime dependencies are listed in your package.json and installed in your Dockerfile. The version matching rules for the automation library still apply.

Versioning

Each image is tagged with up to 2 version tags, depending on the type of the image. One for Node.js version and second for pre-installed web automation library version. If you use the image name without a version tag, you'll always get the latest available version.

We recommend always using at least the Node.js version tag in production Dockerfiles. It will ensure that a future update of Node.js will not break our automations.

Node.js versioning

Our images are built with multiple Node.js versions to ensure backwards compatibility. Currently, we support Node.js versions 22, 24, and 26 (legacy versions still exist, see DockerHub). To select the preferred version, use the appropriate number as the image tag.

# Use Node.js 24
FROM apify/actor-node:24

Automation library versioning

Images that include a pre-installed automation library, which means all images that include puppeteer or playwright in their name, are also tagged with the pre-installed version of the library. For example, apify/actor-node-puppeteer-chrome:24-24.14.0 comes with Node.js 24 and Puppeteer v24.14.0. If you try to install a different version of Puppeteer into this image, you may run into compatibility issues, because the Chromium version bundled with puppeteer will not match the version of Chromium that's pre-installed.

Similarly apify/actor-node-playwright-firefox:24-1.54.1 runs on Node.js 24 and is pre-installed with the Firefox version that comes with v1.54.1.

Installing apify/actor-node-puppeteer-chrome (without a tag) will install the latest available version of Node.js and puppeteer.

Finding available tags

To see all available tags for an image, you can browse Docker Hub directly:

You can also query available tags programmatically using the Docker Hub API:

curl -s "https://registry.hub.docker.com/v2/repositories/apify/actor-node-playwright-chrome/tags?page_size=50" | jq '.results[].name'

Pre-release tags

We also build pre-release versions of the images to test the changes we make. Those are typically denoted by a beta suffix, but it can vary depending on our needs. If you need to try a pre-release version, you can do it like this:

# Without library version.
FROM apify/actor-node:24-beta
# With library version.
FROM apify/actor-node-puppeteer-chrome:24-24.14.0-beta

Node.js package managers

All Node.js images ship with npm and have Corepack enabled, so you can use yarn or pnpm as well. Neither is preinstalled: add a packageManager field to your package.json and Corepack downloads and uses the exact version you pin.

{
"packageManager": "pnpm@10.24.0"
}

The images preconfigure the package managers so that:

  • pnpm and yarn install a flat, npm-style node_modules (node-linker=hoisted for pnpm, nodeLinker: node-modules for yarn) instead of a symlinked store or Plug'n'Play, so dependencies resolve without extra loaders.
  • The yarn and pnpm caches (YARN_CACHE_FOLDER, YARN_GLOBAL_FOLDER, PNPM_CONFIG_STORE_DIR, PNPM_CONFIG_CACHE_DIR) and the Corepack cache (COREPACK_HOME) live under /pkg-cache. npm keeps its default ~/.npm cache. Both directories only hold throwaway data, so you can rm -rf /pkg-cache/* ~/.npm at the end of your Dockerfile to reclaim space without touching installed dependencies.
Overriding the linker

The images set the linker through the PNPM_CONFIG_NODE_LINKER and YARN_NODE_LINKER environment variables. Both pnpm and yarn give environment variables precedence over .npmrc or .yarnrc.yml, so a config file alone does not change the linker. To use a different one, override the variable in your Dockerfile:

# https://pnpm.io/settings#nodelinker
ENV PNPM_CONFIG_NODE_LINKER=isolated
# https://yarnpkg.com/configuration/yarnrc#nodeLinker
ENV YARN_NODE_LINKER=pnp

Best practices

For production Actors, we recommend pinning both the Node.js version and the automation library version in your Dockerfile, and matching the library version in your package.json. This ensures reproducible builds across environments.

Pin the automation library version in both your Dockerfile tag and package.json:

FROM apify/actor-node-playwright-chrome:24-1.52.0
{
"dependencies": {
"crawlee": "^3.0.0",
"playwright": "1.52.0"
}
}
Version matching is important

The version in package.json must match the version in the Docker image tag. If they don't match, npm will attempt to install a different version, which can cause compatibility issues with the pre-installed browser binaries.

Alternative approach (using *)

You can use * as the automation library version in package.json to automatically use whatever version is pre-installed in the Docker image:

FROM apify/actor-node-playwright-chrome:24
{
"dependencies": {
"crawlee": "^3.0.0",
"playwright": "*"
}
}

This approach prevents the library from being re-installed on build, which is useful because the pre-installed libraries are only guaranteed to work with the specific browser versions bundled in the image. However, it provides less control over exactly which version you're running, since the Docker image tag without a library version may be updated over time.

Warning about image size

Browsers are huge. If you don't need them all in your image, it's better to use a smaller image with only the one browser you need. If you don't need the preinstalled apify and crawlee packages either, use the -slim variant.

You should also be careful when installing new dependencies. Nothing prevents you from installing Playwright into theactor-node-puppeteer-chrome image, but the resulting image will be about 3 times larger and extremely slow to download and build.

When you use only what you need, you'll be rewarded with reasonable build and start times.

Apify Docker Images

actor-node

This is the smallest image we have based on Alpine Linux. It does not include any browsers, and it's therefore best used with CheerioCrawler. It benefits from lightning fast builds and container startups.

PuppeteerCrawler

,

PlaywrightCrawler

and other browser based features will NOT work with this image.

FROM apify/actor-node:24

actor-node-puppeteer-chrome

This image includes Puppeteer (Chromium) and the Chrome browser. It can be used with CheerioCrawler and PuppeteerCrawler, but NOT with PlaywrightCrawler.

The image supports XVFB by default, so you can run both headless and headful browsers with it.

FROM apify/actor-node-puppeteer-chrome:24

actor-node-playwright

A very large and slow image that can run all Playwright browsers: Chromium, Chrome, Firefox, WebKit. Everything is installed. If you need to develop or test with multiple browsers, this is the image to choose, but in most cases, it's better to use the specialized images below.

FROM apify/actor-node-playwright:24

actor-node-playwright-chrome

Similar to actor-node-puppeteer-chrome, but for Playwright. You can run CheerioCrawler and PlaywrightCrawler, but NOT PuppeteerCrawler.

It uses the PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD environment variable to block installation of more browsers into the image to keep it small. If you want more browsers, either use the actor-node-playwright image override this env var.

The image supports XVFB by default, so we can run both headless and headful browsers with it.

FROM apify/actor-node-playwright-chrome:24

actor-node-playwright-firefox

Same idea as actor-node-playwright-chrome, but with Firefox pre-installed.

FROM apify/actor-node-playwright-firefox:24

actor-node-playwright-webkit

Same idea as actor-node-playwright-chrome, but with WebKit pre-installed.

FROM apify/actor-node-playwright-webkit:24

actor-node-playwright-camoufox

Same idea as actor-node-playwright-firefox, but with Camoufox, a Firefox fork hardened against bot detection, pre-installed instead of Firefox. The image also ships the camoufox-js and impit packages.

FROM apify/actor-node-playwright-camoufox:24

Example Dockerfile

To use the above images, it's necessary to have a Dockerfile. You can either use this example, or bootstrap your projects with the Crawlee CLI which automatically adds the correct Dockerfile into our project folder.

# Specify the base Docker image. You can read more about
# the available images at https://crawlee.dev/docs/guides/docker-images
# You can also use any other image from Docker Hub.
FROM apify/actor-node:24

# Copy just package.json and package-lock.json
# to speed up the build using Docker layer cache.
COPY package*.json ./

# Install NPM packages, skip optional and development dependencies to
# keep the image small. Avoid logging too much and print the dependency
# tree for debugging
RUN npm --quiet set progress=false \
&& npm install --omit=dev --omit=optional \
&& echo "Installed NPM packages:" \
&& (npm list --omit=dev --all || true) \
&& echo "Node.js version:" \
&& node --version \
&& echo "NPM version:" \
&& npm --version

# Next, copy the remaining files and directories with the source code.
# Since we do this after NPM install, quick build will be really fast
# for most source file changes.
COPY . ./


# Run the image.
CMD npm start --silent