Docker Networking: Connect Containers

By 

Published on

10 min read

Docker networking diagram connecting containers through bridge networks

Once you move past single-container tests, the next problem is communication. A web application needs to reach its database, a reverse proxy needs to forward traffic to an app server, and none of those internal services should be exposed to the public internet by accident.

Docker networking handles that traffic. The defaults work for quick tests, but production-like setups are easier to manage when you understand networks, drivers, DNS names, and published ports. This guide explains how Docker networking works and walks through practical examples for connecting and isolating containers.

How Docker Networking Works

When the Docker daemon starts, it creates three networks automatically: bridge, host, and none. You can see them with docker network ls:

Terminal
docker network ls
output
NETWORK ID     NAME      DRIVER    SCOPE
b84a2c1f9d8e   bridge    bridge    local
c1f3a7b2d4e5   host      host      local
e7d6a8c2b1f4   none      null      local

Each network uses a driver that decides how containers on that network communicate:

  • bridge - Creates an isolated virtual network on one Docker host.
  • host - Removes network namespace isolation and uses the host network directly.
  • none - Starts the container with only a loopback interface.

The bridge driver is the default and works for most single-host applications. For anything beyond a quick test, create a user-defined bridge network instead of relying on the default bridge network. User-defined networks give containers automatic DNS resolution by name and keep unrelated services separated.

The Default Bridge Network

When you start a container without specifying a network, Docker attaches it to the default bridge network. Containers on this network can reach each other by IP address, but not by container name, which makes configuration brittle.

Start two containers on the default network:

Terminal
docker run -d --name default_web1 nginx
docker run -d --name default_web2 nginx

Inspect the default bridge to see both containers and their IP addresses:

Terminal
docker network inspect bridge

The output includes a Containers section listing each container with its assigned IP address. Containers on the default bridge can communicate with those addresses, but a name such as default_web2 does not resolve automatically from default_web1. That is the main reason to avoid the default bridge for application stacks.

Creating a User-Defined Bridge Network

User-defined bridge networks solve the name resolution problem and let you group related containers together. Create one with docker network create:

Terminal
docker network create app_net
output
a1b2c3d4e5f67890123456789abcdef0

Now run containers on that network by passing --network:

Terminal
docker run -d --name net_web --network app_net nginx
docker run -d --name net_client --network app_net alpine sleep 3600

From inside net_client, you can reach net_web by container name:

Terminal
docker exec -it net_client ping -c 2 net_web
output
PING net_web (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.098 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.074 ms

The embedded Docker DNS server resolves the container name to the correct IP address. If you add or remove containers, the DNS records update automatically, so you do not have to track container IP addresses yourself.

Publishing Ports to the Host

Containers on a bridge network are reachable from other containers on the same network, but not from outside the Docker host unless you publish a port. Use -p host_port:container_port with docker run:

Terminal
docker run -d --name port_web -p 8080:80 nginx

Nginx listens on port 80 inside the container, and Docker forwards traffic from port 8080 on the host to it. A browser pointed at http://localhost:8080 reaches the container.

You can publish multiple ports by repeating the flag:

Terminal
docker run -d --name port_app -p 8081:80 -p 8443:443 nginx

By default, Docker publishes the port on all host interfaces. To bind only to a specific host interface, prefix the host port with the address:

Terminal
docker run -d --name loopback_web -p 127.0.0.1:8082:80 nginx

This binds the port to the loopback interface only. The container is reachable from the host itself, but not from other machines on the network.

Connecting a Running Container to a Network

A container is not locked to the network it was started on. You can attach a running container to an additional network with docker network connect:

Terminal
docker network connect app_net default_web1

After connecting, default_web1 has an IP address on both its original network and app_net. Containers on either network can reach it through the network they share. This is useful when a service needs to talk to more than one group of containers, for example a shared logger or monitoring agent.

To remove a container from a network, use docker network disconnect:

Terminal
docker network disconnect app_net default_web1

Isolating Containers with Separate Networks

Multiple user-defined networks act as separate network segments. Containers on different networks cannot reach each other unless one of them is explicitly connected to both. This makes it simple to isolate a frontend tier from a database tier.

Create two networks:

Terminal
docker network create frontend_net
docker network create backend_net

Start a database on backend_net, an application container on both networks, and a web server on frontend_net:

Terminal
docker run -d --name db_net --network backend_net -e POSTGRES_PASSWORD=localpass postgres:16-alpine
docker run -d --name app_net_demo --network backend_net alpine sleep 3600
docker network connect frontend_net app_net_demo
docker run -d --name web_net --network frontend_net -p 8083:80 nginx

The web_net container can reach app_net_demo over frontend_net, and app_net_demo can reach db_net over backend_net. The web_net container cannot reach db_net directly because they do not share a network. If the web container is compromised, it cannot open a connection to the database without going through the application layer.

Warning
The PostgreSQL password above is only a local demo value. Use strong secrets for real services, and do not store production credentials in shell history, Compose files, or source control.

The host Network Driver

The host driver skips network namespace isolation and attaches the container directly to the host network. The container shares the host network interfaces, so any port it listens on is immediately available on the host:

Terminal
docker run --rm -d --name host_nginx --network host nginx

Nginx is now reachable on port 80 of the host with no port publishing. In host mode, -p, --publish, and --publish-all are ignored because there is no separate container network namespace to map from.

Use host networking only when you specifically need it, such as for very high port counts or when the application must bind directly to host interfaces. The trade-off is weaker network isolation and possible port conflicts with services already running on the host.

Host networking is native to Docker Engine on Linux. Docker Desktop supports host networking in version 4.34 and later, but it must be enabled in Docker Desktop settings.

The none Network Driver

The none driver disables external networking for the container. The container gets a loopback interface and nothing else:

Terminal
docker run --rm --network none alpine ip addr

This is useful for batch jobs that process local files and should not reach the network.

Inspecting Networks

docker network inspect prints the full configuration of a network in JSON:

Terminal
docker network inspect app_net

The output includes the subnet, gateway, driver options, and a list of every container attached to the network with its IPv4 and IPv6 addresses. When a container cannot reach another container, this is usually the first place to look.

For a quick overview across all bridge networks, use a filter:

Terminal
docker network ls --filter driver=bridge

You can also inspect a container to see its network attachments:

Terminal
docker inspect net_web

Look for the NetworkSettings.Networks section in the output.

Removing Networks

Unused networks remain on the host until you remove them. Delete a single network with docker network rm:

Terminal
docker network create temp_net
docker network rm temp_net

The removal command fails if any containers are still attached. Stop or disconnect those containers first, then retry.

To clean up every user-defined network that is not in use, run:

Terminal
docker network prune

Docker asks for confirmation and then removes idle user-defined networks. The built-in bridge, host, and none networks are not removed.

Docker Compose Networking

When you run applications with Docker Compose , Compose creates a dedicated default network for each project and attaches every service to it. Services reach each other by service name without extra configuration:

docker-compose.ymlyaml
services:
  web:
    image: nginx
    ports:
      - "8080:80"

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}

Define the password in a local .env file:

.envtxt
POSTGRES_PASSWORD=change-this-password

From the web service, db resolves to the database container IP address. You can also declare multiple named networks under a top-level networks key and attach services selectively, which mirrors the frontend and backend split shown earlier.

Warning
Do not commit .env files or real passwords to version control. Add .env to .gitignore, and use secret management from your platform for production credentials.

Quick Reference

For a printable quick reference, see the Docker cheatsheet .

CommandDescription
docker network lsList Docker networks
docker network create app_netCreate a user-defined bridge network
docker run --network app_net imageStart a container on a network
docker network connect app_net containerAttach a running container to a network
docker network disconnect app_net containerRemove a container from a network
docker network inspect app_netShow network configuration and attached containers
docker run -p 8080:80 imagePublish container port 80 on host port 8080
docker run -p 127.0.0.1:8080:80 imagePublish a port only on the host loopback interface
docker run --network host imageRun a container on the host network
docker run --network none imageRun a container with no external network access
docker network rm app_netRemove an unused network
docker network pruneRemove all unused user-defined networks

Troubleshooting

Containers cannot reach each other by name
Name-based DNS works on user-defined networks. If you started the containers on the default bridge network, recreate them on a user-defined network, or attach them with docker network connect.

Port is already allocated when publishing
Another process on the host is already using that port. Pick a different host port, or find and stop the conflicting process with lsof -i :PORT .

Cannot remove a network
The network still has containers attached. List them with docker network inspect NAME, disconnect or stop them, and try again.

Service is unreachable from the host after publishing a port
Check that the service inside the container is listening on 0.0.0.0, not only on 127.0.0.1. A service bound to the container loopback interface is not reachable through the published port.

The -p option does not work with host networking
Published ports are ignored when a container uses --network host. The container is already using the host network directly, so it must listen on the desired host port itself.

FAQ

What is the difference between a bridge and a host network?
A bridge network creates an isolated virtual network for containers and requires port publishing to expose services on the host. A host network attaches the container directly to the host network, with no separate container network namespace and no port publishing.

Do containers on the same user-defined bridge network need published ports to talk to each other?
No. Port publishing controls access from the host or external network. Containers on the same user-defined network reach each other directly on the container port.

Can two containers share the same name on different networks?
No. Container names are unique per Docker daemon, regardless of how many networks they are attached to. Network-scoped aliases, set with --network-alias, can overlap across networks.

Why does ping not work from some containers?
Some minimal images do not include ping. Alpine includes it by default, which makes it useful for quick network tests. Other images may require installing the relevant package, or you can use a temporary troubleshooting image.

Conclusion

Docker networking becomes easier to manage when each application gets its own user-defined network and only public-facing services publish ports. For more Docker basics, see the docker run and docker exec guides.

Linuxize Weekly Newsletter

A quick weekly roundup of new tutorials, news, and tips.

About the authors

Dejan Panovski

Dejan Panovski

Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 800+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.

View author page