Publication réversible, avec vérification après, pas seulement avant
La publication ne se termine pas lorsque les fichiers sont arrivés sur le serveur, mais lorsqu’une requête réelle prouve que la nouvelle version est bien celle servie. Dans l’une des implémentations, le script synchronise le répertoire, conserve la version précédente à côté, puis extrait le nom du paquet depuis la page livrée et vérifie sur l’adresse publique que c’est bien ce fichier exact qui est servi; sinon, il revient tout seul à la version précédente. Ce n’est pas théorique: le mécanisme a détecté un paquet resté ancien dans le répertoire du runner et a annulé une publication qui, sinon, aurait semblé réussie.
Basculement atomique de version, avec le processus sous systemd
L’alternative à « on arrête, on copie par-dessus, on redémarre » est le répertoire de versions plus un lien symbolique basculé en une seule opération. L’unité systemd pointe toujours vers le chemin stable, et le retour en arrière signifie rebascule du lien. L’unité a une politique de redémarrage en cas d’échec avec une courte pause, un temps d’arrêt maximal, et des restrictions système: sans élévation de privilèges, système de fichiers protégé, répertoires personnels inaccessibles, avec une liste explicite de chemins dans lesquels elle a le droit d’écrire.
nginx en frontal, avec des limites écrites pour le cas réel
Terminaison TLS avec renouvellement automatique du certificat, HSTS avec une durée d’un an et inclusion des sous-domaines, compression avec seuil minimal de taille, ressources statiques avec expiration longue et marquage immuable, et l’application liée à l’interface locale afin de ne pas pouvoir être atteinte directement depuis Internet. Les limites de débit se définissent par type de trafic, pas globalement. Une leçon payée mérite d’être dite: sous HTTP/2, la limite de connexions compte les flux, pas les connexions — à une valeur faible, un seul chargement de page se renvoie lui-même ses polices et scripts avec 429.
Copie de sauvegarde qui se vérifie, pas seulement qui s’exécute
Tâche quotidienne programmée, avec lancement au prochain boot si la machine était arrêtée à l’heure correspondante et avec un délai aléatoire pour que tout ne démarre pas à la même seconde. L’export de la base est compressé, et si le fichier obtenu est vide, l’exécution est traitée comme un échec — un export qui se termine « avec succès » et produit zéro octet est la manière habituelle de découvrir, six mois plus tard, qu’il n’existe aucune copie. Les fichiers du dépôt d’objets sont synchronisés séparément. Une copie 100 % locale ne survit pas à la perte du disque, donc la copie part aussi en dehors de la machine, dans un compte de stockage de l’Union européenne avec réplication géographique, versioning, suppression réversible et expiration automatique — avec accès via identité gérée, afin qu’aucune clé de stockage ne se trouve sur le disque.
La suppression programmée ne s’exécute qu’après une copie réussie
La tâche de rétention dépend explicitement de celle de copie et démarre après elle. Un nettoyage sans copie fraîche est la seule forme de suppression dont on ne revient pas. Contrairement à la copie, la suppression ne récupère pas les exécutions manquées: si la machine était arrêtée, on ne supprime pas deux fois le lendemain. Chaque exécution écrit une ligne dans un tableau d’exécutions, et avant la première activation réelle la tâche tourne à vide pendant au moins une semaine et le journal est lu.
Des alertes qui ne mentent pas et ne spamment pas
Une tâche programmée qui renvoie 200 ne veut pas dire qu’elle a fonctionné. Notre enveloppe d’exécution lit le corps de la réponse et y cherche les échecs partiels, puis envoie une alerte avec limitation de fréquence, afin qu’un service en panne toute la journée ne produise pas quatre-vingt-seize messages identiques. La raison de son existence: un rapport quotidien a été mort onze jours de suite, et la seule trace était un vide dans un tableau que personne ne regardait. La tâche programmée s’était déclenchée les onze fois.
Chaîne de publication qui ne peut pas être détournée en amont
Les actions du flux d’intégration sont fixées sur l’empreinte complète du commit, pas sur l’étiquette. En mars 2026, une action populaire a été redirigée des étiquettes `v1`…`v45` vers du code malveillant et a touché plus de 23.000 dépôts en 24 heures ; une empreinte ne se déplace pas. Le flux ne conserve pas d’identifiants dans la copie de travail, et la publication ne passe pas par un jeton : elle passe par un runner propre, sur la machine cible, avec des droits accordés ponctuellement. Ainsi, aucune clé d’accès au serveur ne reste dans la plateforme de code.
Conteneurs avec limites, vérifications de santé et journaux qui ne remplissent pas le disque
Chaque service a une politique de redémarrage, une vérification de santé propre (préparation de la base de données, un point d’état de l’application), des limites de processeur et de mémoire lorsque la charge l’exige, et la rotation des journaux au niveau du moteur de conteneurs, avec taille maximale et nombre de fichiers. Les ports des bases de données sont liés à l’interface locale, jamais publics. Les variables obligatoires sont déclarées de sorte que le conteneur refuse de démarrer si elles manquent, au lieu de démarrer avec une valeur par défaut dangereuse.
Base de données auto-hébergée, quand l’indépendance compte
Pile complète en conteneurs — base de données, authentification, interface REST, canal temps réel, stockage de fichiers, passerelle d’accès — avec les extensions activées explicitement et avec les paramètres de mémoire et de connexions adaptés à la machine, et non laissés aux valeurs par défaut. Les migrations sont des fichiers versionnés dans le dépôt et s’appliquent manuellement, avec redémarrage du composant qui garde son schéma en mémoire. La raison de ce choix est simple: une plateforme gérée en dehors de l’Union européenne peut être excellente techniquement et pourtant inadaptée juridiquement.