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 300000docker-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: 3Notes
- Bind the published port to
127.0.0.1so 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.