Comprendre Docker de A à Z : conteneurs, réseaux, volumes et Compose
Salut les amateurs de self-hosting ! Si vous bricolez un homelab, vous lancez des conteneurs Docker tous les jours… sans forcément savoir ce qui se passe sous le capot. Le docker-compose.yml qu’on copie-colle est plein de mots un peu magiques : bridge, ports, volumes, restart: unless-stopped… Aujourd’hui, on démystifie tout ça. Pas de sorcellerie : juste des process Linux bien cloisonnés, empaquetés avec leurs dépendances. Une fois ces briques en tête, vous lirez n’importe quelle stack comme un livre ouvert.
C’est quoi un conteneur, au fond ?
Un conteneur, c’est votre application plus toutes ses dépendances (bibliothèques, runtime, config) empaquetées ensemble, qui tournent isolées du reste de la machine. Fini le « ça marche chez moi mais pas sur le serveur » : tout ce dont l’app a besoin voyage avec elle.
Deux mots à ne jamais confondre :
- L’image, c’est le moule figé : la recette + les ingrédients pré-dosés.
- Le conteneur, c’est le plat une fois cuisiné depuis ce moule. Vous pouvez sortir dix conteneurs identiques de la même image, les jeter, les recréer — c’est sans douleur.
Une image se construit depuis un Dockerfile (la recette, étape par étape), en couches empilées et mises en cache. Elle se stocke dans un registre : Docker Hub (public), ou votre propre registre — un Gitea fait très bien le job pour vos images maison.
💡 Sous le capot, un conteneur partage le noyau de la machine hôte. Il est isolé par deux mécanismes du noyau Linux : les namespaces (le cloisonnement : son propre système de fichiers, son réseau, ses process) et les cgroups (les limites de ressources : CPU, RAM). En clair, un conteneur, c’est un process Linux déguisé en mini-machine — pas une vraie machine.
Docker vs machine virtuelle : la vraie différence
C’est LA question qu’on se pose au début. La différence tient en un mot : le noyau.
- Une machine virtuelle virtualise le matériel et embarque un OS complet, avec son propre noyau, par-dessus un hyperviseur. C’est lourd : plusieurs Go, un démarrage en dizaines de secondes voire minutes.
- Un conteneur ne virtualise rien : il partage le noyau de l’hôte et n’embarque que l’app et ses bibliothèques. C’est léger : quelques Mo, un démarrage en millisecondes.
🏠 L’image qui marche bien : une VM, c’est une maison entière, avec ses propres fondations, murs et plomberie. Un conteneur, c’est un appartement dans un immeuble : il partage les fondations (le noyau) avec les voisins, mais reste cloisonné chez lui.
Le compromis ? La VM offre une isolation plus forte (noyau séparé) et peut faire tourner un autre OS (du Windows sur une machine Linux). Le conteneur, lui, partage le noyau Linux de l’hôte — d’où sa légèreté, mais aussi sa contrainte (sur Mac et Windows, Docker tourne en réalité dans une petite VM Linux cachée).
Concrètement : sur mon NAS à base de modeste Celeron, je fais tourner plus de 70 conteneurs sans broncher. Avec des VM, j’en lancerais une poignée et la machine serait à genoux. C’est toute la magie de la densité.
Les réseaux Docker (bridge, host… et les autres)
Chaque conteneur est branché à un ou plusieurs réseaux. Les principaux :
- bridge (le défaut) : un réseau privé virtuel créé par Docker sur l’hôte. Chaque conteneur reçoit une IP interne, les conteneurs se parlent entre eux, et ils sortent vers l’extérieur via du NAT. ⚠️ Piège classique : le réseau
bridgepar défaut ne fait pas de résolution DNS par nom. Sur un réseau bridge personnalisé (que vous créez vous-même), les conteneurs se joignent par leur nom (http://navidrome:4533). C’est pour ça qu’on crée toujours un réseau dédié. - host : le conteneur partage directement la pile réseau de l’hôte. Pas d’isolation réseau, pas de NAT, pas de mapping de ports. Indispensable pour ce qui a besoin de mDNS/UPnP (coucou Music Assistant) ou de performances réseau maximales. Revers de la médaille : aucune isolation, et des conflits de ports possibles avec l’hôte.
- none : aucun réseau du tout. Isolation totale, pour les cas rares où le conteneur ne doit parler à personne.
- macvlan (avancé) : le conteneur obtient sa propre IP sur votre LAN physique et apparaît comme une vraie machine du réseau. Pratique pour certains appareils ou services qui doivent être « visibles » directement.
- overlay (avancé) : pour relier des conteneurs répartis sur plusieurs machines (clusters Swarm).
Créer et cloisonner avec les réseaux
Au-delà des types, le réseau est un vrai outil de sécurité. On crée un réseau d’un coup :
docker network create traefik-public
Déclaré comme externe, il peut être partagé entre plusieurs stacks — c’est le pattern classique pour qu’un reverse proxy comme Traefik atteigne tous vos services.
Et surtout, on cloisonne : votre base de données sur un réseau interne non exposé, votre application sur le réseau public. La base n’est alors joignable que par l’app, jamais depuis l’extérieur. C’est exactement ce qu’on fait pour un service comme Umami : un réseau internal pour le couple app + base, et traefik-public uniquement pour la façade web.
Le mapping de ports
Par défaut, les ports d’un conteneur ne sont pas accessibles depuis l’hôte. Pour en publier un :
docker run -p 8096:8096 jellyfin/jellyfin
La syntaxe, c’est -p HÔTE:CONTENEUR (ou la clé ports: en Compose). Ici, le port 8096 du conteneur devient accessible sur le 8096 de l’hôte. Quelques finesses utiles :
- Binder sur une IP précise pour ne pas exposer à tout le réseau :
-p 127.0.0.1:8096:8096(accessible seulement en local). - Préciser le protocole :
-p 51820:51820/udp.
💡 En réseau host, pas de mapping : le conteneur est directement sur les ports de l’hôte. Et avec un reverse proxy (Traefik), on n’expose souvent aucun port : Traefik route en interne via le réseau Docker, et lui seul écoute sur 80/443. Plus propre, et bien plus sûr — vos services ne traînent pas à l’air libre.
Le restart policy : remonter tout seul après une coupure
C’est ce qui rend un homelab vraiment autonome. La clé restart: dit à Docker quoi faire quand un conteneur s’arrête :
no(défaut) : on ne relance jamais.unless-stopped: on relance toujours, sauf si c’est vous qui l’avez arrêté explicitement. Le bon défaut maison.always: on relance toujours, même un conteneur que vous aviez arrêté à la main (dès que le démon redémarre).on-failure[:max]: on relance uniquement en cas de sortie en erreur, avec un nombre d’essais optionnel.
L’intérêt énorme : au redémarrage de la machine, le démon Docker relance les conteneurs selon leur policy. Une coupure de courant à 4 h du matin ? Votre homelab remonte tout seul pendant que vous dormez. Mettez unless-stopped partout et dormez tranquille.
Les volumes : pour ne pas perdre ses données
Règle d’or : un conteneur est jetable. On le supprime, on le recrée depuis son image, c’est parfaitement normal. Mais du coup… où vont les données ? Sans volume, elles disparaissent avec le conteneur. Les volumes servent à faire persister les données en dehors du conteneur. Deux types :
- Bind mount :
-v /chemin/sur/hote:/data— vous montez un dossier de l’hôte dans le conteneur. Vous maîtrisez le chemin exact, parfait pour la config que vous voulez voir et éditer. - Named volume :
-v mesdata:/data— un volume géré par Docker (rangé dans ses propres dossiers). Plus propre et plus portable pour les données applicatives.
On ajoute :ro pour un montage en lecture seule (votre dossier de musique monté dans un serveur, par exemple).
💡 Petit retour de terrain : sur un NAS sous OpenMediaVault, l’option
noexecsur les partages casse certains conteneurs qui ont besoin d’exécuter des binaires depuis leur volume. La parade que j’utilise : passer en named volumes, qui échappent à cette contrainte (ça m’a sauvé sur Plex et Music Assistant, entre autres).
⚠️ Attention : un volume ne se sauvegarde pas tout seul. Vos données précieuses vivent là-dedans — pensez à les intégrer à votre stratégie de sauvegarde (un bon vieux 3-2-1).
docker run vs docker compose (et c’est quoi une « stack » ?)
Deux façons de lancer vos conteneurs :
docker run: un conteneur à la main, avec une avalanche de drapeaux (-p,-v,--network,--restart…). Pratique pour tester vite fait, mais vite ingérable et impossible à retenir.docker compose: vous décrivez toute une stack — plusieurs services, leurs réseaux et leurs volumes — dans un fichierdocker-compose.ymldéclaratif. Undocker compose up -det tout se monte d’un coup.
Une stack, c’est justement l’ensemble des services décrits dans un Compose : par exemple une app + sa base de données + son reverse proxy, qui forment un tout cohérent.
Le même conteneur, en run puis en Compose :
docker run -d --name navidrome -p 4533:4533 \
-v /musique:/music:ro -v nd_data:/data \
--restart unless-stopped deluan/navidrome:latest
services:
navidrome:
image: deluan/navidrome:latest
container_name: navidrome
restart: unless-stopped
ports:
- "4533:4533"
volumes:
- /musique:/music:ro
- nd_data:/data
volumes:
nd_data:
Le second est lisible, reproductible et versionnable (donc parfait à mettre dans Git !). Ma reco : Compose pour tout ce qui dure, run uniquement pour un test jetable. Et si vous voulez une interface graphique pour gérer vos stacks, Portainer fait ça très bien.
Quelques trucs en plus qui changent la vie
- Images & tags : épinglez une version (
postgres:16-alpine) plutôt quelatest.latestpeut changer sous vos pieds au prochainpullet casser une stack qui marchait. Une version figée = pas de surprise. - Les logs :
docker logs -f <nom>oudocker compose logs -fpour suivre ce qui se passe en direct. Votre premier réflexe de dépannage. - Les mises à jour : à la main (
docker compose pull && docker compose up -d), ou automatisées avec Watchtower. - Le ménage : les images et volumes orphelins bouffent le disque avec le temps.
docker system prunefait le tri (à manier en connaissant ce qu’on supprime). - La sécurité : tournez en utilisateur non-root quand c’est possible (
user:), montez en:roce qui n’a pas besoin d’écriture, cloisonnez vos réseaux, et n’exposez que les ports strictement nécessaires.
Mémo des commandes utiles
docker ps # conteneurs qui tournent
docker ps -a # tous, même arrêtés
docker images # images présentes en local
docker logs -f <nom> # suivre les logs d'un conteneur
docker exec -it <nom> sh # entrer dans un conteneur
docker compose up -d # monter la stack en arrière-plan
docker compose down # arrêter et supprimer la stack
docker compose pull # tirer les nouvelles versions d'images
docker system prune # nettoyer ce qui traîne
Conclusion
Voilà — Docker, ce n’est pas de la magie noire : des process Linux cloisonnés, empaquetés avec leurs dépendances, décrits dans un fichier qu’on versionne. Une fois les briques en tête — image et conteneur, réseau, port, volume, restart policy, Compose — vous lisez n’importe quel docker-compose.yml sans transpirer. Et c’est précisément ce qui rend un homelab gérable : tout est déclaratif, reproductible, et remonte tout seul après une coupure.
Pour aller plus loin, le terrain de jeu est immense : mettre un reverse proxy (Traefik) devant vos services, héberger votre registre privé pour vos propres images, ou monter une CI/CD qui construit et déploie vos conteneurs à chaque commit. Mais ça, ce sont d’autres articles.