Internal HTTPS reverse proxy with automatic SSL certificates via Cloudflare DNS-01 challenge. Traffic arrives via Cloudflare Tunnel; Caddy terminates TLS and reverse-proxies to backend services.
Container: CT 127 IP: 192.168.144.31 Ports: 80 (HTTP), 443 (HTTPS)
Source Code
Source Code: github.com/opajanvv/homelab-docker/tree/main/lanproxy
Local clone: ~/workspace/homelab-docker/lanproxy/
Configs are version-controlled. Edit locally, commit, push, then pull on server + reload Caddy. Never edit Caddyfile directly on the server.
Deployment
# Clone LXC template
pct clone 902 127 --hostname lanproxy --full
pct set 127 --cores 1 --memory 512
pct set 127 -net0 name=eth0,bridge=vmbr0,firewall=1,gw=192.168.144.1,ip=192.168.144.31/23
pct set 127 -mp0 /lxcdata/lanproxy,mp=/data
pct set 127 -mp1 /home/jan/homelab-docker,mp=/opt/homelab-docker
pct set 127 -features nesting=1,keyctl=1
pct set 127 -onboot 1
# Deploy
pct start 127
pct exec 127 -- bash -c 'systemctl enable --now docker'
# Configs available via bind mount (no git clone needed)
pct exec 127 -- bash -c 'cd /opt/homelab-docker/lanproxy && chmod +x install.sh && ./install.sh'
Configuration
Environment Variable:
CLOUDFLARE_API_TOKEN- Cloudflare API token for DNS-01 challenge
Data Locations:
/data/- Caddy data (certificates, logs)/opt/homelab-docker/lanproxy/- Docker Compose config, Caddyfile
Config File: /opt/homelab-docker/lanproxy/Caddyfile
Caddyfile Format:
<service-domain> {
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
encode zstd gzip
reverse_proxy <internal-ip>:<port>
}
Active Routes
| Domain | Backend | Service |
|---|---|---|
| hermes-ui.janvv.nl | 192.168.144.63:8787 | Hermes Web UI |
| hermes.janvv.nl | 192.168.144.63:9119 | Hermes Dashboard (default) |
| assistant.janvv.nl | 192.168.144.120:8123 | Home Assistant |
| photos.janvv.nl | 192.168.144.110:2283 | Immich |
| tasks.janvv.nl | 192.168.144.60:1337 | Planka |
| n8n.janvv.nl | 192.168.144.61:5678 | n8n |
| kijkdoos.janvv.nl | 192.168.144.100:8096 | Jellyfin |
| search.janvv.nl | 192.168.144.64:8080 | SearXNG |
| proxmox.janvv.nl | 192.168.144.10:8006 | Proxmox VE |
| printer.janvv.nl | 192.168.144.66:631 | CUPS admin |
| print.janvv.nl | 192.168.144.66:8080 | CUPS upload page |
| status.janvv.nl | 192.168.144.25:80 | Status monitor |
| terminal.janvv.nl | 192.168.144.10:4711 | ttyd (Proxmox host) |
| laptop1.janvv.nl | 192.168.145.10:4711 | ttyd (laptop1) |
| laptop2.janvv.nl | 192.168.145.20:4711 | ttyd (laptop2) |
| www.janvv.nl, opa.janvv.nl | 192.168.144.72:80 | Grav |
| jokegoudriaan.nl, www.jokegoudriaan.nl | 192.168.144.70:80 | WordPress Joko |
For the full Caddyfile, see the GitHub repo.
How It Works
- External client requests
https://hermes.janvv.nl - Cloudflare DNS resolves to the tunnel (CNAME →
*.cfargotunnel.com) - Cloudflare Tunnel routes the request to Caddy at
https://192.168.144.31 - Caddy receives request, terminates TLS (cert via Cloudflare DNS-01), proxies to backend
- Response flows back through Caddy → tunnel → Cloudflare → client
Backup
What to backup:
/opt/homelab-docker/lanproxy/Caddyfile- Route configuration/lxcdata/lanproxy/- Caddy data (certificates, logs)
Backup command:
cp /opt/homelab-docker/lanproxy/Caddyfile /backup/homelab/
rsync -av /lxcdata/lanproxy/ /backup/homelab/lanproxy/
Maintenance
Update:
pct exec 127 -- bash -c 'cd /opt/homelab-docker/lanproxy && docker compose pull && docker compose up -d'
View logs:
pct exec 127 -- bash -c 'cd /opt/homelab-docker/lanproxy && docker compose logs -f'
Reload config after Caddyfile change:
pct exec 127 -- bash -c 'cd /opt/homelab-docker/lanproxy && docker compose restart'
Adding a New Route
-
Edit Caddyfile locally in
~/workspace/homelab-docker/lanproxy/Caddyfile:new-service.janvv.nl { tls { dns cloudflare {env.CLOUDFLARE_API_TOKEN} } encode zstd gzip reverse_proxy 192.168.144.XX:PORT } -
Commit and push:
cd ~/workspace/homelab-docker && git add lanproxy/Caddyfile && git commit -m "caddy: add new-service" && git push -
Pull on server and reload:
ssh -A jan@server 'cd /home/jan/homelab-docker && git pull && sudo pct exec 127 -- bash -c "cd /opt/homelab-docker/lanproxy && docker compose restart"' -
Add DNS CNAME (proxied) via Cloudflare API pointing to the tunnel, and add a tunnel ingress rule routing to Caddy (
https://192.168.144.31). -
Verify:
curl -sk -o /dev/null -w "%{http_code}" https://new-service.janvv.nl/
Troubleshooting
Certificate errors: Check Cloudflare API token is valid Route not working: Verify service is accessible from Lanproxy container DNS issues: Check Pi-hole DNS configuration