harly.config.json, and doctor validates only its expected services.
After reading this
- You will know the three proxy modes and when to use each
- You will know which ports each mode publishes
- You will know how to point an existing reverse proxy at Harly
Available modes
Caddy is never started in
external or local mode.
Using an external reverse proxy
If another proxy already owns ports 80 and 443, keep it and selectexternal mode during install. Forward the hostname to 127.0.0.1:3000.
Example Nginx server block:
The Nginx block above is a standard reverse-proxy pattern, not a file shipped by Harly. Adapt it to your existing proxy configuration.
Trust your reverse proxy for client IPs
By default, Harly reads the client IP from the rightmost hop inX-Forwarded-For. In external mode that hop is your reverse proxy, not the end user. Workspace IP allowlists, per-IP rate limits, and audit log ipAddress fields will all see the proxy’s address instead of the real client until you tell Harly which peer to trust.
Set TRUSTED_PROXY_IPS in .env to the peer address (or comma-separated addresses) that Harly’s container sees when the proxy forwards a request:
X-Forwarded-For as the client IP. Your reverse proxy must overwrite X-Forwarded-Peer with an operator-controlled marker that matches the value Harly receives for that proxy, as in the Nginx example above; never pass a client-supplied value through unchanged. If Harly runs behind a container or another network hop, replace 127.0.0.1 with the peer address Harly sees and use the same value in TRUSTED_PROXY_IPS. When the peer does not match, Harly falls back to the rightmost hop so a malicious client cannot spoof its address by injecting the header.
Guidelines:
- Use the address Harly actually receives the connection from, not the public IP of the proxy. Behind Docker with a host-network Nginx, that is typically
127.0.0.1or the Docker bridge gateway. - If several load balancers can each forward directly to Harly, list all of them, comma-separated.
- Behind a CDN like Cloudflare in front of your own reverse proxy, trust the reverse proxy’s address here and let the CDN handle client IP resolution in your proxy configuration.
- Leave
TRUSTED_PROXY_IPSunset incaddymode. The bundled Caddy proxy adds exactly one forwarding hop, so the rightmost-hop default already resolves to the real client. - After changing the value, restart the app container so the new environment is picked up.
Troubleshooting
doctor reports the wrong services as unhealthy
Symptom: doctor checks for Caddy even though you selected external mode.
Cause: harly.config.json still records the previous mode.
Solution: re-run the installer’s management menu (npx @harly/cli) and confirm the proxy mode, or edit harly.config.json directly to match your actual topology.
502 from the external proxy
Symptom: the reverse proxy returns a 502 for every request. Cause: Harly is not listening on127.0.0.1:$HARLY_PORT, or the proxy is forwarding to the wrong port.
Solution: confirm docker compose ps shows the app container healthy, then check HARLY_PORT in .env matches the proxy’s upstream port.
Related pages
Self-hosting overview
VPS requirements and DNS setup.
Operations
Health checks, the scheduler, backups, and upgrades.