One question I get asked a lot by people newer to Docker is: “Why does my container have its own IP address, and why can’t it just use the host’s network directly?” The answer lies in one of the most fundamental — and least explained — pieces of Docker’s architecture: the network namespace. Choosing the right networking namespace mode for a container isn’t just a technical detail; it directly affects security, performance, and how your services discover and talk to each other.
In this guide, I’ll break down every networking namespace option Docker offers, when I actually use each one in real projects, and how to configure them with concrete examples.
What Is a Network Namespace, Really?
Linux network namespaces are a kernel feature that lets you create isolated network stacks — each with its own interfaces, routing tables, iptables rules, and even its own loopback (lo) device. When Docker starts a container, by default it creates a brand-new network namespace for it, then connects that namespace to the host using a virtual ethernet (veth) pair, with one end attached to a bridge.
This is what gives containers the illusion of being separate machines on the network, even though they’re just processes running on the same kernel as everything else on the host.
Docker exposes control over this behavior through the --network (or --net) flag, and the options available are:
bridge(default)hostnonecontainer:<name|id>- Custom user-defined networks (bridge, overlay, macvlan, ipvlan)
Let’s go through each one.
1. Bridge Networking (the Default)
When you run a container without specifying --network, Docker attaches it to the default bridge network, backed by the docker0 Linux bridge interface.
docker run -d --name web1 nginx
docker inspect web1 --format '{{.NetworkSettings.IPAddress}}'
Expected output (your subnet may differ):
172.17.0.2
Each container gets its own network namespace, its own IP on the 172.17.0.0/16 subnet (by default), and can reach the outside world via NAT (as covered in Docker’s iptables/MASQUERADE setup).
When I use it: for local development, quick tests, and anything where I don’t need custom DNS-based service discovery between containers on the default bridge (which doesn’t support automatic hostname resolution the way user-defined networks do).
User-Defined Bridge Networks (Preferred over Default)
The default bridge network doesn’t give you automatic DNS resolution between containers — you’d have to use legacy --link or manually track IPs. A user-defined bridge network fixes this:
docker network create my_app_net
docker run -d --name db --network my_app_net postgres:16
docker run -d --name api --network my_app_net myapp:latest
Now, inside the api container, I can resolve db by name:
docker exec -it api ping -c 2 db
Expected output:
PING db (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: seq=0 ttl=64 time=0.089 ms
This is Docker’s embedded DNS server at work, and it’s one of the biggest reasons I always recommend user-defined networks over the default bridge for anything beyond a single throwaway container.
2. Host Networking
With --network host, Docker skips creating a separate network namespace entirely — the container shares the host’s network stack directly.
docker run -d --network host --name webhost nginx
There’s no port mapping needed here; if nginx listens on port 80 inside the container, it’s listening on port 80 on the host directly. Verify:
curl -I http://localhost:80
Trade-offs I weigh before using host networking:
- Performance: No veth pair, no NAT translation overhead — this matters for high-throughput or latency-sensitive workloads (I’ve used this for high-frequency data ingestion services).
- No network isolation: The container can bind to any port on the host and see all host network interfaces. This is a real security consideration — I never use host networking for untrusted or multi-tenant workloads.
- Port conflicts: Since there’s no port remapping, you have to manually ensure no two containers (or host processes) try to bind the same port.
- Platform limitation: Host networking only works on Linux. On Docker Desktop for Mac and Windows,
--network hostis either unsupported or behaves differently because the containers actually run inside a Linux VM.
3. None (No Networking)
With --network none, Docker still creates a network namespace, but it only contains the loopback interface — no veth pair, no external connectivity at all.
docker run -it --network none --name isolated busybox sh
Inside the container:
ip addr
Expected output:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
When I use it: batch processing jobs that only touch local files or mounted volumes and should never be able to reach the network for security reasons (data processing pipelines, sandboxed script execution, security research containers), or when I want to manually configure networking myself using tools outside Docker’s normal flow.
4. Container Networking Namespace Sharing
With --network container:<name|id>, a new container joins an existing container’s network namespace, rather than getting its own. They share the same IP address, port space, and network interfaces.
docker run -d --name main_app myapp:latest
docker run -d --network container:main_app --name sidecar_debug nicolaka/netshoot
Now sidecar_debug sees exactly what main_app sees on the network — same IP, same open ports.
This is the exact mechanism Kubernetes uses for Pods. In Kubernetes, all containers in a Pod share a network namespace via a hidden “pause” container, which is precisely this container: networking mode under the hood.
When I use it: debugging a running container without installing extra tools inside it (attaching a netshoot container to inspect main_app‘s networking), or building sidecar patterns manually with plain Docker before moving to Kubernetes or Compose.
Example debugging session:
docker run -it --rm --network container:main_app nicolaka/netshoot tcpdump -i eth0
This lets me capture traffic exactly as main_app sees it, without installing tcpdump inside the production image.
5. Overlay Networks (Multi-Host)
For multi-host container communication (Docker Swarm), Docker offers overlay networks, which use VXLAN tunneling to connect containers across different physical or virtual hosts as though they were on the same L2 network.
docker swarm init
docker network create -d overlay my_overlay_net
docker service create --name web --network my_overlay_net --replicas 3 nginx
Verify the network:
docker network inspect my_overlay_net
When I use it: Docker Swarm deployments where services need to communicate across multiple physical nodes without manually managing routing. If you’re on Kubernetes instead, this role is filled by a CNI plugin (Calico, Flannel, Cilium, etc.), not Docker’s overlay driver.
6. Macvlan and Ipvlan Networks
Sometimes I need a container to appear as a genuinely separate device on the physical network, with its own MAC address, reachable directly by other devices on the LAN — no NAT, no port mapping. That’s what macvlan gives you.
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 \
my_macvlan_net
docker run -d --network my_macvlan_net --name iot_sim busybox sleep 3600
The container now gets a real IP on the 192.168.1.0/24 LAN, directly reachable from other machines on that network.
When I use it: simulating IoT devices on a lab network, legacy applications that expect to bind directly to a network interface, or scenarios where NAT would break a protocol (some VoIP and multicast-heavy applications).
ipvlan is similar but shares a single MAC address across all containers (useful when your network switch limits the number of MAC addresses per port):
docker network create -d ipvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 \
-o ipvlan_mode=l2 \
my_ipvlan_net
Choosing the Right Namespace: A Decision Framework
Here’s the mental checklist I run through on every project:
| Requirement | Recommended Mode |
|---|---|
| Local dev, quick test container | bridge (default) |
| Multiple containers needing DNS-based discovery | User-defined bridge |
| Max performance, no isolation needed, trusted workload | host |
| No network access required at all | none |
| Debugging another container’s network stack | container:<name> |
| Multi-host service communication (Swarm) | overlay |
| Container needs a real LAN-routable IP | macvlan / ipvlan |
| Kubernetes workloads | Handled by CNI plugin, not Docker networking directly |
Compose Example Combining Multiple Modes
Here’s a docker-compose.yml I’ve used that combines a user-defined bridge network for app services with an isolated debug sidecar sharing another container’s namespace:
version: "3.9"
services:
db:
image: postgres:16
networks:
- backend
environment:
POSTGRES_PASSWORD: examplepass
api:
image: myapp:latest
networks:
- backend
depends_on:
- db
ports:
- "8080:8080"
debug:
image: nicolaka/netshoot
network_mode: "service:api"
command: sleep infinity
networks:
backend:
driver: bridge
Bring it up:
docker compose up -d
Then jump into the debug container to inspect api‘s exact network view:
docker compose exec debug tcpdump -i eth0 port 8080
Kubernetes Equivalent: Pod Network Namespace Sharing
Since container: networking maps directly onto how Kubernetes Pods work, here’s the same sidecar pattern expressed as a Pod spec:
apiVersion: v1
kind: Pod
metadata:
name: api-with-debug-sidecar
spec:
containers:
- name: api
image: myapp:latest
ports:
- containerPort: 8080
- name: debug
image: nicolaka/netshoot
command: ["sleep", "infinity"]
Both containers here automatically share one network namespace — localhost inside one container reaches the other directly, exactly like the container: mode in plain Docker.
Troubleshooting Namespace-Related Issues
Container can’t reach another container by name: Check whether both are on the same user-defined network — the default bridge does not support automatic DNS.
docker network inspect my_app_net --format '{{range .Containers}}{{.Name}} {{end}}'
Host networking container can’t bind to a port: Something else on the host is already using it.
sudo ss -tulpn | grep :80
Macvlan container can’t reach the host itself: This is expected — by design, most Linux macvlan setups block direct communication between the macvlan container and the host’s own IP on the parent interface. You typically need an extra macvlan interface on the host, or use ipvlan instead, if host-to-container communication is required.
Inspecting namespace details directly:
docker inspect <container> --format '{{.NetworkSettings.SandboxKey}}'
sudo nsenter --net=<sandbox_key_path> ip addr
Best Practices
- Default to user-defined bridge networks for anything with more than one container — the DNS-based discovery alone is worth it.
- Reserve host networking for trusted, performance-critical workloads only, and understand it removes network isolation entirely.
- Use
nonedeliberately as a security control for jobs that should never have network access. - Treat
container:mode as your go-to debugging technique before reaching for heavier tools. - Choose macvlan/ipvlan only when you specifically need Layer 2 network presence — they add real operational complexity (switch configuration, promiscuous mode considerations).
- On Kubernetes, remember namespace choices are abstracted away into Pod specs and CNI plugins — Docker’s raw networking flags don’t directly apply.
Summary
Docker’s network namespace options give you a spectrum of isolation, from fully isolated (none) to fully shared (host), with user-defined bridges as the practical default for most multi-container applications and macvlan/ipvlan for specialized LAN-integration needs. Understanding what each mode actually does at the kernel level — creating, sharing, or skipping a network namespace — makes it much easier to choose correctly instead of guessing, and it directly informs how Kubernetes Pod networking works later on if you move in that direction.
References
- Docker Documentation: Networking overview
- Docker Documentation: Network drivers
- Docker Documentation: Macvlan network driver
- Docker Documentation: Overlay network driver
- Kubernetes Documentation: Cluster Networking
- Linux man-pages: network_namespaces(7)
- CNCF: Cloud Native Networking Landscape
