SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-10-23

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 wat beginners vaak 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. Zo bereikt web de database op de hostnaam db, op poort 5432, zonder enige configuratie.

docker compose exec web getent hosts db

Dit print een regel zoals 172.18.0.2 db. Als er niets wordt geprint, 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 verbindingsreeks moet postgresql://postgres:example@db:5432/postgres zijn. Het hostgedeelte 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 cacht, zal zichzelf vastpinnen aan een dode container. En het verouderde bridge-netwerk dat wordt gebruikt door een standaard docker run zonder --network kan containernamen helemaal niet oplossen: containers daar vinden elkaar alleen via IP-adres, en ze zoeken externe hostnamen op via een kopie van het /etc/resolv.conf-bestand van de host. Dat is de reden waarom advies over containerlinks 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 kosten zijn

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

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

Er zijn gegronde redenen om dit te willen. Een proces dat broadcast- of multicast-verkeer op het lokale netwerk moet zien, zoals apparaatdetectie voor een mediaserver of een domotica-hub, kan dit niet zien vanachter een bridge, omdat de bridge dat verkeer niet doorstuurt naar de container. Een monitoring-agent die de interface-tellers van de host leest, heeft de interfaces van de host nodig. En u slaat de adresvertalingsstap over, wat belangrijk is bij hoge pakketsnelheden.

De kosten zijn specifiek.

ports: stopt met werken. Docker waarschuwt dat gepubliceerde poorten worden genegeerd bij gebruik van host-netwerkmodus, en de container bindt alles wat het proces bindt. Twee containers in host-modus die poort 8080 willen gebruiken, botsen, en de tweede stopt met bind: address already in use.

Naamresolutie via servicenaam is in beide richtingen verdwenen. De container bevindt zich niet op het projectnetwerk, dus hij kan db niet oplossen, en de andere services kunnen hem niet oplossen. Hij kan ze nog steeds bereiken via poorten die op de host zijn gepubliceerd, meestal op 127.0.0.1. Op Linux kan hij ook een bridge-container direct bereiken op het IP-adres van die container op elke luisterende poort, omdat de host open toegang heeft tot containers op zijn bridge-netwerken en een container in host-modus de netwerkstack van de host deelt. Die adressen veranderen wanneer containers opnieuw worden aangemaakt, dus een poort gepubliceerd op loopback is de stabiele route.

Isolatie is verdwenen. Een proces dat 0.0.0.0 bindt binnen een container in host-modus, luistert op elke interface van uw server, inclusief de publieke, precies zoals een pakket dat met apt is geïnstalleerd. Er is één voordeel: dit verkeer volgt het normale invoerpad, dus UFW-regels zijn er wel op van toepassing, wat niet geldt voor gepubliceerde poorten.

Host-modus is een functie van de Linux Docker Engine. Docker Desktop ondersteunt dit pas vanaf versie 4.34 en alleen nadat u het inschakelt, met de verdere 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 helft op Docker Desktop, verwacht dan dat hetzelfde bestand zich anders gedraagt.

Kies voor host-modus wanneer u de interfaces van de host nodig heeft. Kies er niet voor om een verbindingsprobleem op te lossen, omdat het meestal het ene probleem vervangt door een lastiger 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 debugt in vier commando's

Begin met de vraag op welk netwerk elke container zich werkelijk bevindt:

docker network inspect shop_default

Het Containers-blok somt elke gekoppelde container met zijn adres op. Een service die in die lijst ontbreekt, bevindt zich op een ander netwerk, in host-modus, of draait niet.

Test naamresolutie vanaf een tijdelijke container die aan hetzelfde netwerk is gekoppeld, zodat u geen extra tools in uw eigen images nodig heeft:

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 naamresolutie of netwerklidmaatschap. Als nslookup slaagt terwijl nc faalt, zijn de naam en het netwerk in orde en is het probleem de verbinding zelf. Als nc Connection refused rapporteert, is de gebruikelijke oorzaak dat de service niet op die poort luistert, of luistert op 127.0.0.1 binnen de eigen container in plaats van op 0.0.0.0. Dat laatste komt vaak voor bij ontwikkelservers, en de oplossing ligt in het bind-adres van de applicatie, niet in Docker. Als nc blijft hangen en een time-out geeft, zoek dan naar iets dat het verkeer onderweg blokkeert, zoals een firewallregel, of een container die gepauzeerd is of niet reageert.

Nog een fout die op een Docker-bug lijkt. Als containers met elkaar kunnen praten 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.

Was dit artikel nuttig?