Ce guide explique comment mettre en place du HTTPS valide en local sur un homelab avec Traefik, Cloudflare, Let’s Encrypt et Pi-hole v6.
L’objectif est simple: obtenir un certificat valide pour des sous-domaines comme homeassistant.mydomain.fr ou vault.mydomain.fr, sans exposer votre serveur sur Internet. Les ports 80 et 443 peuvent rester fermés sur votre box. La validation du certificat se fait via le challenge DNS-01, donc Let’s Encrypt vérifie que vous contrôlez le domaine en discutant directement avec l’API DNS de Cloudflare.
Ce que cette méthode fait, et ce qu’elle ne fait pas
Cette méthode permet de:
- garder vos services accessibles uniquement depuis votre réseau local
- utiliser un vrai certificat TLS public et valide
- déléguer la génération et le renouvellement des certificats à Traefik
- faire résoudre
*.mydomain.frvers l’IP locale de Traefik grâce à Pi-hole
En revanche, cette méthode ne fait pas les choses suivantes:
- Cloudflare ne proxy pas votre trafic local
- vos services ne deviennent pas accessibles publiquement
- le HTTPS ne remplace pas le durcissement des applications exposées sur votre LAN
Autrement dit, Cloudflare sert ici uniquement à prouver à Let’s Encrypt que vous contrôlez bien mydomain.fr.
1. Créer un token API Cloudflare minimal
Pour que Traefik puisse créer puis supprimer les entrées DNS temporaires demandées par Let’s Encrypt, il lui faut un token Cloudflare ayant uniquement les droits nécessaires.
- Connectez-vous à votre tableau de bord Cloudflare.
- Ouvrez My Profile puis API Tokens.
- Cliquez sur Create Token.
- Utilisez le modèle Edit zone DNS.
- Configurez les droits suivants:
Permissions:
Zone | DNS | Edit
Zone Resources:
Include | Specific zone | mydomain.fr
- Créez le token puis copiez-le immédiatement.
Ne le commitez jamais dans Git. Le plus simple est de le stocker dans un fichier d’environnement non versionné, par exemple .env:
CLOUDFLARE_DNS_API_TOKEN=CLOUDFLARE_DNS_API_TOKEN_A_REMPLACER
CLOUDFLARE_ZONE_API_TOKEN=CLOUDFLARE_ZONE_API_TOKEN_A_REMPLACER
2. Faire pointer le domaine local vers Traefik avec Pi-hole v6
Le certificat sera demandé pour des noms publics comme homeassistant.mydomain.fr, mais les machines de votre réseau local ne doivent pas sortir sur Internet pour atteindre ces services. Il faut donc dire à Pi-hole que tout ce qui se termine par mydomain.fr doit pointer vers l’IP locale de Traefik, par exemple 192.168.87.10.
Dans Pi-hole v6, la méthode la plus propre consiste à injecter la règle directement dans le docker-compose.yml:
services:
pihole:
image: pihole/pihole:latest
environment:
TZ: "Europe/Paris"
FTLCONF_misc_dnsmasq_lines: "address=/mydomain.fr/192.168.87.10"
Si vous avez plusieurs directives dnsmasq à ajouter dans cette variable, vous pouvez les séparer par des ;.
Relancez ensuite le conteneur:
docker compose up -d --force-recreate
Vous pouvez vérifier tout de suite la résolution DNS locale:
nslookup homeassistant.mydomain.fr 192.168.87.10
Réponse attendue:
Server: 192.168.87.10
Address: 192.168.87.10#53
Name: homeassistant.mydomain.fr
Address: 192.168.87.10
3. Préparer Traefik pour stocker les certificats
Traefik stocke les certificats obtenus dans un fichier acme.json. Ce fichier doit avoir des permissions restrictives, sinon Traefik refusera de démarrer.
Sur l’hôte Docker:
mkdir -p /volume2/docker/traefik/data
touch /volume2/docker/traefik/data/acme.json
chmod 600 /volume2/docker/traefik/data/acme.json
4. Configurer Traefik directement dans docker-compose.yml
Si vous préférez tout piloter depuis le service Traefik lui-même, vous pouvez déclarer les entryPoints, le provider Docker et le resolver Let’s Encrypt directement dans command.
services:
traefik:
image: traefik:v3.7
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "9090:8080" # acceptable en homelab si limité au LAN
environment:
- CLOUDFLARE_DNS_API_TOKEN=${CLOUDFLARE_DNS_API_TOKEN}
- CLOUDFLARE_ZONE_API_TOKEN=${CLOUDFLARE_ZONE_API_TOKEN}
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.file.filename=/etc/traefik/dynamic.yml"
- "--providers.file.watch=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
# HTTPS ENTRY POINT
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
# CLOUDFLARE RESOLVER
- "--certificatesresolvers.cloudflare-dns.acme.email=admin@mydomain.fr" # <= A REMPLACER
- "--certificatesresolvers.cloudflare-dns.acme.storage=/acme.json"
- "--certificatesresolvers.cloudflare-dns.acme.dnschallenge=true"
- "--certificatesresolvers.cloudflare-dns.acme.dnschallenge.provider=cloudflare"
- "--certificatesresolvers.cloudflare-dns.acme.dnschallenge.resolvers=1.1.1.1:53,1.0.0.1:53"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "/volume2/docker/traefik/traefik-dynamic.yml:/etc/traefik/dynamic.yml:ro"
- "/volume2/docker/traefik/data/acme.json:/acme.json"
- "/volume2/docker/traefik/logs:/var/log/traefik"
networks:
- traefik
networks:
traefik:
external: true
Avec cette approche, le resolver cloudflare-dns est déclaré au même endroit que le reste de la configuration Traefik, ce qui évite d’avoir un traefik.yml séparé pour le strict minimum.
Les ports 80 et 443 sont bien ouverts sur la machine qui héberge Traefik, mais rien n’oblige à les exposer vers Internet sur votre box. Ils doivent simplement rester joignables depuis votre réseau local.
Le dashboard --api.insecure=true peut être acceptable sur un homelab, mais uniquement si son accès reste cantonné au LAN ou protégé en amont.
5. Déclarer les routers TLS via les labels Docker
Une fois Traefik configuré, le plus simple est d’utiliser les labels Docker pour rattacher chaque service au bon nom d’hôte et au bon resolver.
Exemple avec Pi-hole:
labels:
- "traefik.enable=true"
- "traefik.http.routers.pihole.entrypoints=websecure"
- "traefik.http.routers.pihole.rule=Host(`pihole.mydomain.fr`)"
- "traefik.http.routers.pihole.tls=true"
- "traefik.http.routers.pihole.tls.certresolver=cloudflare-dns"
- "traefik.http.services.pihole.loadbalancer.server.port=80" # le port exposé par le container
Vous pouvez faire exactement la même chose pour les autres services Docker du homelab:
L’idée reste toujours la même:
- un
Host()par sous-domaine - l’entrypoint
websecure tls=truetls.certresolver=cloudflare-dns
Traefik demandera automatiquement le certificat nécessaire dès qu’il verra le routeur correspondant.
6. Option: exposer un service hors Docker avec dynamic.yml
Si un service tourne directement sur une autre machine du réseau, ou sur une IP locale brute, le provider fichier reste pratique. Il devient alors un complément aux labels, pas le mécanisme principal.
Exemple avec un Home Assistant:
http:
routers:
homeassistant:
rule: "Host(`homeassistant.mydomain.fr`)"
entryPoints:
- websecure
service: homeassistant-service
tls:
certResolver: cloudflare-dns
services:
thermostat-service:
loadBalancer:
servers:
- url: "http://192.168.87.42"
L’intérêt de cette approche est qu’elle fonctionne aussi pour des services qui ne sont pas dans le même docker-compose.yml que Traefik.
7. Validation
Une fois la pile relancée avec docker compose up -d, Traefik va:
- détecter un routeur TLS demandant un certificat
- appeler l’API Cloudflare pour créer un enregistrement temporaire
_acme-challenge - laisser Let’s Encrypt valider la zone
- stocker le certificat dans
acme.json - servir ensuite le service en HTTPS sur le LAN
Depuis un poste de votre réseau local, vous pouvez alors ouvrir une URL comme:
https://homeassistant.mydomain.fr
Si tout est correct, vous aurez un certificat valide et un cadenas sans alerte.
Pour confirmer que le DNS local pointe bien vers Traefik, vous pouvez aussi tester:
nslookup vault.mydomain.fr
La résolution doit renvoyer l’IP LAN de votre reverse proxy, pas une IP publique.
8. Limites et précautions
Quelques points importants pour finir:
- limitez le token Cloudflare au strict minimum, sur une seule zone
- ne stockez pas ce token en clair dans un dépôt Git
- gardez
acme.jsonavec des permissions600 - cette méthode évite l’exposition Internet pour la validation du certificat, mais n’empêche pas un service mal configuré d’être vulnérable sur le LAN
- si un client du réseau ne passe pas par Pi-hole, il risque de résoudre
mydomain.frsur une IP publique au lieu de l’IP locale de Traefik et donc d’avoir une 404. Dans mon cas j’ai configuré pihole au niveau du routeur comme resolver DNS.
Cette combinaison Pi-hole + Traefik + Cloudflare + Let’s Encrypt est à mon sens l’une des solutions les plus propres pour obtenir un HTTPS local crédible, automatisé et maintenable dans un homelab. Tout se fait par label docker, le simple fait de déclarer une stack va engendrer la crétion d’un certificat.
