← Terug naar tutorials

Docker-fout oplossen: "port already in use" en conflicten met port mapping

dockerport already in useport mappingtroubleshootingdocker composenetwerkenbeginnerswindowsmacoslinux

Docker-fout oplossen: “port already in use” en conflicten met port mapping

De foutmelding bind: address already in use, port is already allocated of in gewone taal “port already in use” is één van de meest voorkomende problemen bij Docker. Het betekent dat Docker (of een container) een poort op je hostmachine wil gebruiken die al bezet is door een ander proces—dat kan een andere container zijn, maar ook een lokale service zoals Nginx, Apache, PostgreSQL, een IDE, of zelfs een “vergeten” achtergrondproces.

In deze tutorial leer je:

Alles is in Markdown, met echte commando’s die je kunt kopiëren.


Inhoudsopgave

  1. Wat betekent “port already in use” in Docker?
  2. Hoe werkt port mapping in Docker?
  3. De fout reproduceren (en begrijpen)
  4. Stap 1: Welke container gebruikt de poort?
  5. Stap 2: Welk hostproces gebruikt de poort?
  6. Stap 3: Conflicten oplossen (praktische oplossingen)
  7. Docker Compose: veelvoorkomende valkuilen en fixes
  8. Geavanceerde oorzaken: IPv6, 0.0.0.0 vs 127.0.0.1, rootless, host network
  9. Preventie: poortstrategie, documentatie en tooling
  10. Checklist: snel van fout naar oplossing

Wat betekent “port already in use” in Docker?

Wanneer je een container start met een port mapping zoals:

docker run -p 8080:80 nginx

vraag je Docker om:

Als poort 8080 op de host al in gebruik is, kan Docker geen listener openen en krijg je een fout in de trant van:

Belangrijk: het gaat bijna altijd om de hostpoort (links van de dubbele punt). De containerpoort (rechts) kan in meerdere containers tegelijk bestaan; containerpoorten zijn “privé” binnen hun eigen netwerk-namespace.


Hoe werkt port mapping in Docker?

Het basisconcept

Docker containers draaien in een eigen netwerknamespace. Een container kan bijvoorbeeld intern poort 80 open hebben, maar vanaf de host is die poort niet automatisch bereikbaar. Port mapping maakt een brug:

Voorbeeld:

docker run -d --name web -p 127.0.0.1:8080:80 nginx

Waarom 0.0.0.0 vaak in de foutmelding staat

Als je -p 8080:80 gebruikt, is dat impliciet:

0.0.0.0 betekent: “bind op alle interfaces”. Dat vergroot de kans op conflicten, maar is ook vaak gewenst voor servers.

Publiceren is niet hetzelfde als EXPOSE

In een Dockerfile kun je:

EXPOSE 80

Dat is documentatie/metadata: “deze container verwacht poort 80”. Het publiceert niets op de host. Publiceren doe je met -p of in Compose met ports:.


De fout reproduceren (en begrijpen)

Scenario: je hebt al iets op poort 8080

Start eerst iets op 8080, bijvoorbeeld een simpele Python server:

python3 -m http.server 8080

In een tweede terminal:

docker run --rm -p 8080:80 nginx

Je krijgt dan een bind-fout, omdat Python al luistert op 8080.

Scenario: een andere container gebruikt de poort

Start een container die 8080 claimt:

docker run -d --name first -p 8080:80 nginx

En daarna:

docker run --rm -p 8080:80 nginx

Ook dan: conflict—maar nu is de “dader” een container.


Stap 1: Welke container gebruikt de poort?

1.1 Toon alle port mappings

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

Zoek naar iets als 0.0.0.0:8080->80/tcp.

Je kunt ook specifieker filteren:

docker ps --format '{{.Names}} {{.Ports}}' | grep -E '(:|->)8080'

1.2 Inspecteer een specifieke container

Als je vermoedt dat container first de poort pakt:

docker inspect first --format '{{json .NetworkSettings.Ports}}' | jq

Zonder jq:

docker inspect first --format '{{.NetworkSettings.Ports}}'

1.3 Bekijk welke poorten Docker denkt te gebruiken

Voor een container:

docker port first

Output kan zijn:

80/tcp -> 0.0.0.0:8080

1.4 Oplossing als het een container is

Stop de container:

docker stop first

Verwijder eventueel:

docker rm first

Of kies een andere hostpoort voor je nieuwe container:

docker run --rm -p 8081:80 nginx

Stap 2: Welk hostproces gebruikt de poort?

Als docker ps niets toont dat 8080 gebruikt, dan is het vrijwel zeker een hostproces (een service buiten Docker).

Hieronder staan commando’s per OS.


Linux: ss, lsof, fuser

Met ss (aanrader, standaard op veel distro’s):

sudo ss -ltnp | grep ':8080'

Je ziet iets als:

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

Met lsof:

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

Met fuser:

sudo fuser -n tcp 8080

En met details:

sudo fuser -v -n tcp 8080

macOS: lsof

Op macOS is lsof meestal de snelste:

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

Je krijgt bijvoorbeeld:

node  12345 user  20u  IPv6 0x...  TCP *:8080 (LISTEN)

Let op: *: betekent “alle interfaces”.


Windows: netstat en Get-NetTCPConnection

PowerShell (modern):

Get-NetTCPConnection -LocalPort 8080 -State Listen | Select-Object LocalAddress,LocalPort,OwningProcess

Zoek daarna de procesnaam:

Get-Process -Id (Get-NetTCPConnection -LocalPort 8080 -State Listen).OwningProcess

Klassiek met netstat:

netstat -ano | findstr :8080

Dan:

tasklist /FI "PID eq 12345"

Stap 3: Conflicten oplossen (praktische oplossingen)

Er zijn meerdere “juiste” oplossingen. Welke je kiest hangt af van je doel: wil je per se hostpoort 8080 gebruiken, of is elke vrije poort oké?

Oplossing A: Kies een andere hostpoort

De snelste fix is vaak: verander -p HOST:CONTAINER.

docker run --rm -p 8081:80 nginx

Of met expliciet localhost:

docker run --rm -p 127.0.0.1:8081:80 nginx

Wanneer dit slim is:

Wanneer dit minder slim is:


Oplossing B: Stop/disable het hostproces dat de poort bezet

Voorbeeld: je hebt lokaal Nginx draaien op 80/8080.

Linux (systemd)

Check status:

sudo systemctl status nginx

Stop:

sudo systemctl stop nginx

Voorkom auto-start:

sudo systemctl disable nginx

macOS (Homebrew services)

brew services list
brew services stop nginx

Windows

Zoek de service die luistert (via PID → procesnaam) en stop die in Services of:

Stop-Process -Id 12345 -Force

Let op: force kill is grof; stop liever de service netjes.


Oplossing C: Laat Docker automatisch een vrije hostpoort kiezen

Als je niet om de hostpoort geeft, kun je:

docker run -d -p 80 nginx

Dit betekent: “publiceer containerpoort 80 op een willekeurige vrije hostpoort”.

Bekijk welke poort gekozen is:

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

Of:

docker port <containernaam>

Handig voor testcontainers en CI, minder handig als je mensen wilt laten browsen naar een vaste URL.


Oplossing D: Bind alleen op localhost (vermindert conflicten en exposure)

Als je een poort alleen lokaal nodig hebt:

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

Dit lost geen conflict op als een ander proces óók op 127.0.0.1:8080 luistert, maar het voorkomt conflicten met services die alleen op een extern interface binden (en het is veiliger: geen open poort op je LAN).

Je kunt ook specifiek op een host-IP binden:

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

Oplossing E: Verwijder “spookcontainers” en oude mappings

Soms denk je dat er niets draait, maar er staat nog een container in “Exited” of een Compose project dat deels draait.

Bekijk alles:

docker ps -a

Ruim gestopte containers op:

docker container prune

Ruim ongebruikte netwerken op (kan helpen bij Compose restanten):

docker network prune

Let op: prune verwijdert resources; gebruik het bewust.


Oplossing F: Gebruik een reverse proxy i.p.v. veel losse hostpoorten

Als je veel webservices draait, krijg je snel poortchaos (8080, 8081, 8082…). Een betere aanpak is:

Voorbeeld met Nginx-proxy container (conceptueel):

Dit voorkomt veel “port already in use” situaties omdat je nog maar twee publieke poorten beheert.


Docker Compose: veelvoorkomende valkuilen en fixes

Compose maakt het eenvoudig om poorten te definiëren, maar ook eenvoudig om conflicten te creëren.

1) Conflicterende ports: tussen services

Voorbeeld (fout): twee services claimen dezelfde hostpoort 8080.

docker compose up

Je krijgt dan een bind error.

Fix: geef unieke hostpoorten:

Of publiceer maar één service en laat de rest intern.

2) Gebruik expose voor interne poorten

In Compose:

Als je database niet vanaf je host hoeft:

Je voorkomt dan conflicten met een lokale PostgreSQL op je host.

3) Poorten blijven “bezet” na mislukte start?

Meestal komt dat door een container die toch draait of door een ander proces. Controleer:

docker compose ps
docker ps

Stop alles van dat project:

docker compose down

Als je meerdere Compose projecten hebt met dezelfde service/poort, kunnen ze elkaar bijten.

4) Projectnamen en parallelle stacks

Compose gebruikt een projectnaam (vaak directorynaam). Twee keer dezelfde stack draaien in twee mappen kan alsnog poortconflicten geven als ze dezelfde hostpoorten publiceren.

Je kunt een andere projectnaam gebruiken:

docker compose -p mijnproject2 up -d

Maar let op: hostpoorten blijven uniek moeten zijn.


Geavanceerde oorzaken: IPv6, 0.0.0.0 vs 127.0.0.1, rootless, host network

Soms lijkt alles correct, maar blijft de fout terugkomen. Dan zit het vaak in één van deze details.

1) IPv6 vs IPv4: poort bezet op :: maar niet op 0.0.0.0

Op sommige systemen kan een proces op IPv6 “alle interfaces” binden (::) en daarmee ook IPv4 blokkeren (afhankelijk van dual-stack instellingen). Je ziet dan in ss of lsof iets met IPv6.

Check op Linux:

sudo ss -ltnp | grep 8080

Als je iets ziet als:

dan is de poort bezet, ook al zie je geen 0.0.0.0:8080.

Oplossing: stop het proces of bind Docker expliciet op een specifiek IP (bijv. alleen IPv4 localhost):

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

2) --network host (host networking) omzeilt port mapping

Als je een container start met:

docker run --rm --network host nginx

dan deelt de container de netwerkstack van de host. Er is geen NAT/port mapping; de container luistert direct op hostpoorten. Conflicten worden dan identiek aan “normale” hostprocessen.

Wanneer nuttig:

Wanneer gevaarlijk:

3) Rootless Docker en port restrictions

Bij rootless Docker zijn lage poorten (<1024) vaak lastiger te binden zonder extra configuratie. Dat geeft soms andere errors (permission denied), maar kan ook verwarrend zijn als je poortmapping probeert te veranderen.

Controleer of je rootless draait:

docker info | grep -i rootless

Als je rootless gebruikt en je wil poort 80 binden, kies dan bijvoorbeeld 8080 of configureer rootless port forwarding volgens Docker documentatie.

4) “Userland proxy” en iptables/nftables interacties

Docker gebruikt afhankelijk van platform en instellingen een userland proxy en/of kernel NAT rules. In zeldzame gevallen kan een oude proxy blijven hangen of kan firewallsoftware (UFW, firewalld) roet in het eten gooien.

Handige checks:

docker info | grep -i proxy

En kijk naar Docker daemon logs (Linux):

sudo journalctl -u docker --since "10 minutes ago"

Als je herhaaldelijk rare bind issues ziet, kan een Docker daemon restart helpen:

sudo systemctl restart docker

Let op: dit stopt doorgaans alle containers.


Preventie: poortstrategie, documentatie en tooling

1) Spreek een poortbereik af per type service

Een simpele, effectieve afspraak in teams:

Zo verklein je de kans dat iedereen “toevallig” 8080 pakt.

2) Publiceer alleen wat je echt nodig hebt

Databases, queues en interne backends hoeven vaak niet op de host bereikbaar te zijn. Laat ze in het Docker netwerk en publiceer alleen de edge (reverse proxy of één API).

3) Gebruik 127.0.0.1:HOSTPORT:... voor lokale ontwikkeling

Dit voorkomt dat je per ongeluk services op je LAN openzet en vermindert kans op conflicten met services die alleen extern binden.

Voorbeeld:

docker run -d --name adminer -p 127.0.0.1:8082:8080 adminer

4) Maak een “poortstatus” scriptje

Linux/macOS (bash) voorbeeld om snel te zien wie luistert:

PORT=8080
echo "== Docker containers met poort $PORT =="
docker ps --format '{{.Names}} {{.Ports}}' | grep -E "[:.]$PORT->|:$PORT-" || true

echo "== Host listeners op poort $PORT =="
if command -v ss >/dev/null 2>&1; then
  sudo ss -ltnp | grep ":$PORT" || true
else
  sudo lsof -iTCP:$PORT -sTCP:LISTEN -n -P || true
fi

Checklist: snel van fout naar oplossing

Gebruik deze volgorde om snel te debuggen:

  1. Welke hostpoort is het?
    Lees de fout: meestal 0.0.0.0:XXXX.

  2. Is het een container?

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

    Stop/verwijder de container of kies een andere hostpoort.

  3. Is het een hostproces?
    Linux:

    sudo ss -ltnp | grep ':XXXX'

    macOS:

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

    Windows:

    Get-NetTCPConnection -LocalPort XXXX -State Listen
  4. Fix kiezen:

    • andere hostpoort (-p 8081:80);
    • hostproces stoppen;
    • automatisch poort kiezen (-p 80);
    • alleen localhost binden (-p 127.0.0.1:8080:80);
    • reverse proxy gebruiken.
  5. Compose?
    Controleer docker compose ps, docker compose down, en dubbele ports:.


Praktisch voorbeeld: van fout naar werkende setup

Stel: je wil Nginx draaien op hostpoort 8080:

docker run --rm -p 8080:80 nginx

Je krijgt “port already in use”.

1) Check containers

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

Als je ziet dat myapp al 0.0.0.0:8080->... gebruikt:

docker stop myapp

Start daarna Nginx opnieuw.

2) Geen container? Check hostproces (Linux)

sudo ss -ltnp | grep ':8080'

Stel je ziet:

users:(("node",pid=12345,fd=20))

Stop die node app (afhankelijk van hoe je hem startte). Als het een systemd service is:

sudo systemctl stop my-node-service

3) Of kies een andere poort

Als je node app moet blijven draaien:

docker run --rm -p 8081:80 nginx

Of alleen lokaal:

docker run --rm -p 127.0.0.1:8081:80 nginx

Veelvoorkomende misverstanden (en hoe je ze vermijdt)


Afsluiting

De kern van “port already in use” is altijd: één hostpoort kan maar door één listener tegelijk gebruikt worden (per IP/protocol). Docker port mapping probeert zo’n listener te maken; als iets anders die poort al heeft, faalt het.

Met de commando’s uit deze tutorial kun je systematisch achterhalen of het conflict door een container of door een hostproces komt, en vervolgens de juiste oplossing kiezen: poort aanpassen, proces stoppen, alleen localhost binden, automatisch poort laten kiezen, of overstappen op een reverse proxy-architectuur.

Als je wilt, kun je de exacte foutmelding en de output van deze twee commando’s plakken, dan kan ik gericht meedenken:

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

en (Linux/macOS):

sudo lsof -iTCP:<POORT> -sTCP:LISTEN -n -P