Comment une erreur de manipulation m’a obligé à comprendre ce que Docker conserve réellement — et à reconstruire mon infrastructure à partir des conteneurs encore vivants
Le 16 août 2026, j’ai commis le genre d’erreur que l’on pense toujours pouvoir éviter jusqu’au jour où elle arrive réellement.
Sur mon VPS, le répertoire :
/opt/homelab/stacks
a été supprimé.
Ce n’était pas un simple dossier de travail. C’était le cœur de mon organisation Docker : fichiers compose.yaml, configurations, sources de plusieurs applications, données liées à certains services et définitions permettant de reconstruire mon infrastructure.
À ce moment-là, mon VPS faisait notamment tourner :
- mon site WordPress NVSD Homelab ;
- Concerto à l’Infini ;
- Fractal Studio ;
- Math Art ;
- plusieurs sites statiques ;
- Nginx Proxy Manager ;
- Dockge ;
- PostgreSQL ;
- MariaDB ;
- Redis.
La première réaction aurait pu être de redémarrer les services, de relancer Docker ou de tenter immédiatement de recréer les stacks.
Cela aurait probablement été la pire chose à faire.
Car malgré la disparition de /opt/homelab/stacks, une chose essentielle était encore vraie :
les conteneurs fonctionnaient toujours.
Et ces conteneurs encore vivants allaient devenir ma principale source de récupération.
Le paradoxe : les fichiers Compose avaient disparu, mais Docker continuait de fonctionner
Lorsque j’ai constaté la suppression du répertoire, j’ai immédiatement vérifié Docker :
docker ps
À ma grande surprise, les services étaient toujours là.
Des conteneurs comme :
concerto-infini-api-1
concerto-infini-web-1
fractal-studio-web
fractal-studio-api
fractal-studio-worker
fractal-studio-db
fractal-studio-redis
math-art
nvsd-homelab-wordpress
nvsd-homelab-db
npm-npm-1
dockge
continuaient à fonctionner.
Cela peut sembler étrange au premier abord, mais c’est une distinction fondamentale dans Docker.
Docker Compose n’est pas Docker
Un fichier compose.yaml est essentiellement une description de l’état souhaité de l’infrastructure.
Il dit à Docker :
- quelle image utiliser ;
- quels volumes monter ;
- quelles variables d’environnement fournir ;
- quels réseaux utiliser ;
- quels ports exposer ;
- quelle commande lancer ;
- quelle politique de redémarrage appliquer.
Mais une fois le conteneur créé, Docker conserve sa propre définition dans son espace interne.
Les images sont également stockées séparément, typiquement dans :
/var/lib/docker
tout comme les volumes Docker.
Supprimer :
/opt/homelab/stacks
ne revient donc pas automatiquement à exécuter :
docker compose down
et encore moins :
docker system prune
Les processus déjà lancés continuent de tourner.
Dans mon cas, cette différence a fait toute la différence.
Première règle : ne plus rien arrêter
À partir de ce moment-là, mon objectif a changé.
Je ne cherchais plus immédiatement à « réparer » le VPS.
Je cherchais d’abord à préserver tout ce qui existait encore.
La règle est devenue :
Ne pas redémarrer. Ne pas recréer. Ne pas nettoyer. Sauvegarder d’abord.
J’ai créé un espace de récupération séparé :
sudo mkdir -p /opt/homelab-recovery
Puis plusieurs sous-répertoires :
sudo mkdir -p \
/opt/homelab-recovery/inspect \
/opt/homelab-recovery/filesystems \
/opt/homelab-recovery/images \
/opt/homelab-recovery/database-dumps \
/opt/homelab-recovery/deleted-open-files
L’idée était simple :
Conteneurs vivants
│
├── configuration Docker
├── images
├── système de fichiers
├── volumes
├── bases de données
└── fichiers supprimés encore ouverts
│
▼
/opt/homelab-recovery
Avant toute reconstruction, je voulais créer une photographie aussi complète que possible de l’état restant.
Faire l’inventaire avant de toucher quoi que ce soit
La première étape a été de documenter les conteneurs encore présents.
docker ps
Puis les volumes :
docker volume ls
Et les réseaux :
docker network ls
Dans mon infrastructure, le réseau Docker proxy était particulièrement important.
C’est lui qui permettait à Nginx Proxy Manager de joindre plusieurs services sans qu’ils soient nécessairement exposés directement sur l’interface publique du VPS.
J’ai ensuite utilisé docker inspect.
Par exemple :
sudo docker inspect concerto-infini-api-1 \
> /opt/homelab-recovery/inspect/concerto-infini-api-1.json
L’opération peut être répétée sur tous les conteneurs.
docker inspect est extrêmement précieux dans ce type de situation.
Même si le fichier Compose original a disparu, il permet de retrouver :
- l’image ;
- les variables d’environnement ;
- les volumes ;
- les bind mounts ;
- les réseaux ;
- les alias réseau ;
- les ports ;
- le
restart policy; - les commandes ;
- les healthchecks ;
- les labels Docker Compose.
Par exemple :
docker inspect fractal-studio-api \
--format '{{json .Mounts}}' | jq
ou :
docker inspect fractal-studio-api \
--format '{{json .NetworkSettings.Networks}}' | jq
Les labels sont eux aussi particulièrement utiles :
docker inspect fractal-studio-api \
--format '{{json .Config.Labels}}' | jq
Docker Compose y inscrit notamment des informations telles que le nom du projet et le nom logique du service.
En pratique, un conteneur encore vivant contient une grande partie de l’information nécessaire pour reconstruire son Compose.
Sauvegarder les images Docker
Certaines de mes applications utilisaient des images construites localement.
Dans une situation normale, une image Docker devrait pouvoir être reconstruite à partir :
- du
Dockerfile; - du code source ;
- des dépendances ;
- du fichier Compose.
Mais une partie de ces éléments venait justement de disparaître.
Il fallait donc considérer les images encore présentes sur le VPS comme des objets précieux.
J’ai créé une archive des images utilisées par les conteneurs actifs.
Le résultat a été enregistré dans :
/opt/homelab-recovery/images/active-container-images.tar
L’archive faisait environ 1,9 Go.
La logique reproductible est la suivante :
docker ps -q \
| xargs docker inspect \
--format '{{.Config.Image}}' \
| sort -u
Cette commande permet d’obtenir la liste des images utilisées.
Elles peuvent ensuite être archivées avec :
sudo docker save \
-o /opt/homelab-recovery/images/active-container-images.tar \
IMAGE_1 IMAGE_2 IMAGE_3
Puis l’archive peut être contrôlée :
tar -tf \
/opt/homelab-recovery/images/active-container-images.tar \
> /dev/null
Une image sauvegardée pourra ensuite être restaurée avec :
docker load \
-i active-container-images.tar
Ce n’est pas une alternative à la conservation du code source.
Mais en situation de récupération, c’est une assurance supplémentaire extrêmement utile.
Exporter les systèmes de fichiers des conteneurs
Deuxième couche de sécurité : docker export.
Cette commande transforme le système de fichiers visible à l’intérieur d’un conteneur en archive TAR.
Exemple :
sudo docker export \
concerto-infini-api-1 \
-o /opt/homelab-recovery/filesystems/concerto-infini-api-1.tar
J’ai réalisé cette opération pour pratiquement tous les conteneurs importants.
Le répertoire de récupération a ainsi contenu notamment :
math-art.tar
concerto-infini-api-1.tar
concerto-infini-web-1.tar
fractal-studio-api.tar
fractal-studio-web.tar
fractal-studio-worker.tar
fractal-studio-db.tar
fractal-studio-redis.tar
npm-npm-1.tar
dockge.tar
nvsd-homelab-wordpress.tar
nvsd-homelab-db.tar
Il faut cependant comprendre une limitation importante :
docker exportne sauvegarde pas le contenu des volumes Docker montés dans le conteneur.
Il sauvegarde le système de fichiers du conteneur.
C’est donc une couche supplémentaire de récupération, mais pas une sauvegarde complète d’une stack.
Une erreur intéressante : Unexpected EOF
Lors du premier export de concerto-infini-api-1, la vérification de l’archive a retourné une erreur :
Unexpected EOF
L’archive était incomplète.
Je l’ai supprimée puis recréée.
La seconde exportation était correcte.
Cette petite erreur a renforcé une règle importante :
Une sauvegarde qui n’a jamais été vérifiée n’est pas encore une sauvegarde.
Après chaque export important :
tar -tf fichier.tar > /dev/null
permet au minimum de vérifier que l’archive TAR peut être parcourue.
Les volumes Docker : ce qui m’a sauvé pour les données persistantes
Les volumes Docker nommés sont indépendants du répertoire contenant le fichier Compose.
Par exemple, supprimer :
/opt/homelab/stacks/nvsd-homelab-wordpress
ne supprime pas automatiquement :
nvsd_homelab_wordpress_data
nvsd_homelab_db_data
Ils restent gérés par Docker.
C’est une des raisons pour lesquelles les données WordPress et MariaDB étaient toujours présentes.
Pour inspecter un volume :
docker volume inspect nvsd_homelab_wordpress_data
La même logique s’applique aux volumes utilisés par PostgreSQL ou d’autres services.
Pour les fichiers ordinaires, un volume peut également être archivé indépendamment du conteneur :
docker run --rm \
-v NOM_DU_VOLUME:/source:ro \
-v /opt/homelab-recovery/volumes:/backup \
alpine \
sh -c 'tar -czf /backup/volume.tar.gz -C /source .'
Pour une base de données active, en revanche, je préfère un dump logique à une simple copie brute du volume.
Sauvegarder PostgreSQL tant que Fractal Studio fonctionnait encore
Fractal Studio utilisait PostgreSQL.
Le conteneur :
fractal-studio-db
était encore fonctionnel.
C’est la situation idéale pour produire une sauvegarde logique.
Une forme pratique, lorsque POSTGRES_USER est défini dans le conteneur, est :
docker exec fractal-studio-db \
sh -c 'pg_dumpall -U "$POSTGRES_USER"' \
> /opt/homelab-recovery/database-dumps/fractal-studio-postgresql.sql
L’intérêt du dump logique est qu’il ne dépend pas de la disposition interne du volume PostgreSQL.
Pour restaurer plus tard :
cat fractal-studio-postgresql.sql \
| docker exec -i fractal-studio-db \
sh -c 'psql -U "$POSTGRES_USER"'
La règle que je retiens est claire :
pour une base active, sauvegarder le contenu avec les outils de la base avant de manipuler son volume.
Même principe pour MariaDB et WordPress
Le site Homelab utilisait :
nvsd-homelab-wordpress
nvsd-homelab-db
avec MariaDB.
Là encore, le fait que le conteneur fonctionne encore permet de produire un dump cohérent.
Par exemple :
docker exec nvsd-homelab-db \
sh -c 'mariadb-dump \
-uroot \
-p"$MARIADB_ROOT_PASSWORD" \
--all-databases \
--single-transaction' \
> /opt/homelab-recovery/database-dumps/nvsd-homelab-mariadb.sql
Le fichier obtenu peut être vérifié simplement :
ls -lh \
/opt/homelab-recovery/database-dumps/nvsd-homelab-mariadb.sql
Puis restauré ultérieurement avec le client MariaDB.
WordPress possède en réalité deux catégories de données à protéger :
Base MariaDB
+
wp-content, médias, thèmes et extensions
Le dump SQL ne suffit donc pas.
Il faut également conserver le volume WordPress.
Redis : quand la sauvegarde RDB refuse de fonctionner normalement
Fractal Studio utilisait également Redis :
fractal-studio-redis
C’est à ce moment qu’une autre difficulté est apparue.
Redis signalait une erreur de type :
MISCONF
liée à l’échec d’une sauvegarde RDB automatique et à la protection :
stop-writes-on-bgsave-error
Plutôt que de commencer à modifier la configuration d’un Redis en pleine récupération, j’ai utilisé directement son mécanisme d’export RDB :
docker exec fractal-studio-redis \
redis-cli --rdb /tmp/fractal-studio-redis.rdb
Puis j’ai récupéré le fichier depuis le conteneur :
docker cp \
fractal-studio-redis:/tmp/fractal-studio-redis.rdb \
/opt/homelab-recovery/database-dumps/fractal-studio-redis.rdb
Le fichier obtenu faisait seulement quelques kilo-octets, ce qui était cohérent avec la faible quantité de données présente à ce moment-là.
Encore une fois, l’objectif n’était pas de « réparer Redis » immédiatement.
L’objectif était :
sortir les données du système vivant avant d’entreprendre quoi que ce soit d’autre.
Le moment le plus intéressant : récupérer des fichiers déjà supprimés grâce à /proc
Nginx Proxy Manager et Dockge posaient un problème différent.
Leurs fichiers SQLite avaient été situés dans l’arborescence supprimée.
Sur le disque, les chemins n’existaient donc plus.
Mais leurs processus fonctionnaient encore.
Sous Linux, supprimer un fichier ne signifie pas nécessairement que ses données disparaissent immédiatement.
Si un processus possède encore un descripteur de fichier ouvert sur ce fichier, l’inode peut rester accessible jusqu’à la fermeture du dernier descripteur.
Et Linux expose ces descripteurs dans :
/proc/<PID>/fd/
C’est probablement la partie la plus spectaculaire de cette récupération.
Rechercher les fichiers supprimés mais encore ouverts
Une commande très utile est :
sudo lsof +L1
On peut filtrer :
sudo lsof +L1 \
| grep '/opt/homelab/stacks'
Cela permet d’identifier les processus utilisant encore des fichiers qui ont été supprimés du système de fichiers.
Récupérer la base SQLite de Nginx Proxy Manager
Pour Nginx Proxy Manager, le processus concerné avait le PID :
8067
et le fichier SQLite était encore accessible par le descripteur :
18
J’ai pu examiner le lien :
sudo readlink /proc/8067/fd/18
puis copier directement les données :
sudo mkdir -p \
/opt/homelab-recovery/deleted-open-files/npm
sudo cp \
/proc/8067/fd/18 \
/opt/homelab-recovery/deleted-open-files/npm/database.sqlite
J’avais de nouveau une base SQLite utilisable :
database.sqlite
d’environ 112 Ko.
Je pouvais ensuite l’interroger :
sqlite3 database.sqlite
Puis :
.tables
et examiner la table importante :
SELECT
id,
domain_names,
forward_host,
forward_port,
certificate_id,
enabled
FROM proxy_host;
Cela m’a permis de retrouver des informations de routage de Nginx Proxy Manager.
Autrement dit :
un fichier dont le nom avait disparu du système était encore récupérable parce qu’un processus l’utilisait toujours.
Si j’avais redémarré Nginx Proxy Manager avant cette opération, le descripteur aurait été fermé.
La récupération via /proc serait devenue impossible.
Même opération pour Dockge
Dockge utilisait également SQLite, mais avec plusieurs fichiers :
dockge.db
dockge.db-wal
dockge.db-shm
Le processus concerné avait le PID :
2945
et les données étaient encore ouvertes sur plusieurs descripteurs :
27
28
29
J’ai créé le dossier :
sudo mkdir -p \
/opt/homelab-recovery/deleted-open-files/dockge
puis récupéré les trois fichiers :
sudo cp /proc/2945/fd/27 \
/opt/homelab-recovery/deleted-open-files/dockge/dockge.db
sudo cp /proc/2945/fd/28 \
/opt/homelab-recovery/deleted-open-files/dockge/dockge.db-wal
sudo cp /proc/2945/fd/29 \
/opt/homelab-recovery/deleted-open-files/dockge/dockge.db-shm
Pour SQLite en mode WAL, conserver ensemble :
.db
.db-wal
.db-shm
est important.
Ne récupérer que le fichier .db peut conduire à une base ne contenant pas les dernières transactions.
Pourquoi il ne fallait surtout pas redémarrer le VPS
À ce stade, j’ai compris à quel point un réflexe apparemment logique aurait pu être destructeur.
Si j’avais simplement exécuté :
sudo reboot
les conséquences auraient pu être très différentes.
Les conteneurs auraient tenté de redémarrer.
Les bind mounts pointant vers des chemins supprimés auraient pu échouer.
Certaines applications auraient perdu leurs fichiers de configuration.
Les processus NPM et Dockge auraient fermé leurs descripteurs.
Et les fichiers supprimés accessibles dans /proc auraient disparu définitivement.
Même chose avec un :
docker compose down
si j’avais commencé à recréer approximativement les stacks avant de sauvegarder l’état existant.
La première phase de récupération doit donc être conservatrice.
On ne répare rien.
On extrait tout ce que l’on peut.
Reconstruire les fichiers Compose depuis les conteneurs
Une fois :
- les images archivées ;
- les systèmes de fichiers exportés ;
- les bases sauvegardées ;
- les fichiers SQLite récupérés ;
- les volumes identifiés ;
- les informations
docker inspectarchivées ;
j’ai pu commencer la reconstruction.
Pour chaque conteneur, j’ai comparé :
Config.Image
Config.Env
Config.Cmd
Config.Entrypoint
HostConfig.RestartPolicy
HostConfig.PortBindings
Mounts
NetworkSettings.Networks
Config.Healthcheck
Config.Labels
Puis j’ai recréé progressivement les compose.yaml.
Une stack reconstruite reprend typiquement une structure de ce type :
services:
application:
image: image-originale
container_name: nom-original
restart: unless-stopped
volumes:
- volume_existant:/chemin/dans/le/conteneur
networks:
- proxy
networks:
proxy:
external: true
volumes:
volume_existant:
external: true
Le mot important ici est :
external: true
Lorsque le volume contenant les vraies données existe déjà, je ne veux pas que Compose tente d’en créer silencieusement un autre sous un nouveau nom.
Même logique pour le réseau :
networks:
proxy:
external: true
Je reconnecte ainsi les nouveaux conteneurs au réseau déjà utilisé par Nginx Proxy Manager.
Recréer progressivement, jamais tout en même temps
J’ai choisi de remettre l’infrastructure en service stack par stack.
Pour chaque service :
docker compose config
avant :
docker compose up -d
Puis :
docker compose ps
et :
docker logs NOM_DU_CONTENEUR
Cette approche est beaucoup plus sûre que de tenter de reconstruire dix services simultanément.
Elle permet de distinguer :
- une erreur Docker ;
- un problème de réseau ;
- une variable d’environnement manquante ;
- un volume mal nommé ;
- un problème applicatif ;
- une erreur de reverse proxy.
Tester d’abord Docker en interne
Avant de tester Internet, je voulais savoir si Nginx Proxy Manager pouvait réellement joindre les applications.
Le principe est de vérifier les réseaux :
docker inspect NOM_DU_CONTENEUR \
--format '{{json .NetworkSettings.Networks}}' \
| jq
Puis de lancer un test depuis le réseau Docker concerné.
Par exemple :
docker run --rm \
--network proxy \
curlimages/curl \
http://NOM_DU_SERVICE
Cette méthode permet de séparer deux problèmes très différents :
Application inaccessible dans Docker
et :
Application accessible dans Docker
mais inaccessible depuis Internet
Si le premier test échoue, inutile de chercher du côté de Let’s Encrypt.
Reconstruire le reverse proxy et le HTTPS
Nginx Proxy Manager devait retrouver plusieurs informations :
nom de domaine
↓
service Docker
↓
port interne
↓
certificat TLS
Exemple conceptuel :
https://concerto.nvsd.ch
│
▼
Nginx Proxy Manager
│
▼
concerto-infini-web
│
:80
Une fois les conteneurs correctement reliés au réseau proxy, le reverse proxy pouvait à nouveau les atteindre par leur nom Docker.
L’objectif était d’éviter d’exposer inutilement les ports des applications directement sur le VPS.
Après reconstruction des règles et des certificats, j’ai testé les sites depuis l’extérieur avec :
curl -IL --max-time 20 \
https://concerto.nvsd.ch/
Même principe pour :
curl -IL --max-time 20 \
https://fractal.nvsd.ch/
et :
curl -IL --max-time 20 \
https://math-art.nvsd.ch/
Le résultat attendu était revenu :
HTTP/2 200
Voir à nouveau ces réponses après la suppression de l’arborescence complète des stacks était le véritable point final de la récupération.
Les erreurs rencontrées pendant la récupération
L’opération n’a évidemment pas été parfaitement linéaire.
Une première boucle docker inspect a échoué
Certaines écritures dans le répertoire de récupération ont renvoyé :
Permission denied
J’ai recommencé l’opération avec les droits appropriés.
Cela semble trivial, mais pendant un incident il faut éviter que ce genre d’erreur conduise à croire qu’une sauvegarde existe alors qu’elle est vide ou incomplète.
Une archive Docker était tronquée
Le premier export du conteneur API de Concerto a produit :
Unexpected EOF
Il a fallu refaire l’export.
Depuis cet incident, je considère la vérification comme faisant partie intégrante de la sauvegarde.
Redis refusait ses snapshots habituels
L’erreur MISCONF aurait pu m’entraîner dans une réparation Redis.
Je l’ai volontairement évitée pendant la phase critique.
J’ai extrait les données d’abord.
La réparation vient ensuite.
Nginx Proxy Manager affichait temporairement sa page par défaut
Pendant une partie de la reconstruction, le proxy répondait, mais le bon hôte n’était pas encore associé.
C’est un cas intéressant :
DNS OK
HTTPS / serveur OK
Nginx OK
mais routage applicatif incorrect
Une page qui répond ne signifie donc pas nécessairement que l’infrastructure est réparée.
Ce que cet incident m’a appris sur Docker
Cette récupération a complètement changé ma façon de considérer une infrastructure Docker.
Avant, j’avais tendance à voir un projet principalement sous cette forme :
compose.yaml
+
conteneurs
Aujourd’hui je le vois plutôt ainsi :
Infrastructure reproductible
│
├── code source
├── Dockerfile
├── compose.yaml
├── variables de configuration
├── secrets
├── images ou capacité à les reconstruire
├── volumes persistants
├── dumps des bases
├── configuration réseau
├── reverse proxy
└── procédure de restauration
Un seul de ces éléments ne suffit pas.
Une image Docker n’est pas une sauvegarde
Le fait d’avoir récupéré mes images a été extrêmement utile.
Mais une image seule ne contient pas nécessairement :
- la base PostgreSQL ;
- la base MariaDB ;
- les médias WordPress ;
- Redis ;
- les données NPM ;
- les données Dockge ;
- les secrets ;
- le fichier Compose ;
- les certificats.
De la même façon, un volume sans fichier Compose peut être difficile à réutiliser proprement plusieurs mois plus tard.
La sauvegarde doit donc préserver la capacité à reconstruire le système, pas seulement ses données.
Un fichier Compose doit être considéré comme du code source
C’est probablement une de mes principales conclusions.
Mes fichiers Compose étaient dans :
/opt/homelab/stacks
mais cela ne suffit pas.
Ils devraient également exister dans une sauvegarde indépendante, voire dans un dépôt Git privé.
Le VPS doit pouvoir disparaître complètement sans faire disparaître avec lui la définition de l’infrastructure.
Autrement dit :
le serveur de production ne doit jamais être l’unique endroit où existe la recette permettant de reconstruire le serveur de production.
Ce que je veux désormais sauvegarder
Mon système de sauvegarde doit au minimum contenir quatre couches.
1. Infrastructure
compose.yaml
Dockerfile
.env.example
configurations
scripts
documentation
2. Données persistantes
volumes WordPress
fichiers utilisateurs
assets applicatifs
configurations persistantes
3. Bases de données
Avec des dumps applicatifs réguliers :
PostgreSQL → pg_dump / pg_dumpall
MariaDB → mariadb-dump
Redis → RDB
SQLite → copie cohérente / backup SQLite
4. Éléments permettant une récupération d’urgence
Lorsque certaines images sont construites localement :
docker save
peut constituer une deuxième ligne de défense.
Mais surtout : sauvegarder ailleurs que sur le VPS
Créer :
/opt/homelab/backup
est utile.
Mais si le VPS disparaît complètement, cette sauvegarde disparaît avec lui.
Une véritable stratégie doit donc posséder au moins une copie hors machine :
VPS
│
├── données actives
│
└── sauvegarde locale
│
▼
copie externe
Le stockage externe peut changer.
Le principe, lui, ne change pas.
Une sauvegarde reproductible doit être testée
Cet incident m’a également rappelé une évidence que l’on oublie facilement :
la sauvegarde n’est que la moitié du problème.
L’autre moitié est :
restore
Je veux donc pouvoir prendre un serveur Docker vierge et exécuter une procédure documentée ressemblant à :
1. installer Docker
2. restaurer les fichiers Compose
3. recréer les réseaux
4. recréer les volumes
5. restaurer PostgreSQL
6. restaurer MariaDB
7. restaurer Redis
8. restaurer WordPress
9. restaurer Nginx Proxy Manager
10. lancer les stacks
11. vérifier le réseau interne
12. vérifier HTTPS
Si cette procédure ne peut pas être exécutée, la sauvegarde reste incomplète.
Les commandes à ne surtout pas lancer trop vite après un incident
Si des conteneurs fonctionnent encore après la perte de leurs fichiers de configuration, je commencerais aujourd’hui par considérer comme dangereuses les commandes suivantes :
docker system prune -a
docker volume prune
docker compose down -v
ainsi qu’un simple :
reboot
tant que je n’ai pas compris exactement ce qui est encore récupérable.
Même un redémarrage de conteneur peut fermer le dernier descripteur permettant de récupérer un fichier supprimé.
Ce que je ferais aujourd’hui dans les cinq premières minutes
Si je devais revivre exactement le même incident :
1. Stopper toute manipulation.
2. Ne rien redémarrer.
3. Lancer docker ps.
4. Sauvegarder docker inspect.
5. Inventorier volumes et réseaux.
6. Chercher les fichiers supprimés encore ouverts avec lsof +L1.
7. Faire les dumps PostgreSQL, MariaDB et Redis.
8. Sauvegarder les images locales.
9. Exporter les conteneurs critiques.
10. Copier l’ensemble du dossier de récupération hors du VPS.
11. Seulement ensuite commencer la reconstruction.
L’ordre est important.
Chaque minute pendant laquelle les processus restent vivants peut maintenir accessibles des informations qu’un redémarrage ferait disparaître.
Conclusion : Docker m’a sauvé, mais il ne remplace pas une stratégie de sauvegarde
Cette erreur aurait pu conduire à la perte de plusieurs semaines de travail.
Elle s’est finalement transformée en une expérience extrêmement instructive.
Le fait que les conteneurs fonctionnent encore m’a permis d’utiliser Docker lui-même comme source de reconstruction :
docker inspect
docker export
docker save
docker volume inspect
docker exec
docker cp
Linux m’a fourni une seconde porte de sortie avec :
/proc/<PID>/fd
PostgreSQL, MariaDB et Redis ont pu être traités directement depuis leurs conteneurs encore actifs.
Les volumes Docker avaient survécu à la suppression des fichiers Compose.
Les configurations ont pu être reconstituées.
Les réseaux ont été recréés.
Nginx Proxy Manager a de nouveau routé les applications.
Et finalement :
concerto.nvsd.ch
fractal.nvsd.ch
math-art.nvsd.ch
répondaient de nouveau en HTTPS avec :
HTTP/2 200
Mais je ne considère pas cette récupération comme la preuve que les sauvegardes sont facultatives.
C’est exactement l’inverse.
J’ai réussi parce que plusieurs circonstances étaient favorables :
- Docker tournait toujours ;
- les volumes existaient encore ;
- les images n’avaient pas été supprimées ;
- les bases fonctionnaient ;
- certains fichiers SQLite étaient encore ouverts ;
- le VPS n’avait pas été redémarré.
Un simple :
docker system prune
un redémarrage mal placé ou une panne du VPS aurait pu raconter une toute autre histoire.
La leçon principale de cet incident n’est donc pas :
« Docker conserve les conteneurs, donc je peux récupérer mon serveur. »
Elle est plutôt :
Une infrastructure sérieuse doit pouvoir être détruite entièrement puis reconstruite à partir de sauvegardes documentées, vérifiées et stockées ailleurs.
Mon Homelab continuera à évoluer.
Mais désormais, chaque nouveau service devra répondre à une question supplémentaire :
si je supprime ce serveur demain, comment est-ce que je le reconstruis ?
C’est probablement la question la plus importante que cet incident m’ait appris à poser.