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.fr vers 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.

  1. Connectez-vous à votre tableau de bord Cloudflare.
  2. Ouvrez My Profile puis API Tokens.
  3. Cliquez sur Create Token.
  4. Utilisez le modèle Edit zone DNS.
  5. Configurez les droits suivants:
Permissions:
Zone | DNS | Edit

Zone Resources:
Include | Specific zone | mydomain.fr
  1. 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=true
  • tls.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:

  1. détecter un routeur TLS demandant un certificat
  2. appeler l’API Cloudflare pour créer un enregistrement temporaire _acme-challenge
  3. laisser Let’s Encrypt valider la zone
  4. stocker le certificat dans acme.json
  5. 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.json avec des permissions 600
  • 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.fr sur 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.