renderd is an always-on HTTP service that renders dynamic pages with headless Chromium.
It is built for scrapers and data pipelines that need rendered HTML without paying browser startup cost on every request.
This project codebase was fully created with AI assistance.
- Persistent browser worker pool (warm workers).
- HTTP API:
POST /render,GET /health. - Optional click flow for dynamic "Load more" style pages.
- Configurable host, port, workers, queue, and timeouts.
- Cross-platform browser detection (Linux, macOS, Windows).
- JSON configuration + environment variable overrides.
Request flow:
POST /renderrequest arrives.- Request is validated by API layer.
- Job is dispatched to a worker in round-robin order.
- Worker opens page, applies optional clicks, extracts HTML.
- Response returns HTML, final URL, worker id, elapsed time, click count.
Response:
{
"status": "ok",
"workers": 2
}Request body:
{
"url": "https://example.com",
"timeout_ms": 20000,
"click_selector": "button.load-more",
"click_text": "Load more",
"click_times": 3,
"wait_after_click_ms": 1200
}Notes:
urlis required and must start withhttp://orhttps://.click_selectorhas priority overclick_text.click_times > 0requiresclick_selectororclick_text.
Response body:
{
"html": "<html>...</html>",
"final_url": "https://example.com/final",
"worker_id": 1,
"elapsed_ms": 1540,
"clicked_count": 2
}By default the service reads config.json from project root.
You can pass explicit config path via CLI:
cargo run --release -- --config ./config.jsonConfig resolution priority:
- CLI flag
--config/-c - Env var
RENDERER_CONFIG_PATH ./config.json<crate_root>/config.json- Built-in defaults
Example configuration is provided in config.example.json.
These env vars override file config:
RENDERER_BIND(host:port)RENDERER_HOSTRENDERER_PORTRENDERER_WORKERSCHROME_EXECUTABLEorCHROME(browser path)
- Rust toolchain (edition 2024, Rust >= 1.85)
- Installed browser (
google-chrome,chromium, or equivalent)
cp config.example.json config.json
cargo run --release -- --config ./config.jsonService starts on http://127.0.0.1:3020 by default.
curl -sS http://127.0.0.1:3020/render \
-H 'content-type: application/json' \
-d '{"url":"https://example.com"}' | jq -r '.elapsed_ms, .worker_id'docker build -t renderd .
docker run --rm -p 3020:3020 -e RENDERER_HOST=0.0.0.0 renderdContainer starts with:
/usr/local/bin/app --config /app/config.jsonRun with external config file:
docker run --rm -p 3020:3020 -e RENDERER_HOST=0.0.0.0 \
-v "$(pwd)/config.json:/app/config.json:ro" \
renderd /usr/local/bin/app --config /app/config.jsoncargo fmt --all
cargo clippy --all-targets --all-features -- -D warnings
cargo check --all-targetssrc/api.rs- HTTP handlers and request validation.src/pool.rs- worker pool and scheduling.src/worker.rs- browser lifecycle and rendering pipeline.src/platform/*- OS-specific browser path candidates.src/js/*- JavaScript snippets for page interactions.src/config.rs- JSON config loading and env overrides.
Please report vulnerabilities via private disclosure process described in SECURITY.md.
MIT, see LICENSE.