Deployment
Run Reader in production with Docker or bare metal.
Two common deployment patterns, in order of operational complexity: Docker and bare metal Linux.
Docker
Reader ships with a production-ready Dockerfile and compose file in examples/deployment/docker/ in the GitHub repo.
docker-compose.yml
version: "3.8"
services:
reader:
build: .
platform: linux/amd64
ports:
- "3001:3001"
shm_size: "2gb"
security_opt:
- seccomp:unconfined
cap_add:
- SYS_ADMIN
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3001/health"]
interval: 30s
timeout: 10s
retries: 3
restart: unless-stopped
Critical Docker constraints
These are not optional - Reader will fail in subtle ways without them:
platform: linux/amd64- Chromium is bundled as x86_64 only. On Apple Silicon Macs, Docker will emulate x86_64 (slow); on a remote x86_64 Linux host, it runs natively.shm_size: 2gb- Chrome uses/dev/shmheavily. The Docker default of 64 MB causes crashes with unhelpful errors.seccomp:unconfinedandcap_add: SYS_ADMIN- required for the Chrome sandbox. Without these, Chrome fails to spawn child processes.- Base image:
node:22-slimplus Chrome system libraries (installed in the Dockerfile).
Running it
cd examples/deployment/docker
docker-compose up -d
curl http://localhost:3001/health
# {"status":"ok"}
curl -X POST http://localhost:3001/scrape \
-H "Content-Type: application/json" \
-d '{"urls": ["https://example.com"]}'
The example container exposes a small HTTP API wrapping ReaderClient. Adapt it to your needs.
Bare metal Linux
Install directly on a Linux server if you don't want Docker.
# Install Node 22 via nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
source ~/.bashrc
nvm install 22
nvm use 22
# Install Chrome system dependencies
sudo apt-get update && sudo apt-get install -y \
wget gnupg ca-certificates curl \
fonts-liberation fonts-freefont-ttf xvfb \
libasound2 libatk-bridge2.0-0 libatk1.0-0 libatspi2.0-0 \
libcups2 libdbus-1-3 libdrm2 libgbm1 libgtk-3-0 \
libnspr4 libnss3 libxcomposite1 libxdamage1 \
libxfixes3 libxkbcommon0 libxrandr2 libxshmfence1 libxss1 \
xdg-utils
# Clone and install Reader
git clone https://github.com/vakra-dev/reader.git
cd reader
npm install
npm run build
# Run the daemon
node dist/cli/index.js start --pool-size 5
# Or embed in your own Node app (recommended for servers)
npm install @vakra-dev/reader
Run as a systemd service
Create /etc/systemd/system/reader.service:
[Unit]
Description=Reader scraping daemon
After=network.target
[Service]
Type=simple
User=reader
WorkingDirectory=/opt/reader
ExecStart=/usr/bin/node /opt/reader/dist/cli/index.js start --pool-size 5
Restart=on-failure
RestartSec=10
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl enable reader
sudo systemctl start reader
sudo systemctl status reader
Production tuning
Memory
Each browser instance uses 300-500 MB. Plan for:
size: 5- 2-3 GB RAM headroomsize: 10- 4-6 GB RAM headroomsize: 20- 8-12 GB RAM headroom
Leave 2 GB free for the Node process, OS, and buffers.
Monitoring
Expose PoolStats via a /health endpoint in your app. Watch for:
queueLength > 0 sustained→ scale upunhealthy > 0 sustained→ something is causing browser crashesavgRequestDurationclimbing → target sites changed, retries are kicking in
Graceful shutdown
In Node apps, always close the client on shutdown:
process.on("SIGTERM", async () => {
await reader.close();
process.exit(0);
});
Reader has auto-cleanup registered too, but explicit is safer under systemd and container orchestrators.