Tuning
Concurrency is the pool size
The render pipeline’s concurrency is bound to the browser pool: the number of render workers equals --render-pool-max-total. The default is 1 — a single browser, serial rendering. For any real load, raise it:
-e RENDER_POOL_MAX_TOTAL=4
-e RENDER_POOL_MIN_IDLE=1
-e RENDER_POOL_MAX_IDLE=4Each browser consumes memory and CPU; size MAX_TOTAL to your container’s resources and expected concurrency, and set container memory/CPU limits.
Timeouts
| Setting | Default | Purpose |
|---|---|---|
--render-timeout | 30s | Max time for one render. |
--render-retry-attempts | 3 | Retries on failure. A slow render can consume up to timeout × attempts. |
--render-pool-borrow-timeout | 1m | Max wait to acquire a browser when the pool is saturated. |
--render-pool-repay-timeout | 5s | Max time to return/clean up a browser. |
With the default single-browser pool and these timeouts, one slow render serializes everything behind it. Raising the pool size and bounding per-request time at your proxy both help.
Recycling
Browsers are recycled by two mechanisms:
- Usage count — a browser/tab is destroyed after
--factory-max-usage-countrenders (default 1000), bounding memory growth. - Eviction — idle browsers are periodically evicted (
--render-pool-time-between-eviction, default 1h).
Health checks
/_status borrows a browser to read live version info, so under a saturated single-browser pool it can block up to the borrow timeout. Use /_metadata for liveness (it does not touch the pool), and either size the pool with headroom or treat /_status as a readiness check with a generous timeout.
page vs runner
For high throughput on Chromium, --factory-type=page reuses one browser process across tabs, reducing per-render startup cost. runner gives stronger isolation (a crash affects only one render). Benchmark both for your workload.