Skip to content

elhombre/renderd

Renderd

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.

AI disclosure

This project codebase was fully created with AI assistance.

Features

  • 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.

Architecture

Request flow:

  1. POST /render request arrives.
  2. Request is validated by API layer.
  3. Job is dispatched to a worker in round-robin order.
  4. Worker opens page, applies optional clicks, extracts HTML.
  5. Response returns HTML, final URL, worker id, elapsed time, click count.

API

GET /health

Response:

{
  "status": "ok",
  "workers": 2
}

POST /render

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:

  • url is required and must start with http:// or https://.
  • click_selector has priority over click_text.
  • click_times > 0 requires click_selector or click_text.

Response body:

{
  "html": "<html>...</html>",
  "final_url": "https://example.com/final",
  "worker_id": 1,
  "elapsed_ms": 1540,
  "clicked_count": 2
}

Configuration

By default the service reads config.json from project root.

You can pass explicit config path via CLI:

cargo run --release -- --config ./config.json

Config resolution priority:

  1. CLI flag --config / -c
  2. Env var RENDERER_CONFIG_PATH
  3. ./config.json
  4. <crate_root>/config.json
  5. Built-in defaults

Example configuration is provided in config.example.json.

Environment overrides

These env vars override file config:

  • RENDERER_BIND (host:port)
  • RENDERER_HOST
  • RENDERER_PORT
  • RENDERER_WORKERS
  • CHROME_EXECUTABLE or CHROME (browser path)

Quick start

Prerequisites

  • Rust toolchain (edition 2024, Rust >= 1.85)
  • Installed browser (google-chrome, chromium, or equivalent)

Run

cp config.example.json config.json
cargo run --release -- --config ./config.json

Service starts on http://127.0.0.1:3020 by default.

Test request

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

docker build -t renderd .
docker run --rm -p 3020:3020 -e RENDERER_HOST=0.0.0.0 renderd

Container starts with:

/usr/local/bin/app --config /app/config.json

Run 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.json

Development

cargo fmt --all
cargo clippy --all-targets --all-features -- -D warnings
cargo check --all-targets

Project layout

  • src/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.

Security

Please report vulnerabilities via private disclosure process described in SECURITY.md.

License

MIT, see LICENSE.

About

HTTP service for rendering JavaScript-heavy pages using pooled headless browsers.

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors