Mon toolkit DevOps maison : certificats SSL, Docker et générateur de pipelines

devops
docker
ssl
hetzner
ansible
ci-cd
gitlab
gitea
Author

Sylvain Pham

Published

January 2, 2026

Suite de mon article d’hier sur mon toolkit DevOps maison.

La vision

Ma boîte à outils vise à supprimer toute friction entre l’idée générée par une IA et son déploiement : l’IA pousse le code sur GitHub, un clic déploie le tout. Fini la configuration manuelle de l’infra, des certificats, de Docker ou de la base de données, des tâches répétitives qui cassent l’élan créatif et polluent ma charge mentale.

L’automatisation uniformise aussi la sécurité : tous les projets bénéficient du même niveau de protection. Une vulnérabilité dans la supply chain ? On corrige une fois, et les 400 projets sont patchés.

Aujourd’hui, trois nouvelles briques : les certificats SSL automatiques, le déploiement Docker optionnel et un générateur de pipelines CI/CD.

Certificats SSL avec Hetzner DNS

Le problème des apps locales

Comment obtenir un certificat SSL valide pour une application qui n’est pas exposée sur Internet ? Les challenges HTTP classiques de Let’s Encrypt nécessitent que le serveur soit accessible publiquement.

La solution : Pi-hole + Caddy + Hetzner DNS API

┌─────────────────────────────────────────────────────────────────┐
│                    CERTIFICATS SSL                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   Caddy  ──►  DNS Challenge  ──►  Hetzner DNS API               │
│     │                                                            │
│     ▼                                                            │
│   Certificat Let's Encrypt                                       │
│   automatique                                                    │
│                                                                  │
│   Pi-hole  ──►  Split-Horizon DNS                               │
│     │           (résolution locale)                              │
│     ▼                                                            │
│   app.local.domain → 192.168.x.x                                │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Trois composants :

Composant Rôle
Pi-hole DNS interne, résout les domaines vers les IPs locales
Caddy Reverse proxy + obtention automatique des certificats
Hetzner DNS API Validation DNS-01 pour Let’s Encrypt

Split-Horizon DNS avec Pi-hole : le même domaine résout différemment selon où tu te trouves :

  • Depuis Internet → IP publique (ou rien)
  • Depuis le réseau local → Pi-hole redirige vers l’IP privée du serveur

Résultat : des apps accessibles uniquement en local, mais avec des certificats SSL parfaitement valides.

Pourquoi Caddy ?

Caddy simplifie énormément la gestion des certificats : - Obtention et renouvellement automatique - Support natif des DNS challenges - Configuration minimale

# Caddyfile
app.local.domain {
    tls {
        dns hetzner {env.HETZNER_API_TOKEN}
    }
    reverse_proxy localhost:3000
}

Comparé à certbot ou acme.sh, pas de cron à gérer, pas de scripts de renouvellement ; Caddy fait tout.

Déploiement Docker optionnel

Natif ou conteneurisé : le choix

Le Catalog API supporte maintenant deux modes de déploiement :

Mode Cas d’usage Avantages
Natif Apps simples, scripts Léger, accès direct au système
Docker Apps complexes, isolation Reproductible, dépendances isolées

Dashboard des applications - mode Docker et Native

Dans l’interface, on voit les deux modes en action :

  • docker-test : une app API deployee en mode Docker sur le port 3000
  • linkster : une app web en mode natif sur le port 8081

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                    DÉPLOIEMENT                                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   Catalog API                                                    │
│       │                                                          │
│       ├── deploy_type: native  ──►  git clone + systemd         │
│       │                                                          │
│       └── deploy_type: docker  ──►  docker compose up           │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Configuration dans le Catalog

{
  "name": "mon-app",
  "repo": "git@github.com:user/app.git",
  "deploy_type": "docker",
  "docker_compose_file": "docker-compose.prod.yml"
}

Interface de gestion des certificats

L’interface permet de gerer les certificats SSL directement depuis le dashboard :

Liste des certificats SSL

Chaque certificat affiche :

  • Le domaine (ex: docker-test.portal.sylvainp.dev)
  • Le deployment associe
  • L’issuer (Let’s Encrypt)
  • Le status (pending, active, expired)
  • L’option auto-renew

Creation d’un nouveau certificat

Modal de creation de certificat

Pour ajouter un certificat :

  1. Selectionner le deployment cible
  2. Entrer le domaine souhaite
  3. Choisir l’issuer (Let’s Encrypt)
  4. Activer l’auto-renew (recommande)

Le systeme se charge ensuite d’obtenir le certificat via le DNS challenge Hetzner et de configurer Caddy automatiquement.

Générateur de pipelines CI/CD

Configurer un pipeline CI/CD, c’est souvent du copier-coller entre projets avec des ajustements manuels. Pour automatiser ça, j’ai ajouté un générateur de pipelines directement dans le dashboard.

Deux plateformes supportées

Plateforme Fichier généré
Gitea Actions .gitea/workflows/deploy.yaml
GitLab CI .gitlab-ci.yml

Étapes configurables

Le générateur propose des étapes à cocher selon les besoins :

Étape Description
Build & Push Construit l’image Docker et la pousse vers le registry
Deploy Déploie via Ansible sur le serveur cible
Tests Exécute les tests du projet
PHPStan Analyse statique PHP (qualité du code)
Trivy Scan de sécurité de l’image Docker (vulnérabilités)
Notify Notification webhook en fin de pipeline

Mise à jour en temps réel

Dès qu’on coche ou décoche une option, le YAML se régénère instantanément. Plus besoin de cliquer sur un bouton “Générer” : le feedback est immédiat.

Exemple de pipeline généré (GitLab CI)

stages:
  - build
  - security
  - deploy

build:
  stage: build
  image: docker:24
  services:
    - docker:24-dind
  script:
    - docker build -t $IMAGE_NAME:$CI_COMMIT_TAG .
    - docker push $IMAGE_NAME:$CI_COMMIT_TAG

trivy:
  stage: security
  image: aquasec/trivy:latest
  script:
    - trivy image --exit-code 1 --severity CRITICAL,HIGH $IMAGE_NAME:latest

deploy:
  stage: deploy
  script:
    - ansible-playbook deploy.yml -e "app_id=5 server_id=1"

Une fois satisfait, on copie le YAML ou on le télécharge directement.

Pistes d’amélioration : gestion des secrets

Le déploiement Docker soulève la question de la gestion des secrets (credentials registry, tokens API, etc.). Trois approches possibles :

Approche Complexité Sécurité Cas d’usage
Gitea Secrets Simple Correcte Démarrer rapidement
Vault Élevée Excellente Environnement multi-apps
Ansible + Vault Moyenne Excellente Séparation build/deploy

Option 1 : Gitea Secrets : Les secrets sont stockés dans Gitea et injectés via ${{ secrets.VAR }}. Simple et suffisant pour commencer.

Option 2 : Vault : HashiCorp Vault centralise tous les secrets. Le pipeline s’authentifie et récupère les credentials à la volée. Plus sécurisé mais nécessite une infrastructure Vault.

Option 3 : Ansible + Vault : Le pipeline CI ne gère que le build, Ansible gère le déploiement avec les secrets depuis Vault. Élégant car sépare les responsabilités.

Pour l’instant, je pars sur l’option 1 pour me débloquer, avec une migration vers Vault prévue quand le nombre de projets le justifiera.

Prochaines étapes


Stack utilisée : Laravel 12 · Alpine.js · Ansible · Docker · Let’s Encrypt · Hetzner DNS API · Trivy · PHPStan