Caddy come reverse proxy Docker: setup con Compose e HTTPS automatico
Usa Caddy come reverse proxy Docker: un solo stack compose, una rete condivisa, reverse_proxy verso i nomi dei servizi e HTTPS automatico, più quando conviene usare le label di caddy-docker-proxy.
In breve: esegui Caddy in un container dedicato che pubblica le porte 80 e 443, collega lui e i container delle tue app a un'unica rete Docker condivisa e punta `reverse_proxy` al nome di ogni servizio e alla sua porta interna (per esempio `reverse_proxy app:3000`). Caddy risolve il nome tramite il DNS integrato di Docker e ottiene automaticamente un certificato TLS per ogni indirizzo di sito.
Questa guida mostra prima l'approccio con un semplice Caddyfile, poi la variante basata su label caddy-docker-proxy, e spiega quando conviene ciascuna su un singolo VPS. Se esegui Caddy direttamente sull'host invece che in Docker, vedi la guida base al reverse proxy con Caddy.
Come si incastrano i pezzi
Solo il container di Caddy pubblica porte verso l'esterno. Ogni container backend si unisce a una rete bridge definita dall'utente (qui chiamata `web`) ed espone la propria porta solo su quella rete. Il server DNS integrato di Docker, disponibile sulle reti definite dall'utente, permette a Caddy di raggiungere `app:3000` tramite il nome del servizio o del container, così non devi mai scrivere a mano IP dei container che cambiano a ogni ricreazione.
Non pubblicare i backend è più importante di quanto sembri: Docker scrive le proprie regole iptables, quindi una porta pubblicata con `ports:` può essere raggiungibile da internet anche quando ufw dice che è bloccata. Usa `expose:` o nessuna mappatura di porte per tutto ciò che deve stare dietro il proxy.
| Componente | Ruolo | Pubblicato sull'host? |
|---|---|---|
| container caddy | Terminazione TLS, routing per hostname, redirect da HTTP a HTTPS | Sì: 80/tcp, 443/tcp, 443/udp |
| rete web | Rete bridge condivisa con DNS integrato per i nomi dei servizi | No |
| container delle app | I tuoi servizi (web app, API, dashboard) | No, raggiungibili solo tramite Caddy |
| volume caddy_data | Certificati, chiavi private, account ACME | No, ma deve essere persistente |
Passo per passo: Caddyfile semplice con docker compose
- 01.Punta al VPS un record A (e un record AAAA se usi IPv6) per ogni hostname prima di avviare Caddy, altrimenti l'emissione dei certificati fallirà.
- 02.Crea la rete condivisa una sola volta: `docker network create web`.
- 03.Crea `/opt/proxy/compose.yaml` e `/opt/proxy/Caddyfile` come mostrato sotto, poi esegui `docker compose up -d` in `/opt/proxy`.
- 04.Collega ogni stack applicativo alla rete esterna `web` e avvialo con `docker compose up -d`.
- 05.Segui l'emissione del primo certificato con `docker compose logs -f caddy` in `/opt/proxy`.
- 06.Dopo aver modificato il Caddyfile, ricarica senza downtime: `docker compose exec -w /etc/caddy caddy caddy reload`.
# /opt/proxy/compose.yaml
services:
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp" # HTTP/3
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data # certificates and ACME account keys
- caddy_config:/config
networks:
- web
volumes:
caddy_data:
caddy_config:
networks:
web:
external: trueCollega un'app e scrivi il Caddyfile
Ogni stack applicativo dichiara la stessa rete esterna. Il nome del servizio diventa l'hostname che Caddy contatta. Il volume `caddy_data` è quello che non devi mai perdere: eliminarlo costringe Caddy a richiedere di nuovo tutti i certificati, il che può farti incappare nei rate limit ACME.
# /opt/app/compose.yaml
services:
app:
image: ghcr.io/example/app:latest
restart: unless-stopped
expose:
- "3000" # reachable on the network, NOT published on the host
networks:
- web
networks:
web:
external: true
# /opt/proxy/Caddyfile
{
email [email protected]
}
app.example.com {
reverse_proxy app:3000
}
grafana.example.com {
reverse_proxy grafana:3000
}
api.example.com {
reverse_proxy api-1:8080 api-2:8080 {
lb_policy round_robin
health_uri /healthz
health_interval 10s
}
}Backend in Caddy: upstream, load balancing e health check
Nella terminologia di Caddy un backend è un upstream di `reverse_proxy`. Puoi elencare più upstream in una sola direttiva, scegliere una `lb_policy` come `round_robin`, `least_conn` o `ip_hash` e aggiungere health check attivi con `health_uri` così i container guasti escono dalla rotazione. Anche i check passivi (`fail_duration`, `max_fails`) sono utili quando l'app non ha un endpoint di health.
Per impostazione predefinita Caddy imposta `X-Forwarded-For`, `X-Forwarded-Proto` e `X-Forwarded-Host` sulle richieste inoltrate. Configura la tua app perché si fidi del range di rete del proxy, così vede il vero IP del client e genera link `https://`. WebSocket e gRPC funzionano attraverso `reverse_proxy` senza impostazioni aggiuntive; per gRPC in chiaro usa `reverse_proxy h2c://service:50051`.
Label al posto del Caddyfile: caddy-docker-proxy
Chi cerca un «Caddy proxy manager» di solito vuole quello che offre Nginx Proxy Manager: aggiungi un container e ottieni hostname e certificato senza modificare un file centrale. Caddy non ha una GUI ufficiale, ma il progetto della community caddy-docker-proxy (github.com/lucaslorentz/caddy-docker-proxy) genera il Caddyfile dalle label dei container e ricarica Caddy quando i container si avviano o si fermano.
Richiede accesso in lettura al socket Docker, che di fatto equivale a root sull'host. Montalo in sola lettura, tieni aggiornata l'immagine del proxy e preferisci il Caddyfile semplice se gestisci solo pochi servizi.
services:
caddy:
image: lucaslorentz/caddy-docker-proxy:ci-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
environment:
- CADDY_INGRESS_NETWORKS=web
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- caddy_data:/data
networks:
- web
whoami:
image: traefik/whoami
networks:
- web
labels:
caddy: whoami.example.com
caddy.reverse_proxy: "{{upstreams 80}}"
volumes:
caddy_data:
networks:
web:
external: trueCaddyfile semplice vs caddy-docker-proxy
| Caddyfile semplice | Label caddy-docker-proxy | |
|---|---|---|
| La configurazione sta in | Un solo file versionato in git | Label sparse tra i file compose |
| Accesso al socket Docker | Non necessario | Obbligatorio (delicato per la sicurezza) |
| Nuovo servizio | Modifica il Caddyfile, esegui `caddy reload` | Aggiungi le label, avvia il container |
| Immagine | Ufficiale `caddy:2` | Immagine della community basata su Caddy |
| Debug | Leggi direttamente il file | Ispeziona la configurazione generata nei log |
| Ideale per | Setup VPS piccoli e stabili | Molti servizi di breve durata o modificati spesso |
Eseguirlo su un VPS
Su un VPS KVM NoctisNode hai root completo e un IPv4 dedicato, quindi pubblicare 80 e 443 da un unico container Caddy e nascondere tutto il resto è semplice. Un Wisp (1 vCPU, 1 GB di RAM) basta per Caddy più un paio di container leggeri; gli stack più pesanti stanno bene su Pulse o Umbra. I dettagli sull'emissione dei certificati e i possibili errori sono trattati in come funziona l'HTTPS automatico di Caddy, mentre l'inoltro TCP grezzo o SSH è in proxy TCP e SSH con Caddy. Proteggi l'SSH dell'host con la guida all'hardening SSH.
FAQ
QCome faccio a usare Caddy come reverse proxy verso un container Docker?
AMetti Caddy e il container sulla stessa rete Docker definita dall'utente e usa `reverse_proxy service-name:port` nel Caddyfile. Non usare `localhost`, perché dentro il container di Caddy si riferisce a Caddy stesso.
QPerché Caddy restituisce 502 Bad Gateway per il mio container?
ADi solito i container non sono su una rete condivisa, la porta è sbagliata (usa la porta interna del container, non una porta pubblicata sull'host) oppure l'app ascolta solo su 127.0.0.1 dentro il suo container. Fai in modo che l'app si metta in ascolto su 0.0.0.0 e controlla `docker network inspect web`.
Qcaddy-docker-proxy è sicuro da usare?
AFunziona bene, ma richiede il socket Docker, che concede un controllo equivalente a root sull'host. Montalo in sola lettura, tieni aggiornata l'immagine ed evita di esporre l'endpoint admin di Caddy.
QDove salva i certificati Caddy in Docker?
AIn `/data` dentro l'immagine ufficiale, quindi monta lì un volume con nome. Perderlo significa riemettere tutti i certificati e potenzialmente raggiungere i rate limit ACME.
QEsiste un Caddy proxy manager con interfaccia web?
ACaddy di per sé non ha un'interfaccia web ufficiale; si configura con un Caddyfile, JSON o la sua API admin. Strumenti basati su label come caddy-docker-proxy offrono un flusso simile: aggiungi un container e sei pronto.