Genèse
J’ai toujours hébergé mes projets chez des tiers. Vercel par-ci, Heroku par-là, un Supabase pour la base. Ça marche, mais on finit par éparpiller ses données chez cinq fournisseurs différents, sans vraiment comprendre ce qui tourne sous le capot. Quand un déploiement échoue sur un PaaS, on ouvre un ticket. Quand il échoue sur son propre serveur, on comprend pourquoi.
Début mars 2026, j’ai commandé un VPS Ubuntu vierge avec une idée simple : tout reconstruire from scratch, sur une seule machine, entièrement automatisé. Le déclencheur concret, c’est ComRenov. J’avais besoin d’héberger cette app quelque part, et plutôt que de prendre un PaaS de plus, j’ai décidé que ce serait l’occasion de monter la plateforme qui accueillerait tous mes projets sur tordu-jardin.fr. Un make deploy et l’ensemble de l’infra se déploie. Pas de clic dans une console web, pas de config manuelle. Juste des rôles Ansible et un Makefile.
Architecture
L’infra repose sur une quinzaine de services Docker orchestrés par 21 rôles Ansible. Chaque rôle est idempotent : on peut le rejouer dix fois, il ne casse rien. L’ensemble se déploie en une commande, ou rôle par rôle avec make deploy TAGS=monitoring quand on veut cibler un morceau précis.
En façade, Traefik sert de reverse proxy. Il découvre les conteneurs via les labels Docker et gère les certificats TLS automatiquement avec Let’s Encrypt. Derrière, trois réseaux isolés séparent le proxy, le monitoring et la base de données. C’est du defense-in-depth : même si un conteneur est compromis, il ne voit pas les autres réseaux. Le socket Docker n’est jamais exposé directement, un proxy read-only filtre les appels.
Côté observabilité, c’est la partie dont je suis le plus content. Prometheus collecte les métriques, Loki agrège les logs, Tempo stocke les traces distribuées, et Grafana affiche tout ça dans une dizaine de dashboards avec des SLOs et du calcul d’error budget. J’ai poussé jusqu’au RUM côté navigateur avec Faro pour avoir les Web Vitals et les erreurs JavaScript en temps réel. Alertmanager route les alertes vers une instance Ntfy self-hosted, donc les notifications arrivent sur mon téléphone sans dépendre d’un service externe.
Ce qui rend l’infra intéressante
Ce n’est pas un homelab bricolé. J’ai emprunté les pratiques SRE qu’on utilise en entreprise et je les ai appliquées à un VPS perso. Pas pour le flex, mais parce que c’est le meilleur endroit pour apprendre : en production professionnelle, l’infra est déjà en place et les choix sont faits. Ici, je peux tout construire de zéro, casser des choses, et comprendre pourquoi ces pratiques existent. Chaque conteneur a un healthcheck, des limites de ressources, et le flag no-new-privileges. Les backups Restic tournent vers Scaleway S3, et PostgreSQL a du Point-In-Time Recovery via WAL-G pour pouvoir restaurer la base à n’importe quel instant.
SSH est accessible uniquement via le tunnel WireGuard. Fail2ban exporte ses métriques vers Prometheus, donc je vois les tentatives d’intrusion dans Grafana. Tous les secrets vivent dans un vault Ansible, et un playbook vérifie avant chaque déploiement qu’aucun placeholder CHANGE_ME ne traîne dans les variables. Pour le CI/CD, j’ai itéré trois fois en moins de deux heures avant de trouver la bonne approche : l’API Portainer avec un token scopé, qui donne au runner GitLab juste ce qu’il faut sans exposer le socket Docker.
La fiabilité de l’ensemble est validée par dr-test, qui simule des scénarios catastrophe dans une VM jetable et mesure le temps de reconstruction complète. L’infra sert déjà de socle pour ComRenov et Basalt Beholder, avec un rôle app-template générique prêt à accueillir les prochains projets.
Journal
Du VPS vierge à une infra fonctionnelle
Tout a commencé un soir de mars. En quelques heures, le scaffold complet était en place : hardening SSH, firewall deny-all, Docker, Traefik avec TLS auto, la stack monitoring au complet, Authentik en SSO centralisé, et les premières apps. Le genre de soirée où tu ne vois pas le temps passer.
Le marathon du 5 mars
La journée la plus intense du projet. PostgreSQL s’installe sur la machine hôte avec une isolation réseau hub & spoke, une suite de smoke tests vérifie que chaque service répond correctement, et les premiers dashboards SLO apparaissent. L’après-midi, j’ajoute le tracing distribué avec Tempo et le RUM navigateur avec Faro. Le debug du pipeline Faro vers Loki m’a donné du fil à retordre, mais en fin de journée le monitoring passait de “ça collecte des métriques” à une observabilité complète.
Hardening et backup
Un sprint dédié à la solidité. Chaque conteneur reçoit ses limites de ressources et ses flags de sécurité. Les backups Restic sont configurés vers S3 avec une rétention 7j/4s/6m. PostgreSQL obtient le PITR via WAL-G. Et un petit service Flask, le pg-provisioner, permet de créer des bases à la volée pour les nouvelles apps sans toucher manuellement au serveur.
Le pipeline CI/CD en trois tentatives
J’ai cherché comment déployer automatiquement depuis GitLab. D’abord les webhooks Portainer, trop fragiles. Puis le mount du Docker socket dans le runner, trop permissif. Finalement, l’API Portainer avec un token scopé a trouvé le bon équilibre : le CI n’a accès qu’à ce dont il a besoin, pas au socket Docker.
Consolidation
Retour après quelques jours de pause. Le pipeline CI révèle ses failles en production : stacks qui ne se recréent pas, cache Docker qui bloque les mises à jour. Le tunnel WireGuard tombait silencieusement, corrigé par un watchdog systemd. Le GitLab Runner passe d’un bricolage à un vrai rôle Ansible. C’est à ce moment-là que le projet atteint sa forme finale : 21 rôles et une infra qu’on peut reconstruire de zéro en une commande.