Trovella Wiki

Caddy Reverse Proxy

Caddy configuration for TLS termination, HTTP/3, www redirect, and reverse proxying to the Next.js container.

The Caddyfile

The entire Caddy configuration is 8 lines in infra/Caddyfile:

trovella.ai {
    reverse_proxy web:3000
    encode gzip zstd
}

www.trovella.ai {
    redir https://trovella.ai{uri} permanent
}

This handles:

  1. TLS termination -- Caddy automatically obtains and renews Let's Encrypt certificates for trovella.ai. No certbot, no cron job, no manual certificate management.
  2. HTTP to HTTPS redirect -- Caddy automatically redirects HTTP (port 80) to HTTPS (port 443) for any configured domain.
  3. Reverse proxying -- all HTTPS traffic to trovella.ai is forwarded to the web container on port 3000 via the Docker internal network.
  4. Compression -- responses are compressed with gzip or zstd based on client support.
  5. www redirect -- www.trovella.ai permanently redirects (301) to trovella.ai with the original URI preserved.

How TLS Works

Caddy uses the ACME HTTP-01 challenge to obtain certificates from Let's Encrypt. The flow:

  1. Caddy listens on port 80 and 443
  2. Let's Encrypt sends a challenge request to http://trovella.ai/.well-known/acme-challenge/<token>
  3. Caddy responds with the proof token
  4. Let's Encrypt issues the certificate
  5. Caddy stores the certificate in the caddy-data Docker volume
  6. Caddy automatically renews before expiration (typically every 60-90 days)

This requires Cloudflare DNS to be in DNS-only mode (grey cloud, no Cloudflare proxy) so that HTTP-01 challenges reach Caddy directly. If Cloudflare proxy mode were enabled, Cloudflare would terminate TLS and the ACME challenge would fail.

HTTP/3 Support

The Docker Compose configuration exposes port 443 for both TCP and UDP:

ports:
  - "80:80"
  - "443:443"
  - "443:443/udp" # HTTP/3

Caddy advertises HTTP/3 support via the Alt-Svc response header. Clients that support QUIC (Chrome, Firefox, Edge) will upgrade to HTTP/3 automatically. The UDP port exposure is what enables this -- without it, HTTP/3 would be silently unavailable.

Why Caddy Over Alternatives

FeatureCaddyNginx + certbotTraefik
Automatic TLSBuilt-in, zero configRequires certbot + cronBuilt-in
Configuration~10-line Caddyfile50+ line nginx.conf + sites-availableDocker labels or TOML
HTTP/3Built-inRequires recompilation with quic patchBuilt-in
Docker service discoveryNot needed (fixed services)Not neededPrimary use case
AI agent familiarityLowerHigher (more common)Medium

Caddy was chosen because the Caddyfile is shorter than the equivalent Nginx configuration including certbot setup. Traefik's Docker label-based routing adds unnecessary complexity when the set of services is fixed. The trade-off is that AI agents are more likely to generate Nginx-specific configuration, but the 8-line Caddyfile is simple enough that this has not been a practical issue.

What Caddy Does Not Do

Caddy is not configured to proxy admin dashboards. Inngest (port 8288) and Drizzle Studio (port 4983) are accessed only via SSH tunnel:

# Inngest dashboard
gcloud compute ssh trovella-prod-vm \
  --zone=us-central1-a --project=trovella-prod \
  --tunnel-through-iap -- -L 8288:localhost:8288
# Then open http://localhost:8288

This is intentional -- keeping these off the public internet avoids the need for authentication middleware on internal tooling. See Networking for the full IAP SSH access model.

Certificate Persistence

TLS certificates and OCSP staples are stored in the caddy-data Docker volume. This volume survives container restarts and docker compose down. If the volume is deleted, Caddy re-obtains certificates on next startup (within Let's Encrypt rate limits -- 5 duplicate certificates per week).

Deployment

The Caddyfile is SCPed to the VM during each deploy:

gcloud compute scp infra/Caddyfile $VM_NAME:~ --tunnel-through-iap
# Then moved to /opt/trovella/Caddyfile

Caddy watches the Caddyfile for changes and reloads automatically when docker compose up -d recreates the container. TLS certificates are preserved in the named volume across container recreations.

Future Considerations

  • Cloudflare proxy mode -- enabling the orange cloud in Cloudflare would add CDN, DDoS protection, and WAF. This would require switching from HTTP-01 to DNS-01 ACME challenges (Caddy supports this with a Cloudflare API token plugin).
  • Additional domains -- adding subdomains (e.g., api.trovella.ai) would be additional blocks in the Caddyfile.
  • Rate limiting -- Caddy supports rate limiting via the rate_limit directive if application-level rate limiting proves insufficient.

On this page