blumeops/docs/reference/services/caddy.md
Erich Blume d26a6ae3b2 Update docs for Caddy routing and direct WireGuard peering
Comprehensive docs pass reflecting the new Fly proxy architecture:
- Fly proxy routes through Caddy on indri (not per-service TS Ingress)
- Direct WireGuard peering via --port=41641 pinning
- DERP relay performance lesson in Tailscale docs
- Caddy now in public traffic path
- indri tagged as flyio-target
- Removed fly-reload references
- Updated architecture diagrams and per-service setup guide
- Added changelog fragment

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 09:57:30 -07:00

3.3 KiB

title modified tags
Caddy 2026-04-18
service
networking
tls

Caddy

Reverse proxy for *.ops.eblu.me services with automatic TLS via ACME DNS-01.

Quick Reference

Property Value
Domain *.ops.eblu.me
HTTPS Port 443
Config ansible/roles/caddy/templates/Caddyfile.j2
Binary Custom build with Gandi DNS plugin

Why Caddy?

Caddy provides a single TLS termination point for all BlumeOps services:

  • Wildcard certificate for *.ops.eblu.me via Let's Encrypt
  • DNS-01 challenge using Gandi API (no port 80 needed)
  • Unified access from k8s pods, containers, and tailnet clients

See routing for when to use *.ops.eblu.me vs *.tail8d86e.ts.net.

Proxied Services

Indri-Local Services

Subdomain Backend Service
forge.ops.eblu.me localhost:3001 forgejo
registry.ops.eblu.me localhost:5050 zot
jellyfin.ops.eblu.me localhost:8096 jellyfin

Kubernetes Services

K8s services are proxied via their Tailscale Ingress endpoints:

Subdomain Backend Service
grafana.ops.eblu.me grafana.tail8d86e.ts.net grafana
argocd.ops.eblu.me argocd.tail8d86e.ts.net argocd
cv.ops.eblu.me cv.tail8d86e.ts.net cv
docs.ops.eblu.me docs.tail8d86e.ts.net docs (now publicly available at docs.eblu.me via flyio-proxy)
feed.ops.eblu.me feed.tail8d86e.ts.net miniflux
... ... (see defaults/main.yml for full list)

TCP Services (Layer 4)

Port Backend Service
2222 localhost:2200 Forgejo SSH
5432 pg.tail8d86e.ts.net:5432 postgresql

Configuration

Caddy is managed via the caddy Ansible role:

# Deploy caddy changes
mise run provision-indri -- --tags caddy

Key files:

  • ansible/roles/caddy/defaults/main.yml - Service definitions
  • ansible/roles/caddy/templates/Caddyfile.j2 - Caddy config template

Secrets

Secret Source Description
GANDI_BEARER_TOKEN 1Password API token for DNS-01 challenges

The token is written to ~/.config/caddy/gandi-token (chmod 0600) and sourced by the Caddy wrapper script.

Security Considerations

Caddy has no authentication layer — it is a plain reverse proxy. Access control relies entirely on Tailscale ACLs restricting which devices can reach indri on port 443. Currently tag:homelab, autogroup:admin, and tag:flyio-proxy (via tag:flyio-target on indri) can reach Caddy.

The flyio-proxy routes all public traffic through Caddy. This is the path for *.eblu.me requests from the public internet. Caddy sees these as requests from the Fly VM with Host: *.ops.eblu.me headers — the same routes used by tailnet clients.

Custom Build

Custom xcaddy build with Gandi DNS and L4 plugins. See build-caddy-with-plugins for build instructions and forge mirror details.

  • gandi - DNS hosting and ACME DNS-01 provider
  • routing - Service routing architecture
  • forgejo - Git forge (proxied by Caddy)
  • zot - Container registry (proxied by Caddy)
  • tailscale-operator - K8s services use Tailscale Ingress, then Caddy