Skip to content

Docker ​

A container is the most reliable way to run the prerender service - Chromium and its system libraries are pinned, and resource limits are easy to apply.

Dockerfile ​

The repository ships a Dockerfile. It uses Puppeteer's official base image, which already contains a matching Chromium and all required libraries.

dockerfile
FROM ghcr.io/puppeteer/puppeteer:latest

ENV PUPPETEER_SKIP_DOWNLOAD=1 \
    NODE_ENV=production

WORKDIR /app

COPY package*.json ./
RUN npm ci --omit=dev

COPY dist ./dist

EXPOSE 3000
CMD ["node", "dist/cli.js", "serve", "--host", "0.0.0.0", "--port", "3000"]

PUPPETEER_SKIP_DOWNLOAD=1 avoids a second Chromium download - the base image already provides one, and Puppeteer finds it automatically.

Build and run ​

bash
npm run build

docker build -t headless-prerender .

docker run --rm -p 3000:3000 \
  --memory 1g --cpus 1 \
  headless-prerender \
  serve --host 0.0.0.0 --port 3000 \
        --allow-origin https://www.example.com \
        --token "$PRERENDER_TOKEN" \
        --cache-ttl 300000

docker-compose ​

yaml
services:
  prerender:
    build: .
    command:
      - serve
      - --host=0.0.0.0
      - --port=3000
      - --allow-origin=https://www.example.com
      - --token=${PRERENDER_TOKEN}
      - --cache-ttl=300000
    ports:
      - "127.0.0.1:3000:3000"
    mem_limit: 1g
    cpus: 1.0
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "node", "-e", "fetch('http://localhost:3000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
      interval: 30s
      timeout: 5s
      retries: 3

Notes ​

  • Bind the published port to 127.0.0.1 so only the host's reverse proxy can reach it. See Security.
  • Set --memory / mem_limit. A single render can briefly use several hundred MB; 1 GB is a safe starting point for low concurrency.
  • Add restart: unless-stopped (or a Kubernetes liveness probe on /health) so the service recovers from a crashed browser.

Released under the MIT License.