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 |

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 :

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

Pour ajouter un certificat :
- Selectionner le deployment cible
- Entrer le domaine souhaite
- Choisir l’issuer (Let’s Encrypt)
- 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