Forward Host Port to Docker Container
Docker containers run in isolated network environments by default, which means services running inside containers aren't accessible from your host machine or the outside world without explicit port forwarding. Understanding how to map ports between your host and containers is essential for accessing web applications, databases, APIs, and other containerized services.
This guide covers everything you need to know about Docker port forwarding, from basic mapping to advanced scenarios.
TLDR
Use -p or --publish to forward ports when running a container: docker run -p 8080:80 nginx maps host port 8080 to container port 80. Use -p 127.0.0.1:8080:80 to bind only to localhost. For running containers, you must commit and restart with new port mappings. Use docker-compose for complex port configurations.
Prerequisites
You need Docker installed and basic familiarity with running containers. Understanding of network ports and how services listen on ports will help.
Basic Port Forwarding
The -p flag maps a host port to a container port.
Syntax
docker run -p <host-port>:<container-port> <image>
Example - running Nginx:
# Map host port 8080 to container port 80
docker run -p 8080:80 nginx
Now access Nginx at http://localhost:8080 on your host machine. Inside the container, Nginx listens on port 80, but you access it via 8080 on your host.
Multiple Port Mappings
Map multiple ports with multiple -p flags:
# Map both HTTP and HTTPS
docker run -p 8080:80 -p 8443:443 nginx
Or forward a range of ports:
# Map ports 8080-8090 to 80-90
docker run -p 8080-8090:80-90 myapp
Publishing All Exposed Ports
Images can declare ports with EXPOSE in their Dockerfile. Use -P (capital P) to automatically map all exposed ports to random high ports on the host:
docker run -P nginx
Check which ports were assigned:
docker ps
Output:
CONTAINER ID IMAGE PORTS NAMES
abc123def456 nginx 0.0.0.0:32768->80/tcp eager_morse
Nginx's port 80 is mapped to host port 32768.
Find the actual port programmatically:
docker port <container-name> 80
Output:
0.0.0.0:32768
Binding to Specific Interfaces
By default, -p 8080:80 binds to all network interfaces (0.0.0.0), making the service accessible from anywhere. Bind to specific interfaces for security:
Localhost Only
# Only accessible from the host machine
docker run -p 127.0.0.1:8080:80 nginx
Now http://localhost:8080 works, but external machines can't connect.
Specific IP Address
# Bind to specific network interface
docker run -p 192.168.1.100:8080:80 nginx
The service is only accessible via the specified IP address.
UDP Port Forwarding
Specify the protocol for UDP services:
# Forward UDP port
docker run -p 5353:53/udp dns-server
# Forward both TCP and UDP
docker run -p 8080:80/tcp -p 5353:53/udp myapp
Docker Compose Port Mapping
In docker-compose.yml, use the ports section:
version: '3'
services:
web:
image: nginx
ports:
- "8080:80"
- "8443:443"
database:
image: postgres
ports:
- "127.0.0.1:5432:5432"
app:
image: myapp
ports:
- "3000-3005:3000-3005"
Start services:
docker-compose up
All port mappings are applied automatically.
Short vs Long Syntax
Docker Compose supports both syntaxes:
Short syntax:
ports:
- "8080:80"
- "127.0.0.1:5432:5432"
Long syntax (more explicit):
ports:
- target: 80 # Container port
published: 8080 # Host port
protocol: tcp
mode: host
- target: 5432
published: 5432
host_ip: 127.0.0.1
Adding Ports to Running Containers
You cannot add port forwarding to a running container directly. You must stop, commit, and restart with new port mappings.
Method 1: Commit and Restart
# Stop the container
docker stop mycontainer
# Commit current state to new image
docker commit mycontainer myapp-with-data
# Remove old container
docker rm mycontainer
# Run with new port mapping
docker run -p 8080:80 --name mycontainer myapp-with-data
Method 2: iptables Port Forwarding
Forward ports using iptables without restarting:
# Get container IP
CONTAINER_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mycontainer)
# Forward host port 8080 to container port 80
sudo iptables -t nat -A DOCKER -p tcp --dport 8080 -j DNAT --to-destination ${CONTAINER_IP}:80
# Allow the forwarded traffic
sudo iptables -t nat -A POSTROUTING -j MASQUERADE -p tcp --source ${CONTAINER_IP} --destination ${CONTAINER_IP} --dport 80
This forwards traffic without restarting the container, but the mapping is lost when Docker restarts.
Method 3: Docker Proxy
Use a reverse proxy container (like Nginx or Traefik) to route traffic to containers without direct port mappings:
# Run application without port mapping
docker run --name myapp myimage
# Run nginx-proxy
docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy
# nginx-proxy automatically detects containers and proxies to them
Checking Port Mappings
View port mappings for a running container:
# Using docker ps
docker ps
# Using docker port
docker port <container-name>
# Using docker inspect
docker inspect <container-name> | grep -A 20 "Ports"
Example output:
"Ports": {
"80/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "8080"
}
]
}
Common Port Mapping Patterns
Web Applications
# Frontend application
docker run -p 3000:3000 react-app
# Backend API
docker run -p 8000:8000 api-server
# Database (localhost only)
docker run -p 127.0.0.1:5432:5432 postgres
Development Environment
# docker-compose.yml
version: '3'
services:
app:
build: .
ports:
- "3000:3000" # Web server
- "35729:35729" # Live reload
volumes:
- .:/app
db:
image: postgres
ports:
- "127.0.0.1:5432:5432"
Microservices
version: '3'
services:
api-gateway:
image: api-gateway
ports:
- "80:8080"
auth-service:
image: auth-service
# No external ports - only accessible via Docker network
user-service:
image: user-service
# No external ports
Only the API gateway is exposed externally. Other services communicate via Docker's internal network.
Troubleshooting Port Conflicts
Port Already in Use
Error:
Error: bind: address already in use
Find what's using the port:
# Linux/macOS
sudo lsof -i :8080
# Or use netstat
sudo netstat -tlnp | grep :8080
# Or ss
sudo ss -tlnp | grep :8080
Solutions:
Use a different host port:
docker run -p 8081:80 nginxStop the conflicting service:
sudo systemctl stop apache2 docker run -p 80:80 nginxKill the process using the port:
kill <PID>
Container Port Not Responding
Check if the service inside the container is actually listening:
# Execute command in running container
docker exec mycontainer netstat -tlnp
# Or check if the port is open
docker exec mycontainer nc -zv localhost 80
If the service isn't running inside the container, the port mapping won't help.
Firewall Blocking Access
If the container is accessible from localhost but not externally, check your firewall:
# Check if Docker added firewall rules
sudo iptables -L -n -v | grep 8080
# Allow the port through ufw
sudo ufw allow 8080/tcp
# Or firewalld
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
Docker Networks and Port Forwarding
Containers on the same Docker network can communicate without port forwarding:
# Create a network
docker network create mynetwork
# Run database (no port forwarding needed)
docker run --network mynetwork --name db postgres
# Run app (connects to 'db' by name)
docker run --network mynetwork --name app -p 8080:80 myapp
Inside myapp, connect to the database at postgresql://db:5432 - no port forwarding needed because they're on the same network.
Host Network Mode
Skip Docker networking entirely and use the host's network:
docker run --network host nginx
In host mode:
- No port mapping needed
- Container uses host ports directly
- Service on container port 80 is accessible at host port 80
- Less isolation but simpler networking
Use case: When you need maximum network performance or when port mapping overhead is a concern.
Best Practices
Don't expose databases publicly: Always bind database ports to localhost:
docker run -p 127.0.0.1:5432:5432 postgres
Use high ports for development: Avoid ports below 1024 which require root:
# Good
docker run -p 8080:80 nginx
# Requires sudo
sudo docker run -p 80:80 nginx
Document your port mappings: In your README or docker-compose.yml comments:
services:
app:
ports:
- "3000:3000" # Web UI
- "9229:9229" # Node.js debugger
- "35729:35729" # Live reload
Use Docker Compose for complex setups: It's easier to manage multiple port mappings in YAML than remembering long docker run commands.
Plan your port allocation: Maintain a list of ports used by different containers to avoid conflicts.
Port forwarding is fundamental to using Docker effectively. Whether you're running a simple web server or a complex microservices architecture, understanding how to map ports between your host and containers lets you expose services securely and accessibly. Use -p for basic mapping, Docker Compose for complex setups, and remember to bind sensitive services like databases to localhost only.
These amazing companies help us create free, high-quality DevOps content for the community
DigitalOcean
Cloud infrastructure for developers
Simple, reliable cloud computing designed for developers
DevDojo
Developer community & tools
Join a community of developers sharing knowledge and tools
Want to support DevOps Daily and reach thousands of developers?
Become a SponsorFound an issue?