# ~$ selfhostedops — Caddyfile # Automatic HTTPS for every subdomain. Nothing to renew, ever. { email {$ACME_EMAIL} } git.{$DOMAIN} { reverse_proxy forgejo:3000 } wiki.{$DOMAIN} { reverse_proxy outline:3000 } vault.{$DOMAIN} { reverse_proxy vaultwarden:80 } status.{$DOMAIN} { reverse_proxy uptime-kuma:3001 } # Push notifications (episode 6). Websockets and long polling pass through as-is. ntfy.{$DOMAIN} { reverse_proxy ntfy:80 } # Automation (episode 8). The editor never becomes public: only the webhook # paths are served, everything else under this host answers an empty 404. # A webhook sender out on the internet cannot join your mesh, so this is the # one door that has to stay open - and it is exactly one door wide. n8n.{$DOMAIN} { @webhooks path /webhook/* /webhook-test/* /webhook-waiting/* handle @webhooks { reverse_proxy n8n:5678 } handle { respond 404 } } # NetBird control plane (episode 5) - dashboard, API, gRPC, relay, built-in IdP vpn.{$DOMAIN} { # Native gRPC (needs HTTP/2 cleartext to backend) @grpc header Content-Type application/grpc* reverse_proxy @grpc h2c://netbird-server:80 # Combined server paths (relay, signal, management, OAuth2) @backend path /relay* /ws-proxy/* /api/* /oauth2/* reverse_proxy @backend netbird-server:80 # Dashboard (everything else) reverse_proxy /* netbird-dashboard:80 }