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:
- wat Docker port mapping precies doet (en waar het mis kan gaan);
- hoe je snel achterhaalt wie een poort bezet;
- hoe je conflicten oplost met concrete commando’s (Linux/macOS/Windows);
- hoe je problemen voorkomt met Compose, reverse proxies en goede poortstrategieën;
- hoe je lastige gevallen debugt (IPv4/IPv6, rootless Docker, host networking, userland proxy).
Alles is in Markdown, met echte commando’s die je kunt kopiëren.
Inhoudsopgave
- Wat betekent “port already in use” in Docker?
- Hoe werkt port mapping in Docker?
- De fout reproduceren (en begrijpen)
- Stap 1: Welke container gebruikt de poort?
- Stap 2: Welk hostproces gebruikt de poort?
- Stap 3: Conflicten oplossen (praktische oplossingen)
- Docker Compose: veelvoorkomende valkuilen en fixes
- Geavanceerde oorzaken: IPv6, 0.0.0.0 vs 127.0.0.1, rootless, host network
- Preventie: poortstrategie, documentatie en tooling
- 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:
- op de host poort 8080 te luisteren;
- verkeer door te sturen naar de container poort 80.
Als poort 8080 op de host al in gebruik is, kan Docker geen listener openen en krijg je een fout in de trant van:
Error starting userland proxy: listen tcp4 0.0.0.0:8080: bind: address already in useBind for 0.0.0.0:8080 failed: port is already allocatedlisten tcp :8080: bind: address already in use
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:
- Host:
HOST_IP:HOST_PORT - Container:
CONTAINER_IP:CONTAINER_PORT
Voorbeeld:
docker run -d --name web -p 127.0.0.1:8080:80 nginx
- Docker luistert dan alleen op
127.0.0.1:8080(localhost). - Verkeer naar
localhost:8080gaat naar containerwebpoort 80.
Waarom 0.0.0.0 vaak in de foutmelding staat
Als je -p 8080:80 gebruikt, is dat impliciet:
0.0.0.0:8080 -> container:80(IPv4)- en vaak ook
:::8080 -> container:80(IPv6), afhankelijk van je systeem
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'
-llistening-tTCP-nnumeriek-pprocesinfo
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:
- je ontwikkelt lokaal en poort maakt niet uit;
- je draait meerdere instanties naast elkaar.
Wanneer dit minder slim is:
- je hebt clients die hardcoded
:8080verwachten; - je wil een vaste poort voor documentatie of integraties.
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:
- 1 hostpoort voor HTTP (80)
- 1 hostpoort voor HTTPS (443)
- reverse proxy routeert op hostname of path
Voorbeeld met Nginx-proxy container (conceptueel):
- proxy publiceert
80:80en443:443 - apps publiceren geen hostpoorten, alleen interne containerpoorten
- proxy praat met apps via het Docker netwerk
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:
- service A:
8080:80 - service B:
8081:80
Of publiceer maar één service en laat de rest intern.
2) Gebruik expose voor interne poorten
In Compose:
ports:= publiceer naar hostexpose:= alleen zichtbaar voor andere services op hetzelfde netwerk
Als je database niet vanaf je host hoeft:
- gebruik geen
ports: "5432:5432" - maar alleen
expose: "5432"(of zelfs niets; veel images luisteren al)
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:
LISTEN ... [::]:8080 ...
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:
- performance of speciale netwerktools
- services die moeilijk te NATten zijn
Wanneer gevaarlijk:
- je verliest isolatie
- poortconflicten worden veel waarschijnlijker
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:
- 3000–3999: frontend dev servers
- 5000–5999: API’s
- 8000–8999: tools/dashboards
- 9000–9999: reverse proxies/monitoring
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:
-
Welke hostpoort is het?
Lees de fout: meestal0.0.0.0:XXXX. -
Is het een container?
docker ps --format 'table {{.Names}}\t{{.Ports}}'Stop/verwijder de container of kies een andere hostpoort.
-
Is het een hostproces?
Linux:sudo ss -ltnp | grep ':XXXX'macOS:
sudo lsof -iTCP:XXXX -sTCP:LISTEN -n -PWindows:
Get-NetTCPConnection -LocalPort XXXX -State Listen -
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.
- andere hostpoort (
-
Compose?
Controleerdocker compose ps,docker compose down, en dubbeleports:.
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)
-
“Maar ik heb
EXPOSE 80, waarom werktlocalhost:80niet?”
EXPOSEpubliceert niet. Je hebt-p 80:80nodig. -
“Ik heb de container gestopt, maar poort blijft bezet.”
Controleer of er niet nog een andere container draait, of een hostproces. Gebruikdocker psenss/lsof. -
“Waarom zegt Docker
0.0.0.0:8080terwijl iklocalhostgebruikte?”
Als je-p 8080:80gebruikt, bindt Docker standaard op alle interfaces. Wil je alleen localhost, gebruik-p 127.0.0.1:8080:80. -
“Ik wil twee containers op dezelfde hostpoort.”
Dat kan niet op hetzelfde IP/protocol. Je kunt wel:- verschillende hostpoorten gebruiken (8080 en 8081), of
- een reverse proxy gebruiken op 80/443 en routeren op hostname/path.
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