SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-08-07

Hoe werken Docker Compose netwerken?

Leer hoe Docker Compose automatisch netwerken aanmaakt, services via DNS verbindt en waarom poorten soms UFW omzeilen. Ontdek de werking van bridge netwerken en host mode.

Wat Compose opbouwt voordat uw applicatie start

Docker Compose-netwerken beginnen met één regel: docker compose up maakt een privaat netwerk aan voor het project, koppelt elke service hieraan en zorgt ervoor dat deze services elkaar kunnen bereiken via de servicenaam. U hoeft hiervoor geen enkele networks:-regel te schrijven. De meeste verwarring rondom Compose-netwerken ontstaat doordat men niet weet dat deze standaardinstelling al actief is.

Hier is een klein bestand. Sla dit op als compose.yaml in een map genaamd shop.

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

Start het project en bekijk wat Docker heeft aangemaakt:

docker compose up -d
docker network ls

De lijst bevat nu een netwerk genaamd shop_default. Compose noemt dit <project>_default, waarbij de projectnaam standaard de kleine letters van de mapnaam overneemt. U kunt dit overschrijven met docker compose -p myproject up -d of met een name: myproject op het hoogste niveau in het bestand. De driver is bridge, wat een virtuele switch binnen de host is. Elke container krijgt een adres op een privaat subnet en uitgaand verkeer wordt vertaald naar het adres van de host.

docker compose down verwijdert dat netwerk weer. Dit is de reden waarom een verouderde container van een oud project een netwerk open kan houden: Docker weigert dit met error while removing network: network shop_default has active endpoints, en de oplossing is om de container die er nog aan gekoppeld is te stoppen of te verwijderen.

Als Compose nieuw voor u is, is het de moeite waard om eerst de Compose-bestandsindeling en lifecycle-commando's te lezen, aangezien alles hieronder ervan uitgaat dat u een project kunt starten en stoppen.

DNS op servicenaam is het onderdeel dat beginners missen

Op elk door de gebruiker gedefinieerd netwerk voert Docker een ingebouwde DNS-server uit die elke container ziet op 127.0.0.11. Deze lost servicenamen op naar de huidige containeradressen. Hierdoor bereikt web de database op de hostnaam db, op poort 5432, zonder enige configuratie.

docker compose exec web getent hosts db

Dit drukt een regel af zoals 172.18.0.2 db. Als er niets wordt afgedrukt, bevinden de twee services zich niet op hetzelfde netwerk.

De fout die bijna iedereen een keer maakt, is het gebruik van localhost in de applicatieconfiguratie. Binnen een container is localhost die container zelf, niet de host en niet de andere service. Postgres-clients rapporteren dit duidelijk:

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?

De connection string moet postgresql://postgres:example@db:5432/postgres zijn. Het host-gedeelte is de servicenaam.

Twee details die later tijd besparen. Namen lossen op naar wat er op dat moment draait, dus docker compose up -d --scale web=3 geeft één naam met drie adressen, en een client die DNS voor altijd in de cache houdt, zal zichzelf vastpinnen aan een dode container. Bovendien heeft het verouderde bridge-netwerk dat door een standaard docker run zonder --network wordt gebruikt, helemaal geen naamresolutie. Dat is de reden waarom advies over container-links uit 2016 niet overeenkomt met wat u nu ziet.

U heeft ports: niet nodig om twee services met elkaar te verbinden

ports: publiceert een containerpoort op de host. Dit is bedoeld voor verkeer dat van buiten Docker komt. Het heeft geen invloed op verkeer tussen services onderling, dat al werkt over het volledige poortbereik op het projectnetwerk.

De ports: - "5432:5432" die veel mensen toevoegen aan hun databaseservice is dus nutteloos en zelfs schadelijk: het stelt Postgres bloot aan de publieke interface van de server. Verwijder deze. Als u wilt dat de database bereikbaar is vanaf uw laptop voor een migratie, bind deze dan aan de loopback met "127.0.0.1:5432:5432" en benader deze via een SSH-tunnel. Het verschil tussen een listening socket, een gepubliceerde poort en een firewallregel wordt behandeld in hoe poorten en listening services werken op Linux.

expose: is enkel documentatie binnen Compose. Het opent niets, omdat er tussen containers op hetzelfde netwerk niets was afgesloten.

Wanneer network_mode host de juiste keuze is en wat de consequenties zijn

Host-modus verwijdert de eigen netwerk-namespace van de container en laat het proces direct gebruikmaken van de interfaces van de host.

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

Er zijn gegronde redenen om dit toe te passen. Een proces dat broadcast- of multicast-verkeer op het lokale netwerk moet zien, zoals apparaatdetectie voor een mediaserver of een hub voor huisautomatisering, kan dit niet zien vanachter een bridge. De bridge stuurt dit verkeer namelijk niet door naar de container. Een monitoring-agent die de interface-tellers van de host uitleest, heeft toegang tot de interfaces van de host nodig. Bovendien slaat u de stap van adresvertaling over, wat relevant is bij hoge pakketsnelheden.

De nadelen zijn specifiek.

ports: werkt niet meer. Docker waarschuwt dat gepubliceerde poorten worden genegeerd bij gebruik van host-netwerkmodus; de container bindt aan de poort die het proces zelf aanroept. Twee containers in host-modus die beide poort 8080 willen gebruiken, conflicteren met elkaar, waarna de tweede container stopt met bind: address already in use.

Naamresolutie op basis van servicenaam werkt in beide richtingen niet meer. De container bevindt zich niet in het projectnetwerk, waardoor deze db niet kan resolven en andere services de container evenmin kunnen vinden. De container bereikt andere services alleen via poorten die op de host zijn gepubliceerd, meestal via 127.0.0.1.

Isolatie vervalt. Een proces dat 0.0.0.0 bindt binnen een container in host-modus, luistert op elke interface van uw server, inclusief de publieke interface. Dit werkt exact hetzelfde als een pakket dat via apt is geïnstalleerd. Er is één voordeel: dit verkeer volgt het normale input-pad, waardoor UFW-regels wel van toepassing zijn, wat bij gepubliceerde poorten niet het geval is.

Host-modus is een functie van de Linux Docker Engine. Docker Desktop ondersteunt dit pas vanaf versie 4.34 en alleen nadat u dit heeft ingeschakeld, met de aanvullende beperking dat containers geen host-IP-adressen kunnen binden en alleen TCP en UDP worden afgehandeld. Als de helft van uw team op Linux-servers werkt en de andere helft op Docker Desktop, moet u er rekening mee houden dat hetzelfde bestand zich anders gedraagt.

Kies voor host-modus wanneer u de interfaces van de host nodig heeft. Gebruik het niet om een verbindingsprobleem op te lossen, omdat dit meestal het ene probleem vervangt door een complexer probleem.

Twee Compose-projecten verbinden met een extern netwerk

Een netwerk dat door het ene project is aangemaakt, is niet zichtbaar voor het andere. Daarom kan een reverse proxy in proxy/compose.yaml een app in app/compose.yaml niet zien, zelfs niet op dezelfde server. De oplossing is een netwerk dat niet bij een van beide projecten hoort.

Maak het eenmalig handmatig aan:

docker network create edge

Declareer het vervolgens als extern in elk project. De kant van de 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

De kant van de applicatie:

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 vertelt Compose om verbinding te maken met een bestaand netwerk in plaats van er zelf een aan te maken, en om het te laten bestaan bij docker compose down. De afzonderlijke name:-sleutel is belangrijker dan hij lijkt: zonder deze sleutel zoekt Compose naar een netwerk met exact de naam edge. Met deze sleutel kunt u het netwerk in uw bestand een andere naam geven dan op de host.

Als het netwerk niet bestaat, weigert Compose te starten en wordt gemeld dat het netwerk als extern is gedeclareerd maar niet kon worden gevonden. Maak het netwerk eerst aan.

Let op wat het applicatiebestand doet met internal. De database bevindt zich alleen op dat project-lokale netwerk, waardoor de proxy deze niet kan bereiken en alleen app dat wel kan. Het toevoegen van internal: true onder een netwerk gaat nog een stap verder en verwijdert de route naar de buitenwereld volledig. Dit is een goede standaardinstelling voor een database, maar houd rekening met één nadeel voordat u dit instelt: een container op een intern netwerk kan niets downloaden. Een entrypoint dat bij het opstarten apt-get update of pip install uitvoert, zal daardoor blijven hangen en uiteindelijk falen met een timeout.

Voor een volledig uitgewerkte opstelling met routeringsregels en certificaten, zie meerdere apps draaien achter één Traefik-instantie.

Gepubliceerde poorten omzeilen UFW

Dit is het onderdeel van Compose-netwerken dat vaak leidt tot beveiligingsincidenten. U publiceert een poort, u controleert of UFW actief is en alles behalve SSH blokkeert, en toch is de service bereikbaar vanaf het internet.

sudo ufw status
curl http://203.0.113.10:8080

UFW meldt dat de poort is geblokkeerd. De curl geeft de pagina desondanks weer. Er is niets defect. Docker schrijft zijn eigen adresvertaling en doorstuurregels rechtstreeks in iptables. Verkeer naar een gepubliceerde containerpoort wordt doorgestuurd naar de container in plaats van afgeleverd bij de host. Hierdoor passeert het verkeer nooit de chain die UFW beheert voor lokaal bestemd verkeer. De regels van Docker worden bovendien eerder geëvalueerd dan die van UFW.

De snelle oplossing is om alleen te publiceren waar dit noodzakelijk is:

    ports:
      - "127.0.0.1:8080:80"

Hiermee wordt de host-zijde gebonden aan de loopback-interface. De poort is daardoor alleen bereikbaar vanaf de server zelf, via een SSH-tunnel, en nergens anders vandaan. Plaats het publieke toegangspunt achter een reverse proxy die bewust poort 80 en 443 publiceert. De volledige uitleg, inclusief de DOCKER-USER chain voor situaties waarin u een gepubliceerde poort moet filteren, vindt u in waarom Docker publiceert langs UFW en hoe dit op te lossen.

Hoe u dit in vier commando's debugt

Begin met te controleren op welk netwerk elke container zich daadwerkelijk bevindt:

docker network inspect shop_default

Het Containers-blok toont elke gekoppelde container met het bijbehorende adres. Een service die niet in die lijst staat, bevindt zich op een ander netwerk, draait in host-modus of is niet actief.

Test de naamresolutie vanuit een tijdelijke container die op hetzelfde netwerk is aangesloten, zodat u geen extra tools in uw eigen images hoeft te installeren:

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

Als nslookup faalt, wijst dit op een probleem met naamresolutie of netwerktoegang. Als nslookup slaagt maar nc faalt, betekent dit dat de service wel draait, maar niet op die poort luistert, of dat deze alleen op 127.0.0.1 luistert in plaats van op 0.0.0.0. Dat laatste komt vaak voor bij ontwikkelservers; de oplossing ligt in het bind-adres van de applicatie, niet in Docker.

Nog een fout die op een Docker-bug lijkt: als containers onderling kunnen communiceren, maar een machine op uw kantoor- of VPN-netwerk niet kunnen bereiken, overlapt het Docker-subnet waarschijnlijk dat netwerk. Docker wijst standaard adressen toe vanaf 172.17.0.0/16. Verplaats de pool in /etc/docker/daemon.json:

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

Voer daarna sudo systemctl restart docker uit en maak de betreffende netwerken opnieuw aan, omdat een bestaand netwerk het subnet behoudt waarmee het is aangemaakt.

FAQ

Waarom kunnen mijn containers elkaar niet bereiken via de servicenaam?

Ze bevinden zich niet op hetzelfde netwerk. Compose plaatst elke service automatisch op <project>_default, maar zodra u een networks:-lijst toevoegt aan een service, wordt die lijst de volledige set netwerken voor die service en vervalt het standaardnetwerk. Voer docker network inspect <network> uit en controleer of beide containers in het Containers-blok verschijnen. Controleer ook of geen van de services network_mode: host gebruikt, aangezien een container in host-modus zich op geen enkel Docker-netwerk bevindt en servicenamen niet kan omzetten.

Moet ik poorten publiceren zodat de ene service de andere kan bereiken?

Nee. Op een Compose-netwerk is elke poort van elke container bereikbaar voor de andere containers op dat netwerk. ports: bestaat uitsluitend om een container bloot te stellen aan verkeer van buiten Docker, en expose: is enkel documentatie. Het publiceren van een databasepoort is een veelvoorkomende en kostbare gewoonte, omdat de database hiermee op de publieke interface van uw server wordt geplaatst.

Wat is het verschil tussen bridge- en host-netwerken?

Bridge geeft de container een eigen netwerk-namespace en adres op een virtuele switch, met automatische naamomzetting tussen containers en vertaald uitgaand verkeer. Host geeft de container direct toegang tot de netwerkstack van de host: geen apart adres, geen omzetting via servicenaam, geen poortpublicatie en geen isolatie van andere processen op de host. Bridge is de standaard en de juiste keuze, tenzij het proces de interfaces van de host nodig heeft.

Hoe verbind ik containers uit twee verschillende Compose-bestanden?

Maak een gedeeld netwerk aan met docker network create edge, declareer dit vervolgens in beide bestanden met external: true en koppel de services die met elkaar moeten communiceren. Compose zal dit netwerk niet aanmaken of verwijderen. Als u de aanmaakstap overslaat, weigert Compose te starten en wordt gemeld dat het netwerk als extern is gedeclareerd maar niet is gevonden.

Waarom is mijn container bereikbaar vanaf het internet terwijl UFW de poort blokkeert?

Omdat een gepubliceerde poort wordt afgehandeld door de forwarding-regels die Docker toevoegt aan iptables. Die regels worden eerder gematcht dan die van UFW, en doorgestuurd verkeer passeert sowieso niet de chain die UFW filtert. Bind de host-zijde aan loopback met "127.0.0.1:8080:80" en plaats alles wat publiek toegankelijk moet zijn achter een reverse proxy op poort 80 en 443.