SSD Nodes Learn Hosting plans →
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-08-07

Comprendre le réseau Docker Compose

Réseau bridge par défaut, DNS par nom de service, mode host et réseau partagé entre projets. Voyez aussi pourquoi un port publié peut contourner UFW.

Ce que Compose crée avant le démarrage de votre application

Le réseau Docker Compose repose sur une règle simple : docker compose up crée un réseau privé pour le projet, y rattache chaque service et permet à ces services de communiquer entre eux par nom de service. Vous n’avez pas besoin d’écrire une seule ligne networks: pour cela. La plupart des problèmes de compréhension du réseau Compose viennent du fait que ce réseau par défaut est déjà créé.

Voici un fichier minimal. Enregistrez-le sous compose.yaml dans un répertoire appelé shop.

services:
  web:
    image: nginx:1.27
    ports:
      - "8080:80"
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example

Démarrez le projet et examinez ce que Docker a créé :

docker compose up -d
docker network ls

La liste contient maintenant un réseau appelé shop_default. Compose le nomme <project>_default et le nom du projet correspond par défaut au nom du répertoire, en minuscules. Remplacez-le avec docker compose -p myproject up -d ou avec un name: myproject de niveau supérieur dans le fichier. Son driver est bridge, c’est-à-dire un switch virtuel à l’intérieur de l’hôte. Chaque conteneur reçoit une adresse sur un sous-réseau privé, et le trafic sortant est traduit vers l’adresse de l’hôte au moment de sortir.

docker compose down supprime ensuite ce réseau. C’est pourquoi un ancien conteneur d’un projet précédent peut empêcher la suppression du réseau : Docker renvoie error while removing network: network shop_default has active endpoints, et il faut arrêter ou supprimer le conteneur qui y est encore rattaché.

Si Compose est nouveau pour vous, consultez d’abord la structure du fichier Compose et les commandes de cycle de vie, car tout ce qui suit suppose que vous savez démarrer et arrêter un projet.

Le DNS par nom de service est le point que les débutants oublient

Sur tout réseau défini par l’utilisateur, Docker exécute un serveur DNS intégré que chaque conteneur voit à 127.0.0.11. Il résout les noms de service vers les adresses actuelles des conteneurs. Ainsi, web atteint la base de données avec le nom d’hôte db, sur le port 5432, sans aucune configuration.

docker compose exec web getent hosts db

Cette commande affiche une ligne similaire à 172.18.0.2 db. Si elle n’affiche rien, les deux services ne sont pas sur le même réseau.

L’erreur que presque tout le monde fait au moins une fois consiste à utiliser localhost dans la configuration de l’application. Dans un conteneur, localhost désigne ce conteneur, pas l’hôte ni l’autre service. Les clients Postgres l’indiquent clairement :

could not connect to server: Connection refused
	Is the server running on host "localhost" (127.0.0.1) and accepting
	TCP connections on port 5432?

La chaîne de connexion doit être postgresql://postgres:example@db:5432/postgres. La partie hôte correspond au nom du service.

Deux détails vous feront gagner du temps par la suite. Les noms sont résolus vers ce qui fonctionne actuellement. Ainsi, docker compose up -d --scale web=3 renvoie un nom avec trois adresses. Un client qui met le DNS en cache indéfiniment restera lié à un conteneur arrêté. Par ailleurs, le réseau historique bridge, utilisé par un docker run simple sans --network, ne fournit aucune résolution de noms. C’est pourquoi les conseils de 2016 sur les links entre conteneurs ne correspondent pas à ce que vous observez.

Vous n’avez pas besoin de ports: pour connecter deux services

ports: publie un port de conteneur sur l’hôte. Il sert au trafic entrant depuis l’extérieur de Docker. Il n’a aucun rapport avec le trafic entre services, qui fonctionne déjà sur toute la plage de ports du réseau du projet.

Le ports: - "5432:5432" que beaucoup de personnes ajoutent à leur service de base de données est donc inutile et réellement dangereux : il expose Postgres sur l’interface publique du serveur. Supprimez-le. Si vous voulez y accéder depuis votre ordinateur portable pour effectuer une migration, liez-le à l’interface loopback avec "127.0.0.1:5432:5432", puis utilisez un tunnel SSH. La différence entre un socket en écoute, un port publié et une règle de pare-feu est expliquée dans le fonctionnement des ports et des services en écoute sous Linux.

Sous Compose, expose: sert uniquement de documentation. Il n’ouvre rien, car rien n’était fermé entre les conteneurs connectés au même réseau.

Quand utiliser network_mode: host et quels en sont les coûts

Le mode host supprime le propre namespace réseau du conteneur et permet au processus d’utiliser directement les interfaces de l’hôte.

services:
  probe:
    image: alpine:3.20
    network_mode: host
    command: sleep infinity

Il existe de vraies raisons de l’utiliser. Un processus qui doit voir le trafic broadcast ou multicast sur le réseau local, par exemple pour la découverte des appareils d’un serveur multimédia ou d’une plateforme domotique, ne peut pas le voir derrière un bridge, car celui-ci ne transmet pas ce trafic au conteneur. Un agent de monitoring qui lit les compteurs des interfaces de l’hôte a besoin des interfaces de l’hôte. Vous supprimez également l’étape de translation d’adresses, ce qui peut avoir son importance avec des débits élevés de paquets.

Les coûts sont spécifiques.

ports: ne fonctionne plus. Docker avertit que les ports publiés sont ignorés avec le mode réseau host, et que le conteneur utilise les ports sur lesquels son processus écoute. Deux conteneurs en mode host qui veulent utiliser le port 8080 entrent en conflit, et le second s’arrête avec bind: address already in use.

La résolution par nom de service disparaît dans les deux sens. Le conteneur ne se trouve pas sur le réseau du projet. Il ne peut donc pas résoudre db, et les autres services ne peuvent pas le résoudre non plus. Il ne peut les joindre que via les ports publiés sur l’hôte, généralement sur 127.0.0.1.

L’isolation disparaît. Un processus qui écoute sur 0.0.0.0 dans un conteneur en mode host écoute sur toutes les interfaces de votre serveur, y compris l’interface publique, exactement comme un paquet installé avec apt. Cela présente toutefois un avantage : ce trafic suit le chemin d’entrée normal. Les règles UFW s’y appliquent donc, contrairement aux ports publiés.

Le mode host est une fonctionnalité de Docker Engine sous Linux. Docker Desktop ne le prend en charge qu’à partir de la version 4.34, et uniquement après son activation. De plus, les conteneurs ne peuvent pas utiliser les adresses IP de l’hôte et seuls TCP et UDP sont pris en charge. Si une partie de votre équipe utilise des serveurs Linux et l’autre Docker Desktop, attendez-vous à ce que le même fichier se comporte différemment.

Utilisez le mode host lorsque vous avez besoin des interfaces de l’hôte. Ne l’utilisez pas pour résoudre un problème de connexion, car il remplace généralement un problème par un autre, plus difficile.

Relier deux projets Compose avec un réseau externe

Un réseau créé par un projet n’est pas visible par un autre. C’est pourquoi un reverse proxy dans proxy/compose.yaml ne peut pas voir une application dans app/compose.yaml, même sur le même serveur. La solution consiste à utiliser un réseau qui n’appartient à aucun des deux projets.

Créez-le une seule fois, manuellement :

docker network create edge

Déclarez-le ensuite comme externe dans chaque projet. Côté proxy :

services:
  proxy:
    image: traefik:v3.1
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - edge

networks:
  edge:
    name: edge
    external: true

Côté application :

services:
  app:
    image: nginx:1.27
    networks:
      - edge
      - internal
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example
    networks:
      - internal

networks:
  edge:
    name: edge
    external: true
  internal:

external: true indique à Compose de se connecter à un réseau existant au lieu d’en créer un, et de le conserver sur docker compose down. La clé distincte name: est plus importante qu’il n’y paraît : sans elle, Compose recherche un réseau nommé exactement edge. Avec elle, vous pouvez donner un nom au réseau dans votre fichier et un autre nom au réseau sur l’hôte.

Si le réseau n’existe pas, Compose refuse de démarrer et indique que le réseau a été déclaré comme externe, mais qu’il est introuvable. Créez-le d’abord.

Notez ce que le fichier de l’application fait avec internal. La base de données se trouve uniquement sur le réseau local au projet. Le proxy ne peut donc pas l’atteindre, et seul app le peut. Ajouter internal: true sous un réseau va plus loin et supprime complètement sa route vers l’extérieur. C’est un bon choix par défaut pour une base de données, avec une conséquence à connaître avant de l’appliquer : un conteneur sur un réseau interne ne peut rien télécharger. Un entrypoint qui exécute apt-get update ou pip install au démarrage reste donc bloqué, puis échoue après un délai d’attente.

Pour un exemple complet avec les règles de routage et les certificats, consultez exécuter plusieurs applications derrière une seule instance Traefik.

Les ports publiés contournent UFW

C’est la partie du réseau Compose qui peut provoquer un incident de sécurité. Vous publiez un port, vous vérifiez qu’UFW est actif et refuse tout sauf SSH, mais le service reste accessible depuis Internet.

sudo ufw status
curl http://203.0.113.10:8080

UFW indique que le port est bloqué. Le curl renvoie pourtant la page. Rien n’est cassé. Docker ajoute directement ses propres règles de translation d’adresses et de forwarding dans iptables. Le trafic destiné à un port de conteneur publié est transféré vers le conteneur au lieu d’être remis à l’hôte. Il ne passe donc jamais par la chaîne qu’UFW gère pour le trafic destiné localement. Les règles de Docker sont également évaluées avant celles d’UFW.

La solution rapide consiste à publier le port uniquement là où c’est nécessaire :

    ports:
      - "127.0.0.1:8080:80"

Cela lie le côté hôte à l’interface loopback. Le port est donc accessible depuis le serveur lui-même et via un tunnel SSH, mais depuis nulle part ailleurs. Placez le point d’entrée public derrière un reverse proxy qui publie volontairement les ports 80 et 443. L’explication complète, y compris la chaîne DOCKER-USER pour les cas où vous devez filtrer un port publié, se trouve dans pourquoi Docker publie directement au-delà d’UFW et comment corriger le problème.

Comment le déboguer en quatre commandes

Commencez par vérifier sur quel réseau chaque conteneur se trouve réellement :

docker network inspect shop_default

Le bloc Containers répertorie tous les conteneurs connectés, avec leur adresse. Un service absent de cette liste se trouve sur un autre réseau, utilise le mode host ou n’est pas en cours d’exécution.

Testez la résolution de noms depuis un conteneur temporaire connecté au même réseau. Vous n’avez ainsi besoin d’aucun outil dans vos propres images :

docker run --rm --network shop_default busybox nslookup db
docker run --rm --network shop_default busybox nc -zv db 5432

Un échec de nslookup indique un problème de résolution de noms ou d’appartenance au réseau. Si nslookup réussit alors que nc échoue, le service fonctionne mais n’écoute pas sur ce port, ou écoute sur 127.0.0.1 à l’intérieur de son propre conteneur au lieu de 0.0.0.0. Ce cas est fréquent avec les serveurs de développement. La correction concerne l’adresse d’écoute de l’application, pas Docker.

Un autre problème peut ressembler à un bug Docker. Si les conteneurs communiquent entre eux mais ne peuvent pas joindre une machine du réseau de votre bureau ou de votre VPN, le sous-réseau Docker chevauche probablement ce réseau. Docker alloue par défaut des sous-réseaux à partir de 172.17.0.0/16. Modifiez le pool dans /etc/docker/daemon.json :

{
  "default-address-pools": [
    { "base": "10.200.0.0/16", "size": 24 }
  ]
}

Exécutez ensuite sudo systemctl restart docker et recréez les réseaux concernés, car un réseau existant conserve le sous-réseau avec lequel il a été créé.

FAQ

Pourquoi mes conteneurs ne peuvent-ils pas se joindre par nom de service ?

Ils ne sont pas sur le même réseau. Compose place automatiquement chaque service sur <project>_default, mais dès que vous ajoutez une liste networks: à un service, cette liste devient l’ensemble complet de ses réseaux et le réseau par défaut n’est plus implicite. Exécutez docker network inspect <network> et vérifiez que les deux conteneurs apparaissent dans le bloc Containers. Vérifiez également qu’aucun des deux services n’utilise network_mode: host, car un conteneur en mode host n’est connecté à aucun réseau Docker et ne peut pas résoudre les noms de service.

Dois-je publier les ports pour qu’un service puisse joindre un autre service ?

Non. Sur un réseau Compose, tous les ports de chaque conteneur sont accessibles aux autres conteneurs présents sur ce réseau. ports: sert uniquement à exposer un conteneur au trafic provenant de l’extérieur de Docker, et expose: ne constitue qu’une documentation. Publier le port d’une base de données est une pratique courante et coûteuse, car la base de données devient accessible sur l’interface publique de votre serveur.

Quelle est la différence entre les réseaux bridge et host ?

Le réseau bridge donne au conteneur son propre namespace réseau et une adresse sur un switch virtuel. Il fournit aussi la résolution automatique des noms entre conteneurs et la traduction du trafic sortant. Le réseau host donne directement au conteneur accès à la pile réseau de l’hôte : pas d’adresse séparée, pas de résolution par nom de service, pas de publication de ports et aucune isolation vis-à-vis des autres processus en écoute sur l’hôte. Bridge est le mode par défaut et le bon choix, sauf si le processus doit utiliser les interfaces de l’hôte.

Comment connecter des conteneurs provenant de deux fichiers Compose différents ?

Créez un réseau partagé avec docker network create edge, puis déclarez-le dans les deux fichiers avec external: true et connectez-y les services qui doivent communiquer. Compose ne le créera ni ne le supprimera. Si vous omettez l’étape de création, Compose refuse de démarrer et signale que le réseau est déclaré comme externe, mais introuvable.

Pourquoi mon conteneur est-il accessible depuis Internet alors qu’UFW bloque le port ?

Parce qu’un port publié est traité par les règles de forwarding que Docker ajoute à iptables. Ces règles sont évaluées avant celles d’UFW, et le trafic forwardé ne passe de toute façon pas par la chaîne filtrée par UFW. Liez le côté hôte à l’interface loopback avec "127.0.0.1:8080:80" et placez tout service public derrière un reverse proxy sur les ports 80 et 443.