Skip to content

Installer Caddy en conteneur LXC sur Proxmox

Guide d'installation manuelle, étape par étape, de Caddy en conteneur LXC sur Proxmox VE, en reverse proxy central avec TLS automatique (Let's Encrypt).

Pourquoi Caddy plutôt que nginx + certbot ?

  • HTTPS automatique intégré — aucun outil externe (pas de certbot, pas d'acme.sh)
  • Reload de configuration sans coupure de service
  • Fichier de configuration (Caddyfile) très lisible : ajouter une route = 3 lignes
  • Debug simple : un seul outil, un seul log

Prérequis

  • Un hôte Proxmox VE avec accès pct (en root, sur le shell de l'hôte)
  • Accès internet depuis l'hôte Proxmox (téléchargement du binaire + validation ACME Let's Encrypt)
  • Les ports 80 et 443 redirigés par votre routeur vers l'hôte Proxmox
  • Un nom de domaine pointant vers votre IP publique (un enregistrement DNS A/AAAA par sous-domaine à exposer) — propagé avant le premier démarrage de Caddy avec ce domaine dans le Caddyfile : la validation ACME HTTP-01 échoue si le DNS ne résout pas encore vers votre IP au moment de la demande de certificat

Dans ce guide, on utilisera ces valeurs d'exemple — à remplacer par les vôtres :

Paramètre Valeur d'exemple
ID du conteneur 200
Hostname caddy.votre-domaine.tld
IP fixe 192.168.1.200/24
Passerelle 192.168.1.1
Stockage Proxmox local-lvm
Email ACME admin@votre-domaine.tld

1⃣ Création du conteneur LXC

Télécharger le template Debian 12 s'il n'est pas déjà présent :

pveam update
pveam available | grep debian-12-standard
pveam download local debian-12-standard_12.7-1_amd64.tar.zst   # adapter le nom exact retourné ci-dessus

Créer le conteneur (léger : Caddy ne nécessite que très peu de ressources) :

pct create 200 local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \
    --hostname caddy.votre-domaine.tld \
    --memory 128 \
    --swap 64 \
    --cores 1 \
    --rootfs local-lvm:4 \
    --net0 name=eth0,bridge=vmbr0,ip=192.168.1.200/24,gw=192.168.1.1 \
    --unprivileged 1 \
    --onboot 1

pct start 200

2⃣ Installation des paquets de base

pct exec 200 -- apt-get update
pct exec 200 -- apt-get install -y --no-install-recommends curl ca-certificates

3⃣ Téléchargement et installation du binaire Caddy

Sur l'hôte Proxmox, récupérer la dernière version officielle :

curl -sL https://api.github.com/repos/caddyserver/caddy/releases/latest \
    | grep browser_download_url | grep linux_amd64.tar.gz | grep -v '\.sig\|\.txt' \
    | head -1 | cut -d'"' -f4

Télécharger et extraire le binaire dans le conteneur :

curl -sL "<URL_RETOURNÉE_CI-DESSUS>" -o /tmp/caddy.tar.gz
tar -xzf /tmp/caddy.tar.gz -C /tmp caddy
pct push 200 /tmp/caddy /usr/local/bin/caddy
pct exec 200 -- chmod +x /usr/local/bin/caddy
rm -f /tmp/caddy.tar.gz /tmp/caddy

Créer l'utilisateur système et les répertoires nécessaires :

pct exec 200 -- bash -c "
    useradd -r -s /bin/false -d /var/lib/caddy caddy &&
    mkdir -p /etc/caddy /var/lib/caddy /var/log/caddy &&
    chown caddy:caddy /var/lib/caddy /var/log/caddy
"

4⃣ Fichier de configuration (Caddyfile)

Créer /etc/caddy/Caddyfile dans le conteneur :

pct exec 200 -- bash -c "cat > /etc/caddy/Caddyfile" << 'EOF'
{
    email admin@votre-domaine.tld
}

mon-service.votre-domaine.tld {
    reverse_proxy 192.168.1.X:PORT
}
EOF

Caddy gère automatiquement, pour chaque bloc de domaine : la demande et le renouvellement du certificat Let's Encrypt, la redirection HTTP → HTTPS, et le reverse proxy vers le service interne ciblé.

Cas particuliers utiles

Service avec certificat auto-signé en amont (ex : une interface d'administration interne) :

admin.votre-domaine.tld {
    reverse_proxy https://192.168.1.X:8006 {
        transport http {
            tls_insecure_skip_verify
        }
    }
}

Headers personnalisés (ex : application nécessitant X-Forwarded-Proto) :

app.votre-domaine.tld {
    reverse_proxy 192.168.1.X:PORT
    header_up X-Forwarded-Proto https
}

5⃣ Service systemd

Créer l'unité systemd /etc/systemd/system/caddy.service :

pct exec 200 -- bash -c "cat > /etc/systemd/system/caddy.service" << 'EOF'
[Unit]
Description=Caddy HTTP/2 web server
Documentation=https://caddyserver.com/docs/
After=network-online.target
Wants=network-online.target

[Service]
User=caddy
Group=caddy
ExecStart=/usr/local/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/local/bin/caddy reload --config /etc/caddy/Caddyfile --force
TimeoutStopSec=5s
LimitNOFILE=1048576
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

pct exec 200 -- chown caddy:caddy /etc/caddy/Caddyfile
pct exec 200 -- systemctl daemon-reload
pct exec 200 -- systemctl enable --now caddy

AmbientCapabilities=CAP_NET_BIND_SERVICE permet à Caddy de se lier aux ports 80/443 sans tourner en root.


6⃣ Redirection des ports depuis l'hôte Proxmox (NAT)

Si le conteneur a une IP privée et que le routeur ne forward que vers l'hôte Proxmox, il faut rediriger 80/443 de l'hôte vers le conteneur :

# Activer le forwarding IP sur l'hôte (une fois, persistant)
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
sysctl -p

# Rediriger 80 et 443 vers le conteneur
for PORT in 80 443; do
    iptables -t nat -A PREROUTING -p tcp --dport $PORT -j DNAT --to-destination 192.168.1.200:$PORT
    iptables -I FORWARD -p tcp -d 192.168.1.200 --dport $PORT -j ACCEPT
done

# Persister les règles iptables au redémarrage
iptables-save > /etc/iptables.rules

Ces règles ne sont pas restaurées automatiquement au redémarrage — créer un service systemd dédié :

cat > /etc/systemd/system/iptables-restore.service << 'EOF'
[Unit]
Description=Restore iptables rules
Before=network-pre.target
Wants=network-pre.target

[Service]
Type=oneshot
ExecStart=/sbin/iptables-restore /etc/iptables.rules
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable iptables-restore

⚙️ Points d'administration importants

  • Valider la configuration avant de l'appliquer : pct exec 200 -- caddy validate --config /etc/caddy/Caddyfile — une erreur de syntaxe dans le Caddyfile en production peut faire échouer le reload et laisser Caddy tourner avec l'ancienne config.
  • Recharger sans coupure après modification : pct exec 200 -- systemctl reload caddy (équivalent à caddy reload).
  • Certificats : stockés dans /var/lib/caddy/.local/share/caddy/. Sauvegarder ce répertoire avant toute réinstallation évite de re-déclencher des demandes Let's Encrypt (rate-limit à 50 certificats/semaine/domaine).
  • Limite Let's Encrypt : en cas de test répété, utiliser l'environnement de staging Let's Encrypt (acme_ca https://acme-staging-v02.api.letsencrypt.org/directory dans le bloc global du Caddyfile) pour éviter d'atteindre le rate-limit de production.
  • Logs : pct exec 200 -- journalctl -u caddy -f — c'est la seule source de logs (accès, erreurs, renouvellement de certificats).
  • Supprimer une route : retirer le bloc du Caddyfile puis recharger ; le certificat associé n'est pas révoqué activement, il expire naturellement (90 jours).

Conclusion

Caddy en LXC dédié centralise tout le TLS du réseau interne en un seul point d'entrée, avec une configuration simple à maintenir à la main et un reload sans coupure pour ajouter de nouveaux services au fil de l'eau.