Docker Networking Explained
Bridges, Ports, and Host Communication
By Locallhost.dev Team •
Updated: January 2026 •
12 min read
One of the most confusing parts of moving to Docker is networking. "It works on my machine, but not in the container." This usually happens because containers are isolated worlds. Let's bridge that gap.
The Concept: Container Isolation
Imagine a container as a separate mini-computer running inside your laptop. It has:
- Its own file system.
- Its own process list.
- Its own network interface (ETH0) and IP address.
This means if your app listens on port 3000 inside the container, your laptop (the host) cannot see it by default. You have to open a door.
Port Mapping (The Magic Flag)
To let traffic in, we use the -p flag.
docker run -p 8080:80 nginx
Syntax: -p [HOST_PORT]:[CONTAINER_PORT]
- 8080 (Host): The port you type in your browser (localhost:8080).
- 80 (Container): The port the app inside listens on (Nginx defaults to 80).
Your Browser -> localhost:8080 (Laptop) -> [ Docker NAT ] -> 172.17.0.2:80 (Container)
Bridge Networks
By default, Docker containers run on a "Bridge" network. They can talk to each other if they know each other's IP, but IPs change.
To fix this, we create a custom bridge network:
docker network create my-net
docker run --network my-net --name api my-api-image
docker run --network my-net --name db my-db-image
Now, the API container can connect to the DB using the hostname db instead of an IP address! Docker handles the DNS resolution internally.
Network Modes (Bridge, Host, None, Overlay)
- bridge (default): Containers are isolated with NAT. You expose ports with
-p.
- host: Container shares the host network stack (Linux only). No port mapping needed.
- none: No networking. Useful for batch jobs or security-hardening.
- overlay: Multi-host networking (Docker Swarm/Kubernetes).
Use host mode carefully: It removes isolation and can create port conflicts.
Accessing the Host (host.docker.internal)
A common scenario: You have a Frontend in Docker, but your Backend is running locally on your Mac's VS Code on port 5000.
If your Docker container tries to fetch http://localhost:5000, it will fail. Why? Because localhost inside the container means the container itself.
The Solution:
Use host.docker.internal (Mac & Windows) or 172.17.0.1 (Linux default gateway).
So your API call inside Docker becomes: fetch('http://host.docker.internal:5000/api')
Docker Compose Networking
Docker Compose makes this even easier. It automatically creates a network for your project.
# docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- "3000:3000"
database:
image: postgres
In this setup:
- The
web service can reach the database at hostname database.
- No manual network creation needed.
- Port 3000 is exposed to your laptop, but the database port is only exposed internally to the web service (secure!).
Troubleshooting Checklist
- Container running? Check
docker ps for status.
- Port mapped? Ensure
-p or Compose ports: is set correctly.
- Service binding? Your app must bind to
0.0.0.0, not 127.0.0.1 inside the container.
- Network mismatch? Containers must be on the same network to resolve each other by name.
docker inspect | grep -A 5 Networks
docker network ls
docker network inspect my-net
Security Best Practices
- Expose only necessary ports to the host.
- Keep databases internal to the Compose network.
- Use non-root users inside containers when possible.
- Avoid host networking unless absolutely required.