Nomad + Hetzner DNS + Let’s Encrypt : HTTPS automatique en 5 minutes

devops
nomad
traefik
hetzner
letsencrypt
dns
infrastructure
Author

Sylvain Pham

Published

January 24, 2026

Finalisation d’une infrastructure Nomad : DNS wildcard avec Hetzner, certificats SSL automatiques via Traefik/Let’s Encrypt, et déploiement d’apps en quelques lignes.

Contexte

Suite au premier article sur la migration vers Nomad, il manquait la brique essentielle : exposer les services en HTTPS avec des certificats valides. Cette partie couvre la mise en place complète du DNS et des certificats SSL.

Architecture finale

                        Internet
                            │
                   ┌────────▼────────┐
                   │   Hetzner DNS   │
                   │ *.lab.sylvainp.dev
                   └────────┬────────┘
                            │
              ┌─────────────┴─────────────┐
              │                           │
      ┌───────▼───────┐          ┌────────▼────────┐
      │  dataplatform │          │      ddim       │
      │ 157.180.26.24 │          │ 95.216.158.226  │
      │               │          │                 │
      │  ┌─────────┐  │          │  MySQL, Neo4j   │
      │  │ Traefik │  │          │  (workloads)    │
      │  │ :80/443 │  │          └─────────────────┘
      │  └────┬────┘  │
      │       │       │
      │  ┌────▼────┐  │
      │  │  Nomad  │  │
      │  │ 25+ jobs│  │
      │  └─────────┘  │
      └───────────────┘

1. DNS Wildcard avec Hetzner

Hetzner fournit un service DNS gratuit. Un seul enregistrement wildcard suffit pour router tous les sous-domaines vers Traefik.

Configuration

# Lister les zones
hcloud zone list

# Créer le wildcard
hcloud zone rrset create \
  --name "*.lab" \
  --type A \
  --record "157.180.26.24" \
  sylvainp.dev

Résultat : tout *.lab.sylvainp.dev pointe vers le serveur principal.

Vérification

dig grafana.lab.sylvainp.dev @hydrogen.ns.hetzner.com

# Résultat attendu :
# grafana.lab.sylvainp.dev. 300 IN A 157.180.26.24

2. Traefik avec Let’s Encrypt

Traefik gère automatiquement l’obtention et le renouvellement des certificats via le challenge HTTP-01.

Job Nomad Traefik (extrait)

job "traefik" {
  datacenters = ["hel1"]
  type        = "service"

  group "ingress" {
    # Volume pour persister les certificats
    volume "traefik-certs" {
      type      = "host"
      source    = "traefik-certs"
      read_only = false
    }

    network {
      port "http"  { static = 80 }
      port "https" { static = 443 }
    }

    task "traefik" {
      driver = "docker"

      volume_mount {
        volume      = "traefik-certs"
        destination = "/letsencrypt"
        read_only   = false
      }

      config {
        image = "traefik:v3.2"
        ports = ["http", "https"]
        args = [
          "--api.insecure=true",
          "--entrypoints.web.address=:80",
          "--entrypoints.websecure.address=:443",
          "--entrypoints.web.http.redirections.entryPoint.to=websecure",
          "--providers.consulCatalog=true",
          "--providers.consulCatalog.endpoint=172.17.0.1:8500",
          "--certificatesresolvers.letsencrypt.acme.email=admin@example.com",
          "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json",
          "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web",
        ]
      }
    }
  }
}

Points clés

Élément Rôle
httpChallenge Let’s Encrypt valide via HTTP (port 80)
/letsencrypt/acme.json Stockage persistant des certificats
providers.consulCatalog Auto-découverte des services Nomad
Redirect HTTP → HTTPS Forcé au niveau entrypoint

3. Persistance des certificats

Sans persistance, chaque redémarrage de Traefik déclenche une nouvelle demande de certificat. Let’s Encrypt limite à 50 certificats/domaine/semaine.

Configuration du host volume

Sur le serveur Nomad :

# Créer le répertoire
mkdir -p /opt/nomad/volumes/traefik-certs
chmod 700 /opt/nomad/volumes/traefik-certs

Dans /etc/nomad.d/client.hcl :

client {
  host_volume "traefik-certs" {
    path      = "/opt/nomad/volumes/traefik-certs"
    read_only = false
  }
}

Vérification

# Certificats stockés
cat /opt/nomad/volumes/traefik-certs/acme.json | python3 -m json.tool | grep main

Vue Storage & Backup

Le dashboard inclut une vue dédiée au stockage avec les priorités de backup par service :

Vue Storage & Backup
  • Volumes CSI (Hetzner) : survivent à la perte de VM, snapshots automatiques
  • Volumes Host : backup manuel requis, chemins dans /opt/nomad/volumes/
  • Priorités : critique (bases de données), haute (InfluxDB, Registry), moyenne (Grafana, certificats)

4. Déployer une app en HTTPS

Avec cette infrastructure, déployer une nouvelle app avec HTTPS automatique prend 3 étapes.

Étape 1 : Build et push l’image

cd /path/to/myapp

# Build
docker build -t 157.180.26.24:5000/myapp:latest .

# Push vers le registry privé
docker push 157.180.26.24:5000/myapp:latest

Étape 2 : Job Nomad avec tags Traefik

job "myapp" {
  datacenters = ["hel1"]
  type        = "service"

  group "web" {
    network {
      port "http" { to = 80 }
    }

    task "app" {
      driver = "docker"

      config {
        image = "157.180.26.24:5000/myapp:latest"
        ports = ["http"]
      }

      resources {
        cpu    = 100
        memory = 128
      }

      service {
        name = "myapp"
        port = "http"
        tags = [
          "traefik.enable=true",
          "traefik.http.routers.myapp.rule=Host(`myapp.lab.sylvainp.dev`)",
          "traefik.http.routers.myapp.tls=true",
          "traefik.http.routers.myapp.tls.certresolver=letsencrypt",
        ]

        check {
          type     = "http"
          path     = "/"
          interval = "30s"
          timeout  = "5s"
        }
      }
    }
  }
}

Étape 3 : Déployer

export NOMAD_ADDR=http://157.180.26.24:4646
nomad job run myapp.nomad.hcl

C’est tout. L’app est accessible en HTTPS avec un certificat valide.

curl -I https://myapp.lab.sylvainp.dev
# HTTP/2 200
# Certificate: Let's Encrypt

5. Protection BasicAuth (optionnel)

Pour protéger une app par mot de passe :

# Générer le hash
htpasswd -nbB admin "secret123"
# admin:$2y$05$...

Ajouter dans les tags du service :

tags = [
  # ... tags existants ...
  "traefik.http.routers.myapp.middlewares=myapp-auth",
  "traefik.http.middlewares.myapp-auth.basicauth.users=admin:$$2y$$05$$...",
]

Services exposés

Service URL Certificat
Dashboard dashboard.lab.sylvainp.dev Let’s Encrypt
Grafana grafana.lab.sylvainp.dev Let’s Encrypt
Airflow airflow.lab.sylvainp.dev Let’s Encrypt
Keycloak keycloak.lab.sylvainp.dev Let’s Encrypt

Troubleshooting

Certificat “TRAEFIK DEFAULT CERT”

Le certificat Let’s Encrypt n’est pas encore émis. Vérifier :

# Logs Traefik
nomad alloc logs <traefik-alloc-id>

# Vérifier que le port 80 est accessible
curl http://myapp.lab.sylvainp.dev

DNS ne résout pas

# Vérifier via nameservers Hetzner
dig myapp.lab.sylvainp.dev @hydrogen.ns.hetzner.com

Vérifier un certificat

echo | openssl s_client -connect 157.180.26.24:443 \
  -servername myapp.lab.sylvainp.dev 2>/dev/null | \
  openssl x509 -noout -issuer -dates

Récapitulatif

Composant Solution
DNS Hetzner DNS (wildcard)
Reverse Proxy Traefik v3
Certificats Let’s Encrypt (auto)
Orchestration HashiCorp Nomad
Discovery HashiCorp Consul

Cette stack permet de déployer n’importe quelle app conteneurisée en HTTPS en moins de 5 minutes, sans configuration DNS manuelle pour chaque service.


Hetzner DNS · Traefik · Let’s Encrypt · Nomad