Aller au contenu
Non classé17 août 2026

J’ai supprimé mes stacks : récupération d’un VPS Docker sans perdre les services

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 export ne 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 inspect archivé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.