docker pull atcr.io/rtw.run/caddy:latest
caddy
Caddy reverse proxy fronting every public RTW service for rtw.run, with automatic TLS via Let’s Encrypt. Runs on ports 80 and 443.
The image ships the caddy-dns/cloudflare module so it can issue the *.rtw.run wildcard certificate using ACME DNS-01 (Cloudflare API token via CF_API_TOKEN).
Two Caddyfiles are baked into the image (/etc/caddy/); the entrypoint picks one via RTW_ENV (dev or prod, default prod). Config is fully reproducible from the flake — nothing is hand-edited inside the container.
Routes (production)
| Route (prod) | Upstream | Notes |
|---|---|---|
rtw.run |
127.0.0.1:8080 |
RTW app; http → https redirect is automatic |
geocoder.rtw.run |
127.0.0.1:8999 |
waldo; CORS for *.rtw.run + http://localhost:5173 |
route.rtw.run |
127.0.0.1:8989 |
graphhopper; CORS for *.rtw.run + http://localhost:5173 |
tiles.rtw.run |
127.0.0.1:9090 |
pmtiles (sets its own CORS) |
rtw.social |
127.0.0.1:3000 |
PDS (XRPC, blobs, OAuth) |
*.rtw.run |
127.0.0.1:3000 |
PDS handle/DID resolution (/.well-known/atproto-did); specific subdomains above take precedence |
y-sweet is not proxied — it is an internal service reached only by the RTW app.
Running the container
Multiarch image (amd64 + arm64): atcr.io/rtw.run/caddy:latest
The container runs as uid 1000, so binding to 80/443 needs the NET_BIND_SERVICE capability. ACME certs are persisted under /data (mount a volume there) so they survive restarts.
mkdir -p /srv/rtw/caddy
podman run -d --name caddy --userns=keep-id \
--cap-add NET_BIND_SERVICE \
-p 80:80 -p 443:443 \
-v /srv/rtw/caddy:/data \
-e RTW_ENV=prod \
-e CF_API_TOKEN=<cloudflare api token> \
atcr.io/rtw.run/caddy:latest
Environment
| Var | Default | Meaning |
|---|---|---|
RTW_ENV |
prod |
dev uses the http-only *.localhost Caddyfile; prod uses the TLS one. |
CF_API_TOKEN |
unset | Cloudflare API token for ACME DNS-01 (prod; used to issue the *.rtw.run wildcard cert). |
Local development
Run the same image with RTW_ENV=dev (http only, no token needed). All routes are on *.localhost domains (rtw.localhost, geocoder.localhost, route.localhost, tiles.localhost, pds.localhost), which resolve to 127.0.0.1 on every modern system per RFC 6761 — no /etc/hosts entries or local DNS required.
Source: Caddy · caddy-dns/cloudflare