Self-hosting with Docker
The self-hosted Restura is a single Node container that serves the SPA and the /api/* proxy on one port. Same Hono app that runs in the Cloudflare Worker — just with Node adapters for CONNECT proxying and native WebSockets.
Quick start
Section titled “Quick start”docker run -p 3000:3000 \ -e WORKER_PROXY_TOKEN=$(openssl rand -hex 32) \ ghcr.io/dipjyotimetia/restura:latestOpen http://localhost:3000. All user data lives in the browser’s IndexedDB — the server is stateless.
docker-compose
Section titled “docker-compose”services: restura: image: ghcr.io/dipjyotimetia/restura:latest ports: - '3000:3000' environment: WORKER_PROXY_TOKEN: ${WORKER_PROXY_TOKEN:?required} ENVIRONMENT: production restart: unless-stoppedThe repo ships a docker-compose.yml you can copy.
Configuration
Section titled “Configuration”| Env var | Purpose | Default |
|---|---|---|
PORT | Listen port | 3000 |
HOST | Bind address | 0.0.0.0 |
WORKER_PROXY_TOKEN | Shared token for X-Worker-Token header from the client | required in prod |
REQUIRE_CF_ACCESS | If true, trust the Cf-Access-Authenticated-User-Email header from your reverse proxy | false |
ENVIRONMENT | development allows localhost upstream; anything else enforces SSRF guard | production |
ALLOW_PRIVATE_IPS | Allow RFC 1918 / link-local upstreams (still blocks cloud-metadata) | false |
ALLOWED_ORIGIN | Comma-separated Origin allowlist | * (recommend setting) |
RESTURA_STATIC_ROOT | Path to the SPA bundle | /app/dist/web |
Auth modes
Section titled “Auth modes”You have two options:
- Shared token — set
WORKER_PROXY_TOKENand have the client send it in theX-Worker-Tokenheader. Configure the URL inVITE_WORKER_URLat build time, or in the in-app settings. - Reverse-proxy auth — set
REQUIRE_CF_ACCESS=trueand put Cloudflare Access (or any IdP that injects an authenticated email header) in front. The server reads the email from the proxy header and trusts it.
Internal network access
Section titled “Internal network access”By default the SSRF guard blocks any RFC 1918 / link-local upstream — even from your self-hosted instance. If you genuinely want to proxy to internal services, set ALLOW_PRIVATE_IPS=true. The cloud-metadata blacklist (169.254.169.254) stays enforced regardless.
WebSocket constraints (v1)
Section titled “WebSocket constraints (v1)”The WebSocket ticket store is per-replica in-memory. Tickets expire in 30 seconds. If you run multiple replicas behind a load balancer, you need either:
- Sticky session routing for the
/api/ws-ticket→/api/wsflow, or - A single replica (fine for most internal deployments).
This will be revisited in a future release.
Rate limiting
Section titled “Rate limiting”Per-replica in-memory token bucket. For multi-replica deployments, put a rate limiter (Cloudflare, nginx, Envoy) in front.
Related
Section titled “Related”- Reverse proxy — nginx / Caddy / Traefik snippets.
- Security model — the auth gate, SSRF guard.
- Capability matrix — what does not work in self-hosted mode.