← Terug naar tutorials

Docker-fout oplossen: Bind for 0.0.0.0 failed (poort al in gebruik)

dockerdevopstroubleshootingportsdocker-composelinuxwindowsmacos

Docker-fout oplossen: Bind for 0.0.0.0 failed (poort al in gebruik)

Wanneer je Docker gebruikt om een container te starten, kom je vroeg of laat deze fout tegen:

Deze melding betekent vrijwel altijd: op jouw host (je laptop/VM/server) is die poort al bezet, waardoor Docker de poort niet kan binden aan 0.0.0.0 (alle netwerkinterfaces). In deze tutorial leer je niet alleen hoe je het oplost, maar ook waarom het gebeurt, hoe je het structureel voorkomt, en hoe je de juiste diagnose stelt op Linux, macOS en Windows.


Inhoud

  1. Wat betekent “Bind for 0.0.0.0 failed”?
  2. Snelcheck: welke poort en welke container?
  3. Stap 1: achterhaal welk proces de poort gebruikt
  4. Stap 2: kies een oplossing (met voor- en nadelen)
  5. Docker Compose: typische valkuilen en fixes
  6. Waarom is 0.0.0.0 speciaal? (netwerk-uitleg)
  7. Geavanceerde diagnose: Docker, proxies, IPv4/IPv6 en TIME_WAIT
  8. Best practices om poortconflicten te voorkomen
  9. Checklist: in 60 seconden naar de oplossing

Wat betekent “Bind for 0.0.0.0 failed”?

Wanneer je een container start met port mapping, bijvoorbeeld:

docker run -p 8080:80 nginx:alpine

dan vraag je Docker: “publiceer containerpoort 80 op mijn hostpoort 8080”. Docker probeert dan op de host een socket te openen op 0.0.0.0:8080 (IPv4) en/of :::8080 (IPv6), afhankelijk van je systeeminstellingen. 0.0.0.0 betekent: luister op alle netwerkinterfaces (Wi‑Fi, Ethernet, loopback, VPN, enz.).

Als er al iets luistert op die hostpoort, kan Docker die poort niet claimen. Het OS weigert dat met EADDRINUSE (“address already in use”), en Docker vertaalt dat naar de bekende foutmelding.

Belangrijk: dit is bijna altijd een host-probleem, niet een probleem ín de container. De container kan intern prima op poort 80 luisteren; de conflict zit in de host-port mapping.


Snelcheck: welke poort en welke container?

Lees eerst exact af welke poort in de fout staat. Voorbeelden:

Check ook of je container al bestaat en misschien in een crashloop zit:

docker ps -a

En kijk welke containers poorten publiceren:

docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}"

Als je ziet dat een andere container al 0.0.0.0:8080->80/tcp gebruikt, dan is de oorzaak meteen duidelijk.


Stap 1: achterhaal welk proces de poort gebruikt

De kernvraag: wat gebruikt poort X op de host? Dat kan zijn:

Hieronder per OS de meest betrouwbare commando’s.

Linux

Optie 1: ss (aanbevolen)

sudo ss -lntp | grep ':8080'

Uitleg:

Voorbeeldoutput:

LISTEN 0 4096 0.0.0.0:8080 0.0.0.0:* users:(("node",pid=12345,fd=21))

Dan weet je: node met PID 12345 gebruikt poort 8080.

Optie 2: lsof

sudo lsof -iTCP:8080 -sTCP:LISTEN -n -P

Voorbeeld:

node 12345 user 21u IPv4 ... TCP *:8080 (LISTEN)

Optie 3: netstat (legacy, niet altijd geïnstalleerd)

sudo netstat -tulpn | grep ':8080'

macOS

Op macOS is lsof meestal de snelste:

sudo lsof -iTCP:8080 -sTCP:LISTEN -n -P

Of met netstat:

netstat -anv | grep '\.8080 ' | grep LISTEN

Let op: macOS toont soms niet direct de procesnaam met netstat; lsof is dan handiger.


Windows (PowerShell)

Gebruik PowerShell om de PID te vinden:

netstat -ano | findstr :8080

Voorbeeld:

TCP    0.0.0.0:8080     0.0.0.0:0      LISTENING       15432

Zoek vervolgens het proces bij die PID:

Get-Process -Id 15432

Of uitgebreider:

Get-CimInstance Win32_Process -Filter "ProcessId = 15432" | Select-Object Name, CommandLine

Stap 2: kies een oplossing (met voor- en nadelen)

Als je weet wie de poort gebruikt, kies je de beste fix. Er is zelden “één juiste” oplossing; het hangt af van je doel (lokaal ontwikkelen, productie, security, samenwerking).

Oplossing A: stop of herconfigureer het proces op de host

Als een lokale service de poort gebruikt (bijv. Apache op 80, PostgreSQL op 5432), kun je die stoppen of naar een andere poort verplaatsen.

Linux (systemd)

Stel: nginx gebruikt poort 80:

sudo systemctl status nginx
sudo systemctl stop nginx

Of permanent uitschakelen:

sudo systemctl disable --now nginx

Voor PostgreSQL:

sudo systemctl stop postgresql

macOS (Homebrew services)

brew services list
brew services stop nginx
brew services stop postgresql@16

Windows (Services)

In PowerShell:

Get-Service | Where-Object {$_.Name -like "*nginx*"}
Stop-Service -Name "W3SVC"   # IIS (voorbeeld)

Of via services.msc.

Wanneer kies je dit?

Nadeel


Oplossing B: gebruik een andere host-poort

Dit is de meest voorkomende en vaak veiligste fix: laat de container dezelfde interne poort gebruiken, maar map naar een andere host-poort.

Met docker run

In plaats van:

docker run -p 8080:80 nginx:alpine

gebruik:

docker run -p 8081:80 nginx:alpine

Nu is je app bereikbaar op http://localhost:8081.

Je kunt ook expliciet TCP/UDP kiezen:

docker run -p 8081:80/tcp nginx:alpine

Met Docker Compose

Voorbeeld docker-compose.yml snippet:

services:
  web:
    image: nginx:alpine
    ports:
      - "8081:80"

Toepassen:

docker compose up -d

Wanneer kies je dit?

Nadeel


Oplossing C: stop/verwijder de container die de poort al claimt

Soms is de poort niet bezet door een host-proces, maar door een andere Docker-container die eerder is gestart.

Bekijk containers met poorten:

docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Ports}}"

Stop de container die de poort gebruikt:

docker stop <containernaam-of-id>

Verwijder hem (als je zeker weet dat hij weg mag):

docker rm <containernaam-of-id>

Als het om een Compose project gaat:

docker compose down

Tip: soms draait er een oude Compose stack met dezelfde ports: mapping. Check alle projecten:

docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

Oplossing D: bind alleen op 127.0.0.1 i.p.v. 0.0.0.0

Als je service alleen lokaal nodig is, kun je de binding beperken tot loopback. Dit lost niet elk conflict op (als iets anders ook op 127.0.0.1 luistert), maar het is vooral nuttig voor security en voorspelbaarheid.

docker run

docker run -p 127.0.0.1:8080:80 nginx:alpine

Nu is de service alleen bereikbaar vanaf je eigen machine via localhost:8080, niet vanaf andere devices in je netwerk.

Docker Compose

services:
  web:
    image: nginx:alpine
    ports:
      - "127.0.0.1:8080:80"

Waarom helpt dit soms?

Extra voordeel


Oplossing E: vermijd vaste poorten (dynamische mapping)

Voor sommige workflows hoef je geen vaste hostpoort te kiezen. Je kunt Docker een vrije poort laten kiezen.

docker run -P of -p 0:...

docker run -P nginx:alpine

Docker publiceert dan alle EXPOSE poorten naar willekeurige hostpoorten.

Of specifieker:

docker run -p 0:80 nginx:alpine

Zoek daarna de toegewezen poort:

docker ps --format "table {{.Names}}\t{{.Ports}}"

Voorbeeld:

nginx-container   0.0.0.0:54732->80/tcp

Dan ga je naar http://localhost:54732.

Wanneer kies je dit?

Nadeel


Docker Compose: typische valkuilen en fixes

Compose maakt het makkelijk om poorten te declareren, maar ook makkelijk om per ongeluk conflicten te creëren.

Valkuil 1: meerdere services mappen dezelfde hostpoort

Voorbeeld (fout):

services:
  api:
    ports:
      - "8080:8080"
  admin:
    ports:
      - "8080:3000"

Beide claimen hostpoort 8080. Fix: kies verschillende hostpoorten:

services:
  api:
    ports:
      - "8080:8080"
  admin:
    ports:
      - "8081:3000"

Valkuil 2: je draait dezelfde Compose stack twee keer

Als je in twee mappen hetzelfde composeproject draait met dezelfde poorten, krijg je conflict. Compose gebruikt projectnamen; standaard is dat de mapnaam. Check actieve projecten:

docker compose ls

Stop de stack die je niet nodig hebt:

docker compose -p <projectnaam> down

Of ga naar de juiste map en doe:

docker compose down

Valkuil 3: network_mode: host (Linux)

Met network_mode: host gebruikt de container direct het host-netwerk. Dan gelden poorten alsof het host-processen zijn. Je gebruikt dan meestal geen ports:. Als je toch poortconflicten ziet, komt dat doordat de applicatie in de container zelf probeert te luisteren op een hostpoort die al bezet is.

Voorbeeld:

services:
  app:
    network_mode: host

Dan moet je de app in de container configureren om een vrije poort te gebruiken, of de host-service stoppen.

Valkuil 4: oude containers blijven bestaan

Soms zijn containers gestopt maar niet verwijderd, en Compose kan namen hergebruiken. Gebruik:

docker compose down --remove-orphans

En ruim ongebruikte resources op:

docker system prune

Let op: docker system prune verwijdert ongebruikte containers/netwerken/images; lees de output goed.


Waarom is 0.0.0.0 speciaal? (netwerk-uitleg)

0.0.0.0 is geen “echt” adres waar je naartoe surft; het is een wildcard die betekent: “bind op alle IPv4 interfaces”. Concreet:

Wanneer een proces bindt op 0.0.0.0:8080, claimt het effectief die poort op alle interfaces. Daarom kan een tweede proces niet ook op 0.0.0.0:8080 binden.

Docker port publishing werkt vergelijkbaar: -p 8080:80 betekent doorgaans “publiceer op alle interfaces”, oftewel 0.0.0.0:8080.

Waarom doet Docker dit standaard?

Omdat het voorspelbaar is: je service is bereikbaar via localhost, maar ook via je IP-adres in het netwerk (handig voor testen op mobiel of andere machines). In productie is dit niet altijd gewenst; daarom is expliciet binden op 127.0.0.1 vaak veiliger.


Geavanceerde diagnose: Docker, proxies, IPv4/IPv6 en TIME_WAIT

Soms lijkt het alsof “niemand” de poort gebruikt, maar Docker faalt toch. Dan zijn er een paar minder voor de hand liggende oorzaken.

1) IPv6 binding (:::) vs IPv4 (0.0.0.0)

Op veel systemen luistert een proces op :::8080 (IPv6 wildcard). Afhankelijk van OS-instellingen kan dit ook IPv4 verkeer accepteren (dual stack). Dan lijkt 0.0.0.0:8080 vrij, maar is hij feitelijk bezet.

Check beide:

Linux

sudo ss -lntp | grep 8080

Je ziet dan regels met 0.0.0.0:8080 en/of [::]:8080.

macOS

sudo lsof -iTCP:8080 -sTCP:LISTEN -n -P

2) Docker “userland proxy” en verschillen per platform

Docker kan poorten publiceren via iptables/nftables (Linux) of via een userland proxy. In sommige situaties zie je errors als:

Dit wijst nog steeds op een poortconflict, maar het verklaart waarom de fouttekst soms “userland proxy” noemt.

3) TIME_WAIT is meestal niet de boosdoener (maar soms verwarrend)

TIME_WAIT sockets zijn geen listeners; ze blokkeren normaal gesproken geen bind op een luisterpoort. Maar als een proces nog draait of net herstart, kan het zijn dat het nog steeds luistert, of dat je naar de verkeerde poort kijkt (bijv. UDP vs TCP).

Controleer expliciet op LISTEN:

4) UDP vs TCP

Je kunt een conflict hebben op UDP of TCP. Docker publiceert standaard TCP, tenzij je /udp specificeert. Als jouw app UDP nodig heeft (bijv. DNS, syslog), publiceer dan correct:

docker run -p 5353:5353/udp some-dns-image

En check UDP listeners:

sudo ss -lnup | grep ':5353'

5) “Verborgen” processen: IDE’s, dev servers, achtergrondagents

Veel tools starten automatisch servers:

Als je PID hebt, bekijk de commandline:

Linux

ps -p 12345 -o pid,cmd

macOS

ps -p 12345 -o pid,command

Windows

Get-CimInstance Win32_Process -Filter "ProcessId = 15432" | Select Name, CommandLine

Best practices om poortconflicten te voorkomen

1) Kies een poortconventie per project

Bijvoorbeeld:

Documenteer dit in je README en Compose file.

2) Bind lokaal op 127.0.0.1 voor databases en admin tools

Voorbeeld Compose:

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example
    ports:
      - "127.0.0.1:5433:5432"

Dan is je database niet per ongeluk bereikbaar vanaf je hele netwerk.

3) Publiceer alleen wat je nodig hebt

Als containers onderling praten via Docker-netwerken, hoef je vaak geen ports: te gebruiken. Interne communicatie werkt via service names.

Voorbeeld: je webapp praat met db:5432 via het Compose netwerk. Dan hoef je de database niet naar de host te publiceren.

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example
    # geen ports nodig voor interne communicatie

4) Gebruik .env voor poorten (makkelijk wisselen)

Compose ondersteunt variabelen:

services:
  web:
    image: nginx:alpine
    ports:
      - "${WEB_PORT:-8080}:80"

In .env:

WEB_PORT=8087

Start:

docker compose up -d

5) Ruim regelmatig op

Oude containers en netwerken zorgen niet direct voor poortconflicten als ze niet draaien, maar het helpt om overzicht te houden:

docker ps
docker ps -a
docker network ls
docker volume ls

En opruimen (voorzichtig):

docker system prune

Checklist: in 60 seconden naar de oplossing

  1. Lees de fout: welke hostpoort is het? (bijv. 8080)
  2. Check Docker containers:
    docker ps --format "table {{.Names}}\t{{.Ports}}"
  3. Zoek host-proces op die poort:
    • Linux:
      sudo ss -lntp | grep ':8080'
    • macOS:
      sudo lsof -iTCP:8080 -sTCP:LISTEN -n -P
    • Windows:
      netstat -ano | findstr :8080
      Get-Process -Id <PID>
  4. Kies fix:
    • stop het proces / container, of
    • wijzig hostpoort: -p 8081:80, of
    • bind op loopback: -p 127.0.0.1:8080:80, of
    • gebruik dynamische poort: -p 0:80
  5. Start opnieuw:
    docker compose up -d
    # of
    docker run ...

Praktisch voorbeeld: conflict op poort 5432 (PostgreSQL)

Stel je Compose file:

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret
    ports:
      - "5432:5432"

Je start:

docker compose up

En krijgt:

Bind for 0.0.0.0:5432 failed: port is already allocated

Diagnose (Linux)

sudo ss -lntp | grep ':5432'

Je ziet:

LISTEN 0 244 127.0.0.1:5432 0.0.0.0:* users:(("postgres",pid=912,fd=7))

Dus je host draait al PostgreSQL. Je opties:

  1. Host-Postgres stoppen:
sudo systemctl stop postgresql
  1. Of Docker-Postgres op andere hostpoort:
ports:
  - "5433:5432"

Daarna:

docker compose up -d

En verbinden met:

psql "postgresql://postgres:secret@localhost:5433/postgres"

Praktisch voorbeeld: conflict op poort 80 (Nginx/Apache/IIS)

Je draait:

docker run --rm -p 80:80 nginx:alpine

Fout: poort 80 bezet. Vaak is dat een webserver die automatisch draait.

Linux

sudo ss -lntp | grep ':80'

Als je apache2 of nginx ziet:

sudo systemctl stop apache2
# of
sudo systemctl stop nginx

Of kies een andere hostpoort:

docker run --rm -p 8080:80 nginx:alpine

Windows (IIS)

Poort 80 wordt vaak gebruikt door IIS (W3SVC). Check:

netstat -ano | findstr :80
Get-Process -Id <PID>

Stop IIS service (als dat kan in jouw context):

Stop-Service W3SVC

Of map naar 8080:

docker run --rm -p 8080:80 nginx:alpine

Samenvatting

De fout “Bind for 0.0.0.0 failed (poort al in gebruik)” betekent dat Docker een hostpoort wil claimen die al bezet is. De oplossing is altijd een variant van:

Als je wilt, kan ik je specifieke foutlog en je docker run/docker-compose.yml laten analyseren. Plak dan: