{"id":106,"date":"2026-08-17T13:49:29","date_gmt":"2026-08-17T11:49:29","guid":{"rendered":"https:\/\/homelab.nvsd.ch\/?p=106"},"modified":"2026-08-17T13:52:01","modified_gmt":"2026-08-17T11:52:01","slug":"recuperation-vps-docker-suppression-stacks","status":"publish","type":"post","link":"https:\/\/homelab.nvsd.ch\/?p=106","title":{"rendered":"J\u2019ai supprim\u00e9 mes stacks : r\u00e9cup\u00e9ration d\u2019un VPS Docker sans perdre les services"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Comment une erreur de manipulation m\u2019a oblig\u00e9 \u00e0 comprendre ce que Docker conserve r\u00e9ellement \u2014 et \u00e0 reconstruire mon infrastructure \u00e0 partir des conteneurs encore vivants<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Le 16 ao\u00fbt 2026, j\u2019ai commis le genre d\u2019erreur que l\u2019on pense toujours pouvoir \u00e9viter jusqu\u2019au jour o\u00f9 elle arrive r\u00e9ellement.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sur mon VPS, le r\u00e9pertoire :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/opt\/homelab\/stacks\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">a \u00e9t\u00e9 supprim\u00e9.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ce n\u2019\u00e9tait pas un simple dossier de travail. C\u2019\u00e9tait le c\u0153ur de mon organisation Docker : fichiers <code>compose.yaml<\/code>, configurations, sources de plusieurs applications, donn\u00e9es li\u00e9es \u00e0 certains services et d\u00e9finitions permettant de reconstruire mon infrastructure.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u00c0 ce moment-l\u00e0, mon VPS faisait notamment tourner :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>mon site WordPress <strong>NVSD Homelab<\/strong> ;<\/li>\n\n\n\n<li><strong>Concerto \u00e0 l\u2019Infini<\/strong> ;<\/li>\n\n\n\n<li><strong>Fractal Studio<\/strong> ;<\/li>\n\n\n\n<li><strong>Math Art<\/strong> ;<\/li>\n\n\n\n<li>plusieurs sites statiques ;<\/li>\n\n\n\n<li><strong>Nginx Proxy Manager<\/strong> ;<\/li>\n\n\n\n<li><strong>Dockge<\/strong> ;<\/li>\n\n\n\n<li>PostgreSQL ;<\/li>\n\n\n\n<li>MariaDB ;<\/li>\n\n\n\n<li>Redis.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">La premi\u00e8re r\u00e9action aurait pu \u00eatre de red\u00e9marrer les services, de relancer Docker ou de tenter imm\u00e9diatement de recr\u00e9er les stacks.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cela aurait probablement \u00e9t\u00e9 la pire chose \u00e0 faire.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Car malgr\u00e9 la disparition de <code>\/opt\/homelab\/stacks<\/code>, une chose essentielle \u00e9tait encore vraie :<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>les conteneurs fonctionnaient toujours.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Et ces conteneurs encore vivants allaient devenir ma principale source de r\u00e9cup\u00e9ration.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Le paradoxe : les fichiers Compose avaient disparu, mais Docker continuait de fonctionner<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Lorsque j\u2019ai constat\u00e9 la suppression du r\u00e9pertoire, j\u2019ai imm\u00e9diatement v\u00e9rifi\u00e9 Docker :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker ps\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u00c0 ma grande surprise, les services \u00e9taient toujours l\u00e0.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Des conteneurs comme :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>concerto-infini-api-1\nconcerto-infini-web-1\n\nfractal-studio-web\nfractal-studio-api\nfractal-studio-worker\nfractal-studio-db\nfractal-studio-redis\n\nmath-art\n\nnvsd-homelab-wordpress\nnvsd-homelab-db\n\nnpm-npm-1\ndockge\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">continuaient \u00e0 fonctionner.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cela peut sembler \u00e9trange au premier abord, mais c\u2019est une distinction fondamentale dans Docker.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Docker Compose n\u2019est pas Docker<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Un fichier <code>compose.yaml<\/code> est essentiellement une <strong>description de l\u2019\u00e9tat souhait\u00e9 de l\u2019infrastructure<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Il dit \u00e0 Docker :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>quelle image utiliser ;<\/li>\n\n\n\n<li>quels volumes monter ;<\/li>\n\n\n\n<li>quelles variables d\u2019environnement fournir ;<\/li>\n\n\n\n<li>quels r\u00e9seaux utiliser ;<\/li>\n\n\n\n<li>quels ports exposer ;<\/li>\n\n\n\n<li>quelle commande lancer ;<\/li>\n\n\n\n<li>quelle politique de red\u00e9marrage appliquer.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Mais une fois le conteneur cr\u00e9\u00e9, Docker conserve sa propre d\u00e9finition dans son espace interne.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Les images sont \u00e9galement stock\u00e9es s\u00e9par\u00e9ment, typiquement dans :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/var\/lib\/docker\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">tout comme les volumes Docker.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Supprimer :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/opt\/homelab\/stacks\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">ne revient donc pas automatiquement \u00e0 ex\u00e9cuter :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker compose down\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">et encore moins :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker system prune\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Les processus d\u00e9j\u00e0 lanc\u00e9s continuent de tourner.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dans mon cas, cette diff\u00e9rence a fait toute la diff\u00e9rence.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Premi\u00e8re r\u00e8gle : ne plus rien arr\u00eater<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">\u00c0 partir de ce moment-l\u00e0, mon objectif a chang\u00e9.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Je ne cherchais plus imm\u00e9diatement \u00e0 \u00ab r\u00e9parer \u00bb le VPS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Je cherchais d\u2019abord \u00e0 <strong>pr\u00e9server tout ce qui existait encore<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La r\u00e8gle est devenue :<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Ne pas red\u00e9marrer. Ne pas recr\u00e9er. Ne pas nettoyer. Sauvegarder d\u2019abord.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">J\u2019ai cr\u00e9\u00e9 un espace de r\u00e9cup\u00e9ration s\u00e9par\u00e9 :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mkdir -p \/opt\/homelab-recovery\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Puis plusieurs sous-r\u00e9pertoires :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mkdir -p \\\n  \/opt\/homelab-recovery\/inspect \\\n  \/opt\/homelab-recovery\/filesystems \\\n  \/opt\/homelab-recovery\/images \\\n  \/opt\/homelab-recovery\/database-dumps \\\n  \/opt\/homelab-recovery\/deleted-open-files\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">L\u2019id\u00e9e \u00e9tait simple :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Conteneurs vivants\n      \u2502\n      \u251c\u2500\u2500 configuration Docker\n      \u251c\u2500\u2500 images\n      \u251c\u2500\u2500 syst\u00e8me de fichiers\n      \u251c\u2500\u2500 volumes\n      \u251c\u2500\u2500 bases de donn\u00e9es\n      \u2514\u2500\u2500 fichiers supprim\u00e9s encore ouverts\n              \u2502\n              \u25bc\n      \/opt\/homelab-recovery\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Avant toute reconstruction, je voulais cr\u00e9er une photographie aussi compl\u00e8te que possible de l\u2019\u00e9tat restant.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Faire l\u2019inventaire avant de toucher quoi que ce soit<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">La premi\u00e8re \u00e9tape a \u00e9t\u00e9 de documenter les conteneurs encore pr\u00e9sents.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker ps\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Puis les volumes :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker volume ls\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Et les r\u00e9seaux :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker network ls\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dans mon infrastructure, le r\u00e9seau Docker <code>proxy<\/code> \u00e9tait particuli\u00e8rement important.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">C\u2019est lui qui permettait \u00e0 Nginx Proxy Manager de joindre plusieurs services sans qu\u2019ils soient n\u00e9cessairement expos\u00e9s directement sur l\u2019interface publique du VPS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">J\u2019ai ensuite utilis\u00e9 <code>docker inspect<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Par exemple :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo docker inspect concerto-infini-api-1 \\\n  &gt; \/opt\/homelab-recovery\/inspect\/concerto-infini-api-1.json\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">L\u2019op\u00e9ration peut \u00eatre r\u00e9p\u00e9t\u00e9e sur tous les conteneurs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>docker inspect<\/code> est extr\u00eamement pr\u00e9cieux dans ce type de situation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">M\u00eame si le fichier Compose original a disparu, il permet de retrouver :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>l\u2019image ;<\/li>\n\n\n\n<li>les variables d\u2019environnement ;<\/li>\n\n\n\n<li>les volumes ;<\/li>\n\n\n\n<li>les bind mounts ;<\/li>\n\n\n\n<li>les r\u00e9seaux ;<\/li>\n\n\n\n<li>les alias r\u00e9seau ;<\/li>\n\n\n\n<li>les ports ;<\/li>\n\n\n\n<li>le <code>restart policy<\/code> ;<\/li>\n\n\n\n<li>les commandes ;<\/li>\n\n\n\n<li>les healthchecks ;<\/li>\n\n\n\n<li>les labels Docker Compose.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Par exemple :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker inspect fractal-studio-api \\\n  --format '{{json .Mounts}}' | jq\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">ou :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker inspect fractal-studio-api \\\n  --format '{{json .NetworkSettings.Networks}}' | jq\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Les labels sont eux aussi particuli\u00e8rement utiles :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker inspect fractal-studio-api \\\n  --format '{{json .Config.Labels}}' | jq\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Docker Compose y inscrit notamment des informations telles que le nom du projet et le nom logique du service.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En pratique, <strong>un conteneur encore vivant contient une grande partie de l\u2019information n\u00e9cessaire pour reconstruire son Compose<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Sauvegarder les images Docker<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Certaines de mes applications utilisaient des images construites localement.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dans une situation normale, une image Docker devrait pouvoir \u00eatre reconstruite \u00e0 partir :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>du <code>Dockerfile<\/code> ;<\/li>\n\n\n\n<li>du code source ;<\/li>\n\n\n\n<li>des d\u00e9pendances ;<\/li>\n\n\n\n<li>du fichier Compose.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Mais une partie de ces \u00e9l\u00e9ments venait justement de dispara\u00eetre.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Il fallait donc consid\u00e9rer les images encore pr\u00e9sentes sur le VPS comme des objets pr\u00e9cieux.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">J\u2019ai cr\u00e9\u00e9 une archive des images utilis\u00e9es par les conteneurs actifs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le r\u00e9sultat a \u00e9t\u00e9 enregistr\u00e9 dans :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/opt\/homelab-recovery\/images\/active-container-images.tar\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">L\u2019archive faisait environ <strong>1,9 Go<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La logique reproductible est la suivante :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker ps -q \\\n  | xargs docker inspect \\\n      --format '{{.Config.Image}}' \\\n  | sort -u\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Cette commande permet d\u2019obtenir la liste des images utilis\u00e9es.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Elles peuvent ensuite \u00eatre archiv\u00e9es avec :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo docker save \\\n  -o \/opt\/homelab-recovery\/images\/active-container-images.tar \\\n  IMAGE_1 IMAGE_2 IMAGE_3\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Puis l\u2019archive peut \u00eatre contr\u00f4l\u00e9e :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>tar -tf \\\n  \/opt\/homelab-recovery\/images\/active-container-images.tar \\\n  &gt; \/dev\/null\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Une image sauvegard\u00e9e pourra ensuite \u00eatre restaur\u00e9e avec :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker load \\\n  -i active-container-images.tar\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ce n\u2019est pas une alternative \u00e0 la conservation du code source.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mais en situation de r\u00e9cup\u00e9ration, c\u2019est une assurance suppl\u00e9mentaire extr\u00eamement utile.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Exporter les syst\u00e8mes de fichiers des conteneurs<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Deuxi\u00e8me couche de s\u00e9curit\u00e9 : <code>docker export<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cette commande transforme le syst\u00e8me de fichiers visible \u00e0 l\u2019int\u00e9rieur d\u2019un conteneur en archive TAR.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Exemple :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo docker export \\\n  concerto-infini-api-1 \\\n  -o \/opt\/homelab-recovery\/filesystems\/concerto-infini-api-1.tar\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">J\u2019ai r\u00e9alis\u00e9 cette op\u00e9ration pour pratiquement tous les conteneurs importants.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le r\u00e9pertoire de r\u00e9cup\u00e9ration a ainsi contenu notamment :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>math-art.tar\nconcerto-infini-api-1.tar\nconcerto-infini-web-1.tar\n\nfractal-studio-api.tar\nfractal-studio-web.tar\nfractal-studio-worker.tar\nfractal-studio-db.tar\nfractal-studio-redis.tar\n\nnpm-npm-1.tar\ndockge.tar\n\nnvsd-homelab-wordpress.tar\nnvsd-homelab-db.tar\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Il faut cependant comprendre une limitation importante :<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><code>docker export<\/code> ne sauvegarde pas le contenu des volumes Docker mont\u00e9s dans le conteneur.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Il sauvegarde le syst\u00e8me de fichiers du conteneur.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">C\u2019est donc une couche suppl\u00e9mentaire de r\u00e9cup\u00e9ration, mais <strong>pas une sauvegarde compl\u00e8te d\u2019une stack<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Une erreur int\u00e9ressante : <code>Unexpected EOF<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Lors du premier export de <code>concerto-infini-api-1<\/code>, la v\u00e9rification de l\u2019archive a retourn\u00e9 une erreur :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Unexpected EOF\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">L\u2019archive \u00e9tait incompl\u00e8te.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Je l\u2019ai supprim\u00e9e puis recr\u00e9\u00e9e.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La seconde exportation \u00e9tait correcte.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cette petite erreur a renforc\u00e9 une r\u00e8gle importante :<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Une sauvegarde qui n\u2019a jamais \u00e9t\u00e9 v\u00e9rifi\u00e9e n\u2019est pas encore une sauvegarde.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Apr\u00e8s chaque export important :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>tar -tf fichier.tar &gt; \/dev\/null\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">permet au minimum de v\u00e9rifier que l\u2019archive TAR peut \u00eatre parcourue.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Les volumes Docker : ce qui m\u2019a sauv\u00e9 pour les donn\u00e9es persistantes<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Les volumes Docker nomm\u00e9s sont ind\u00e9pendants du r\u00e9pertoire contenant le fichier Compose.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Par exemple, supprimer :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/opt\/homelab\/stacks\/nvsd-homelab-wordpress\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">ne supprime pas automatiquement :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>nvsd_homelab_wordpress_data\nnvsd_homelab_db_data\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ils restent g\u00e9r\u00e9s par Docker.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">C\u2019est une des raisons pour lesquelles les donn\u00e9es WordPress et MariaDB \u00e9taient toujours pr\u00e9sentes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pour inspecter un volume :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker volume inspect nvsd_homelab_wordpress_data\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">La m\u00eame logique s\u2019applique aux volumes utilis\u00e9s par PostgreSQL ou d\u2019autres services.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pour les fichiers ordinaires, un volume peut \u00e9galement \u00eatre archiv\u00e9 ind\u00e9pendamment du conteneur :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker run --rm \\\n  -v NOM_DU_VOLUME:\/source:ro \\\n  -v \/opt\/homelab-recovery\/volumes:\/backup \\\n  alpine \\\n  sh -c 'tar -czf \/backup\/volume.tar.gz -C \/source .'\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Pour une base de donn\u00e9es active, en revanche, je pr\u00e9f\u00e8re un <strong>dump logique<\/strong> \u00e0 une simple copie brute du volume.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Sauvegarder PostgreSQL tant que Fractal Studio fonctionnait encore<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Fractal Studio utilisait PostgreSQL.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le conteneur :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fractal-studio-db\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u00e9tait encore fonctionnel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">C\u2019est la situation id\u00e9ale pour produire une sauvegarde logique.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Une forme pratique, lorsque <code>POSTGRES_USER<\/code> est d\u00e9fini dans le conteneur, est :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker exec fractal-studio-db \\\n  sh -c 'pg_dumpall -U \"$POSTGRES_USER\"' \\\n  &gt; \/opt\/homelab-recovery\/database-dumps\/fractal-studio-postgresql.sql\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">L\u2019int\u00e9r\u00eat du dump logique est qu\u2019il ne d\u00e9pend pas de la disposition interne du volume PostgreSQL.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pour restaurer plus tard :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cat fractal-studio-postgresql.sql \\\n  | docker exec -i fractal-studio-db \\\n      sh -c 'psql -U \"$POSTGRES_USER\"'\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">La r\u00e8gle que je retiens est claire :<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>pour une base active, sauvegarder le contenu avec les outils de la base avant de manipuler son volume.<\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">M\u00eame principe pour MariaDB et WordPress<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Le site Homelab utilisait :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>nvsd-homelab-wordpress\nnvsd-homelab-db\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">avec MariaDB.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">L\u00e0 encore, le fait que le conteneur fonctionne encore permet de produire un dump coh\u00e9rent.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Par exemple :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker exec nvsd-homelab-db \\\n  sh -c 'mariadb-dump \\\n    -uroot \\\n    -p\"$MARIADB_ROOT_PASSWORD\" \\\n    --all-databases \\\n    --single-transaction' \\\n  &gt; \/opt\/homelab-recovery\/database-dumps\/nvsd-homelab-mariadb.sql\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Le fichier obtenu peut \u00eatre v\u00e9rifi\u00e9 simplement :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ls -lh \\\n  \/opt\/homelab-recovery\/database-dumps\/nvsd-homelab-mariadb.sql\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Puis restaur\u00e9 ult\u00e9rieurement avec le client MariaDB.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">WordPress poss\u00e8de en r\u00e9alit\u00e9 deux cat\u00e9gories de donn\u00e9es \u00e0 prot\u00e9ger :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Base MariaDB\n+\nwp-content, m\u00e9dias, th\u00e8mes et extensions\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Le dump SQL ne suffit donc pas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Il faut \u00e9galement conserver le volume WordPress.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Redis : quand la sauvegarde RDB refuse de fonctionner normalement<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Fractal Studio utilisait \u00e9galement Redis :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fractal-studio-redis\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">C\u2019est \u00e0 ce moment qu\u2019une autre difficult\u00e9 est apparue.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Redis signalait une erreur de type :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>MISCONF\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">li\u00e9e \u00e0 l\u2019\u00e9chec d\u2019une sauvegarde RDB automatique et \u00e0 la protection :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>stop-writes-on-bgsave-error\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Plut\u00f4t que de commencer \u00e0 modifier la configuration d\u2019un Redis en pleine r\u00e9cup\u00e9ration, j\u2019ai utilis\u00e9 directement son m\u00e9canisme d\u2019export RDB :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker exec fractal-studio-redis \\\n  redis-cli --rdb \/tmp\/fractal-studio-redis.rdb\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Puis j\u2019ai r\u00e9cup\u00e9r\u00e9 le fichier depuis le conteneur :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker cp \\\n  fractal-studio-redis:\/tmp\/fractal-studio-redis.rdb \\\n  \/opt\/homelab-recovery\/database-dumps\/fractal-studio-redis.rdb\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Le fichier obtenu faisait seulement quelques kilo-octets, ce qui \u00e9tait coh\u00e9rent avec la faible quantit\u00e9 de donn\u00e9es pr\u00e9sente \u00e0 ce moment-l\u00e0.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Encore une fois, l\u2019objectif n\u2019\u00e9tait pas de \u00ab r\u00e9parer Redis \u00bb imm\u00e9diatement.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">L\u2019objectif \u00e9tait :<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>sortir les donn\u00e9es du syst\u00e8me vivant avant d\u2019entreprendre quoi que ce soit d\u2019autre.<\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Le moment le plus int\u00e9ressant : r\u00e9cup\u00e9rer des fichiers d\u00e9j\u00e0 supprim\u00e9s gr\u00e2ce \u00e0 <code>\/proc<\/code><\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Nginx Proxy Manager et Dockge posaient un probl\u00e8me diff\u00e9rent.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Leurs fichiers SQLite avaient \u00e9t\u00e9 situ\u00e9s dans l\u2019arborescence supprim\u00e9e.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sur le disque, les chemins n\u2019existaient donc plus.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mais leurs processus fonctionnaient encore.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sous Linux, supprimer un fichier ne signifie pas n\u00e9cessairement que ses donn\u00e9es disparaissent imm\u00e9diatement.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Si un processus poss\u00e8de encore un descripteur de fichier ouvert sur ce fichier, l\u2019inode peut rester accessible jusqu\u2019\u00e0 la fermeture du dernier descripteur.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Et Linux expose ces descripteurs dans :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/proc\/&lt;PID&gt;\/fd\/\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">C\u2019est probablement la partie la plus spectaculaire de cette r\u00e9cup\u00e9ration.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Rechercher les fichiers supprim\u00e9s mais encore ouverts<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Une commande tr\u00e8s utile est :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo lsof +L1\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">On peut filtrer :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo lsof +L1 \\\n  | grep '\/opt\/homelab\/stacks'\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Cela permet d\u2019identifier les processus utilisant encore des fichiers qui ont \u00e9t\u00e9 supprim\u00e9s du syst\u00e8me de fichiers.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">R\u00e9cup\u00e9rer la base SQLite de Nginx Proxy Manager<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Pour Nginx Proxy Manager, le processus concern\u00e9 avait le PID :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>8067\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">et le fichier SQLite \u00e9tait encore accessible par le descripteur :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>18\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">J\u2019ai pu examiner le lien :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo readlink \/proc\/8067\/fd\/18\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">puis copier directement les donn\u00e9es :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mkdir -p \\\n  \/opt\/homelab-recovery\/deleted-open-files\/npm\n\nsudo cp \\\n  \/proc\/8067\/fd\/18 \\\n  \/opt\/homelab-recovery\/deleted-open-files\/npm\/database.sqlite\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">J\u2019avais de nouveau une base SQLite utilisable :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>database.sqlite\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">d\u2019environ <strong>112 Ko<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Je pouvais ensuite l\u2019interroger :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sqlite3 database.sqlite\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Puis :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.tables\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">et examiner la table importante :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT\n    id,\n    domain_names,\n    forward_host,\n    forward_port,\n    certificate_id,\n    enabled\nFROM proxy_host;\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Cela m\u2019a permis de retrouver des informations de routage de Nginx Proxy Manager.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Autrement dit :<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>un fichier dont le nom avait disparu du syst\u00e8me \u00e9tait encore r\u00e9cup\u00e9rable parce qu\u2019un processus l\u2019utilisait toujours.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Si j\u2019avais red\u00e9marr\u00e9 Nginx Proxy Manager avant cette op\u00e9ration, le descripteur aurait \u00e9t\u00e9 ferm\u00e9.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La r\u00e9cup\u00e9ration via <code>\/proc<\/code> serait devenue impossible.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">M\u00eame op\u00e9ration pour Dockge<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Dockge utilisait \u00e9galement SQLite, mais avec plusieurs fichiers :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>dockge.db\ndockge.db-wal\ndockge.db-shm\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Le processus concern\u00e9 avait le PID :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>2945\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">et les donn\u00e9es \u00e9taient encore ouvertes sur plusieurs descripteurs :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>27\n28\n29\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">J\u2019ai cr\u00e9\u00e9 le dossier :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mkdir -p \\\n  \/opt\/homelab-recovery\/deleted-open-files\/dockge\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">puis r\u00e9cup\u00e9r\u00e9 les trois fichiers :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo cp \/proc\/2945\/fd\/27 \\\n  \/opt\/homelab-recovery\/deleted-open-files\/dockge\/dockge.db\n\nsudo cp \/proc\/2945\/fd\/28 \\\n  \/opt\/homelab-recovery\/deleted-open-files\/dockge\/dockge.db-wal\n\nsudo cp \/proc\/2945\/fd\/29 \\\n  \/opt\/homelab-recovery\/deleted-open-files\/dockge\/dockge.db-shm\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Pour SQLite en mode WAL, conserver ensemble :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.db\n.db-wal\n.db-shm\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">est important.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ne r\u00e9cup\u00e9rer que le fichier <code>.db<\/code> peut conduire \u00e0 une base ne contenant pas les derni\u00e8res transactions.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Pourquoi il ne fallait surtout pas red\u00e9marrer le VPS<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">\u00c0 ce stade, j\u2019ai compris \u00e0 quel point un r\u00e9flexe apparemment logique aurait pu \u00eatre destructeur.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Si j\u2019avais simplement ex\u00e9cut\u00e9 :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo reboot\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">les cons\u00e9quences auraient pu \u00eatre tr\u00e8s diff\u00e9rentes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Les conteneurs auraient tent\u00e9 de red\u00e9marrer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Les bind mounts pointant vers des chemins supprim\u00e9s auraient pu \u00e9chouer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Certaines applications auraient perdu leurs fichiers de configuration.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Les processus NPM et Dockge auraient ferm\u00e9 leurs descripteurs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Et les fichiers supprim\u00e9s accessibles dans <code>\/proc<\/code> auraient disparu d\u00e9finitivement.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">M\u00eame chose avec un :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker compose down\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">si j\u2019avais commenc\u00e9 \u00e0 recr\u00e9er approximativement les stacks avant de sauvegarder l\u2019\u00e9tat existant.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La premi\u00e8re phase de r\u00e9cup\u00e9ration doit donc \u00eatre <strong>conservatrice<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On ne r\u00e9pare rien.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On extrait tout ce que l\u2019on peut.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Reconstruire les fichiers Compose depuis les conteneurs<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Une fois :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>les images archiv\u00e9es ;<\/li>\n\n\n\n<li>les syst\u00e8mes de fichiers export\u00e9s ;<\/li>\n\n\n\n<li>les bases sauvegard\u00e9es ;<\/li>\n\n\n\n<li>les fichiers SQLite r\u00e9cup\u00e9r\u00e9s ;<\/li>\n\n\n\n<li>les volumes identifi\u00e9s ;<\/li>\n\n\n\n<li>les informations <code>docker inspect<\/code> archiv\u00e9es ;<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">j\u2019ai pu commencer la reconstruction.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pour chaque conteneur, j\u2019ai compar\u00e9 :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Config.Image\nConfig.Env\nConfig.Cmd\nConfig.Entrypoint\nHostConfig.RestartPolicy\nHostConfig.PortBindings\nMounts\nNetworkSettings.Networks\nConfig.Healthcheck\nConfig.Labels\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Puis j\u2019ai recr\u00e9\u00e9 progressivement les <code>compose.yaml<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Une stack reconstruite reprend typiquement une structure de ce type :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>services:\n\n  application:\n    image: image-originale\n    container_name: nom-original\n    restart: unless-stopped\n\n    volumes:\n      - volume_existant:\/chemin\/dans\/le\/conteneur\n\n    networks:\n      - proxy\n\nnetworks:\n  proxy:\n    external: true\n\nvolumes:\n  volume_existant:\n    external: true\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Le mot important ici est :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>external: true\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Lorsque le volume contenant les vraies donn\u00e9es existe d\u00e9j\u00e0, je ne veux pas que Compose tente d\u2019en cr\u00e9er silencieusement un autre sous un nouveau nom.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">M\u00eame logique pour le r\u00e9seau :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>networks:\n  proxy:\n    external: true\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Je reconnecte ainsi les nouveaux conteneurs au r\u00e9seau d\u00e9j\u00e0 utilis\u00e9 par Nginx Proxy Manager.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Recr\u00e9er progressivement, jamais tout en m\u00eame temps<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">J\u2019ai choisi de remettre l\u2019infrastructure en service stack par stack.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pour chaque service :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker compose config\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">avant :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker compose up -d\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Puis :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker compose ps\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">et :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker logs NOM_DU_CONTENEUR\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Cette approche est beaucoup plus s\u00fbre que de tenter de reconstruire dix services simultan\u00e9ment.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Elle permet de distinguer :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>une erreur Docker ;<\/li>\n\n\n\n<li>un probl\u00e8me de r\u00e9seau ;<\/li>\n\n\n\n<li>une variable d\u2019environnement manquante ;<\/li>\n\n\n\n<li>un volume mal nomm\u00e9 ;<\/li>\n\n\n\n<li>un probl\u00e8me applicatif ;<\/li>\n\n\n\n<li>une erreur de reverse proxy.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Tester d\u2019abord Docker en interne<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Avant de tester Internet, je voulais savoir si Nginx Proxy Manager pouvait r\u00e9ellement joindre les applications.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le principe est de v\u00e9rifier les r\u00e9seaux :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker inspect NOM_DU_CONTENEUR \\\n  --format '{{json .NetworkSettings.Networks}}' \\\n  | jq\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Puis de lancer un test depuis le r\u00e9seau Docker concern\u00e9.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Par exemple :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker run --rm \\\n  --network proxy \\\n  curlimages\/curl \\\n  http:&#47;&#47;NOM_DU_SERVICE\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Cette m\u00e9thode permet de s\u00e9parer deux probl\u00e8mes tr\u00e8s diff\u00e9rents :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Application inaccessible dans Docker\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">et :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Application accessible dans Docker\nmais inaccessible depuis Internet\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Si le premier test \u00e9choue, inutile de chercher du c\u00f4t\u00e9 de Let&rsquo;s Encrypt.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Reconstruire le reverse proxy et le HTTPS<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Nginx Proxy Manager devait retrouver plusieurs informations :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>nom de domaine\n        \u2193\nservice Docker\n        \u2193\nport interne\n        \u2193\ncertificat TLS\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Exemple conceptuel :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>https:&#47;&#47;concerto.nvsd.ch\n          \u2502\n          \u25bc\nNginx Proxy Manager\n          \u2502\n          \u25bc\nconcerto-infini-web\n          \u2502\n         :80\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Une fois les conteneurs correctement reli\u00e9s au r\u00e9seau <code>proxy<\/code>, le reverse proxy pouvait \u00e0 nouveau les atteindre par leur nom Docker.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">L\u2019objectif \u00e9tait d\u2019\u00e9viter d\u2019exposer inutilement les ports des applications directement sur le VPS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Apr\u00e8s reconstruction des r\u00e8gles et des certificats, j\u2019ai test\u00e9 les sites depuis l\u2019ext\u00e9rieur avec :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -IL --max-time 20 \\\n  https:&#47;&#47;concerto.nvsd.ch\/\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">M\u00eame principe pour :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -IL --max-time 20 \\\n  https:&#47;&#47;fractal.nvsd.ch\/\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">et :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -IL --max-time 20 \\\n  https:&#47;&#47;math-art.nvsd.ch\/\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Le r\u00e9sultat attendu \u00e9tait revenu :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>HTTP\/2 200\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Voir \u00e0 nouveau ces r\u00e9ponses apr\u00e8s la suppression de l\u2019arborescence compl\u00e8te des stacks \u00e9tait le v\u00e9ritable point final de la r\u00e9cup\u00e9ration.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Les erreurs rencontr\u00e9es pendant la r\u00e9cup\u00e9ration<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">L\u2019op\u00e9ration n\u2019a \u00e9videmment pas \u00e9t\u00e9 parfaitement lin\u00e9aire.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Une premi\u00e8re boucle <code>docker inspect<\/code> a \u00e9chou\u00e9<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Certaines \u00e9critures dans le r\u00e9pertoire de r\u00e9cup\u00e9ration ont renvoy\u00e9 :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Permission denied\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">J\u2019ai recommenc\u00e9 l\u2019op\u00e9ration avec les droits appropri\u00e9s.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cela semble trivial, mais pendant un incident il faut \u00e9viter que ce genre d\u2019erreur conduise \u00e0 croire qu\u2019une sauvegarde existe alors qu\u2019elle est vide ou incompl\u00e8te.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Une archive Docker \u00e9tait tronqu\u00e9e<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Le premier export du conteneur API de Concerto a produit :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Unexpected EOF\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Il a fallu refaire l\u2019export.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Depuis cet incident, je consid\u00e8re la v\u00e9rification comme faisant partie int\u00e9grante de la sauvegarde.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Redis refusait ses snapshots habituels<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">L\u2019erreur <code>MISCONF<\/code> aurait pu m\u2019entra\u00eener dans une r\u00e9paration Redis.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Je l\u2019ai volontairement \u00e9vit\u00e9e pendant la phase critique.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">J\u2019ai extrait les donn\u00e9es d\u2019abord.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La r\u00e9paration vient ensuite.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Nginx Proxy Manager affichait temporairement sa page par d\u00e9faut<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Pendant une partie de la reconstruction, le proxy r\u00e9pondait, mais le bon h\u00f4te n\u2019\u00e9tait pas encore associ\u00e9.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">C\u2019est un cas int\u00e9ressant :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>DNS OK\nHTTPS \/ serveur OK\nNginx OK\nmais routage applicatif incorrect\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Une page qui r\u00e9pond ne signifie donc pas n\u00e9cessairement que l\u2019infrastructure est r\u00e9par\u00e9e.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Ce que cet incident m\u2019a appris sur Docker<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Cette r\u00e9cup\u00e9ration a compl\u00e8tement chang\u00e9 ma fa\u00e7on de consid\u00e9rer une infrastructure Docker.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Avant, j\u2019avais tendance \u00e0 voir un projet principalement sous cette forme :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>compose.yaml\n+\nconteneurs\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Aujourd\u2019hui je le vois plut\u00f4t ainsi :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Infrastructure reproductible\n\u2502\n\u251c\u2500\u2500 code source\n\u251c\u2500\u2500 Dockerfile\n\u251c\u2500\u2500 compose.yaml\n\u251c\u2500\u2500 variables de configuration\n\u251c\u2500\u2500 secrets\n\u251c\u2500\u2500 images ou capacit\u00e9 \u00e0 les reconstruire\n\u251c\u2500\u2500 volumes persistants\n\u251c\u2500\u2500 dumps des bases\n\u251c\u2500\u2500 configuration r\u00e9seau\n\u251c\u2500\u2500 reverse proxy\n\u2514\u2500\u2500 proc\u00e9dure de restauration\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Un seul de ces \u00e9l\u00e9ments ne suffit pas.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Une image Docker n\u2019est pas une sauvegarde<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Le fait d\u2019avoir r\u00e9cup\u00e9r\u00e9 mes images a \u00e9t\u00e9 extr\u00eamement utile.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mais une image seule ne contient pas n\u00e9cessairement :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>la base PostgreSQL ;<\/li>\n\n\n\n<li>la base MariaDB ;<\/li>\n\n\n\n<li>les m\u00e9dias WordPress ;<\/li>\n\n\n\n<li>Redis ;<\/li>\n\n\n\n<li>les donn\u00e9es NPM ;<\/li>\n\n\n\n<li>les donn\u00e9es Dockge ;<\/li>\n\n\n\n<li>les secrets ;<\/li>\n\n\n\n<li>le fichier Compose ;<\/li>\n\n\n\n<li>les certificats.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">De la m\u00eame fa\u00e7on, un volume sans fichier Compose peut \u00eatre difficile \u00e0 r\u00e9utiliser proprement plusieurs mois plus tard.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La sauvegarde doit donc pr\u00e9server <strong>la capacit\u00e9 \u00e0 reconstruire le syst\u00e8me<\/strong>, pas seulement ses donn\u00e9es.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Un fichier Compose doit \u00eatre consid\u00e9r\u00e9 comme du code source<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">C\u2019est probablement une de mes principales conclusions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mes fichiers Compose \u00e9taient dans :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/opt\/homelab\/stacks\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">mais cela ne suffit pas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ils devraient \u00e9galement exister dans une sauvegarde ind\u00e9pendante, voire dans un d\u00e9p\u00f4t Git priv\u00e9.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le VPS doit pouvoir dispara\u00eetre compl\u00e8tement sans faire dispara\u00eetre avec lui la d\u00e9finition de l\u2019infrastructure.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Autrement dit :<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">le serveur de production ne doit jamais \u00eatre l\u2019unique endroit o\u00f9 existe la recette permettant de reconstruire le serveur de production.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Ce que je veux d\u00e9sormais sauvegarder<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Mon syst\u00e8me de sauvegarde doit au minimum contenir quatre couches.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. Infrastructure<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>compose.yaml\nDockerfile\n.env.example\nconfigurations\nscripts\ndocumentation\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">2. Donn\u00e9es persistantes<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>volumes WordPress\nfichiers utilisateurs\nassets applicatifs\nconfigurations persistantes\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">3. Bases de donn\u00e9es<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Avec des dumps applicatifs r\u00e9guliers :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PostgreSQL \u2192 pg_dump \/ pg_dumpall\nMariaDB   \u2192 mariadb-dump\nRedis     \u2192 RDB\nSQLite    \u2192 copie coh\u00e9rente \/ backup SQLite\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">4. \u00c9l\u00e9ments permettant une r\u00e9cup\u00e9ration d\u2019urgence<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Lorsque certaines images sont construites localement :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker save\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">peut constituer une deuxi\u00e8me ligne de d\u00e9fense.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Mais surtout : sauvegarder ailleurs que sur le VPS<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Cr\u00e9er :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/opt\/homelab\/backup\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">est utile.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mais si le VPS dispara\u00eet compl\u00e8tement, cette sauvegarde dispara\u00eet avec lui.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Une v\u00e9ritable strat\u00e9gie doit donc poss\u00e9der au moins une copie hors machine :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>VPS\n \u2502\n \u251c\u2500\u2500 donn\u00e9es actives\n \u2502\n \u2514\u2500\u2500 sauvegarde locale\n          \u2502\n          \u25bc\n   copie externe\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Le stockage externe peut changer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le principe, lui, ne change pas.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Une sauvegarde reproductible doit \u00eatre test\u00e9e<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Cet incident m\u2019a \u00e9galement rappel\u00e9 une \u00e9vidence que l\u2019on oublie facilement :<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>la sauvegarde n\u2019est que la moiti\u00e9 du probl\u00e8me.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">L\u2019autre moiti\u00e9 est :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>restore\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Je veux donc pouvoir prendre un serveur Docker vierge et ex\u00e9cuter une proc\u00e9dure document\u00e9e ressemblant \u00e0 :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1. installer Docker\n2. restaurer les fichiers Compose\n3. recr\u00e9er les r\u00e9seaux\n4. recr\u00e9er les volumes\n5. restaurer PostgreSQL\n6. restaurer MariaDB\n7. restaurer Redis\n8. restaurer WordPress\n9. restaurer Nginx Proxy Manager\n10. lancer les stacks\n11. v\u00e9rifier le r\u00e9seau interne\n12. v\u00e9rifier HTTPS\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Si cette proc\u00e9dure ne peut pas \u00eatre ex\u00e9cut\u00e9e, la sauvegarde reste incompl\u00e8te.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Les commandes \u00e0 ne surtout pas lancer trop vite apr\u00e8s un incident<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Si des conteneurs fonctionnent encore apr\u00e8s la perte de leurs fichiers de configuration, je commencerais aujourd\u2019hui par consid\u00e9rer comme dangereuses les commandes suivantes :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker system prune -a\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>docker volume prune\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>docker compose down -v\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">ainsi qu\u2019un simple :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>reboot\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">tant que je n\u2019ai pas compris exactement ce qui est encore r\u00e9cup\u00e9rable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">M\u00eame un red\u00e9marrage de conteneur peut fermer le dernier descripteur permettant de r\u00e9cup\u00e9rer un fichier supprim\u00e9.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Ce que je ferais aujourd\u2019hui dans les cinq premi\u00e8res minutes<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Si je devais revivre exactement le m\u00eame incident :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1. Stopper toute manipulation.\n\n2. Ne rien red\u00e9marrer.\n\n3. Lancer docker ps.\n\n4. Sauvegarder docker inspect.\n\n5. Inventorier volumes et r\u00e9seaux.\n\n6. Chercher les fichiers supprim\u00e9s encore ouverts avec lsof +L1.\n\n7. Faire les dumps PostgreSQL, MariaDB et Redis.\n\n8. Sauvegarder les images locales.\n\n9. Exporter les conteneurs critiques.\n\n10. Copier l\u2019ensemble du dossier de r\u00e9cup\u00e9ration hors du VPS.\n\n11. Seulement ensuite commencer la reconstruction.\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">L\u2019ordre est important.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Chaque minute pendant laquelle les processus restent vivants peut maintenir accessibles des informations qu\u2019un red\u00e9marrage ferait dispara\u00eetre.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Conclusion : Docker m\u2019a sauv\u00e9, mais il ne remplace pas une strat\u00e9gie de sauvegarde<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Cette erreur aurait pu conduire \u00e0 la perte de plusieurs semaines de travail.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Elle s\u2019est finalement transform\u00e9e en une exp\u00e9rience extr\u00eamement instructive.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le fait que les conteneurs fonctionnent encore m\u2019a permis d\u2019utiliser Docker lui-m\u00eame comme source de reconstruction :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker inspect\ndocker export\ndocker save\ndocker volume inspect\ndocker exec\ndocker cp\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Linux m\u2019a fourni une seconde porte de sortie avec :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/proc\/&lt;PID&gt;\/fd\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">PostgreSQL, MariaDB et Redis ont pu \u00eatre trait\u00e9s directement depuis leurs conteneurs encore actifs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Les volumes Docker avaient surv\u00e9cu \u00e0 la suppression des fichiers Compose.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Les configurations ont pu \u00eatre reconstitu\u00e9es.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Les r\u00e9seaux ont \u00e9t\u00e9 recr\u00e9\u00e9s.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nginx Proxy Manager a de nouveau rout\u00e9 les applications.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Et finalement :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>concerto.nvsd.ch\nfractal.nvsd.ch\nmath-art.nvsd.ch\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">r\u00e9pondaient de nouveau en HTTPS avec :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>HTTP\/2 200\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Mais je ne consid\u00e8re pas cette r\u00e9cup\u00e9ration comme la preuve que les sauvegardes sont facultatives.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">C\u2019est exactement l\u2019inverse.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">J\u2019ai r\u00e9ussi parce que plusieurs circonstances \u00e9taient favorables :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Docker tournait toujours ;<\/li>\n\n\n\n<li>les volumes existaient encore ;<\/li>\n\n\n\n<li>les images n\u2019avaient pas \u00e9t\u00e9 supprim\u00e9es ;<\/li>\n\n\n\n<li>les bases fonctionnaient ;<\/li>\n\n\n\n<li>certains fichiers SQLite \u00e9taient encore ouverts ;<\/li>\n\n\n\n<li>le VPS n\u2019avait pas \u00e9t\u00e9 red\u00e9marr\u00e9.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Un simple :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker system prune\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">un red\u00e9marrage mal plac\u00e9 ou une panne du VPS aurait pu raconter une toute autre histoire.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La le\u00e7on principale de cet incident n\u2019est donc pas :<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">\u00ab Docker conserve les conteneurs, donc je peux r\u00e9cup\u00e9rer mon serveur. \u00bb<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Elle est plut\u00f4t :<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Une infrastructure s\u00e9rieuse doit pouvoir \u00eatre d\u00e9truite enti\u00e8rement puis reconstruite \u00e0 partir de sauvegardes document\u00e9es, v\u00e9rifi\u00e9es et stock\u00e9es ailleurs.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Mon Homelab continuera \u00e0 \u00e9voluer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mais d\u00e9sormais, chaque nouveau service devra r\u00e9pondre \u00e0 une question suppl\u00e9mentaire :<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>si je supprime ce serveur demain, comment est-ce que je le reconstruis ?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">C\u2019est probablement la question la plus importante que cet incident m\u2019ait appris \u00e0 poser.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Comment une erreur de manipulation m\u2019a oblig\u00e9 \u00e0 comprendre ce que Docker conserve r\u00e9ellement \u2014 et \u00e0 reconstruire mon infrastructure \u00e0 partir des conteneurs [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-106","post","type-post","status-publish","format-standard","hentry","category-non-classe"],"_links":{"self":[{"href":"https:\/\/homelab.nvsd.ch\/index.php?rest_route=\/wp\/v2\/posts\/106","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/homelab.nvsd.ch\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/homelab.nvsd.ch\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/homelab.nvsd.ch\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/homelab.nvsd.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=106"}],"version-history":[{"count":1,"href":"https:\/\/homelab.nvsd.ch\/index.php?rest_route=\/wp\/v2\/posts\/106\/revisions"}],"predecessor-version":[{"id":107,"href":"https:\/\/homelab.nvsd.ch\/index.php?rest_route=\/wp\/v2\/posts\/106\/revisions\/107"}],"wp:attachment":[{"href":"https:\/\/homelab.nvsd.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=106"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/homelab.nvsd.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=106"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/homelab.nvsd.ch\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=106"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}