Skip to content

Custom Networks and Container Isolation

Putting every container on the same network means all of them can reach each other — frontend and database, or one microservice and another microservice that should have no reason to know about it. Network segmentation is a zero-cost security control: containers on different networks cannot reach each other unless explicitly connected to both.

Creating a separate network per tier of your application:

  • Limits communication to only what is necessary.
  • Reduces the blast radius if a container is compromised.
  • Makes the architecture explicit and self-documenting.

docker network create uses the bridge driver by default, so you can omit --driver bridge for brevity. Create two networks for a frontend and a backend:

Terminal window
docker network create frontend-net
docker network create backend-net
Terminal window
docker run -d --name frontend --network frontend-net nginx:alpine
docker run -d --name database --network backend-net -e POSTGRES_PASSWORD=example postgres:alpine

frontend and database are on different networks, so they cannot reach each other by default.

A typical three-tier application takes this further with three networks:

NetworkContainers attachedCan reach
frontend-netnginx-proxy, web-appEach other only
backend-netweb-app, api-serverEach other only
data-netapi-server, postgresEach other only

web-app is connected to both frontend-net and backend-net, so it bridges the two tiers. postgres is on data-net only — the nginx proxy cannot reach the database directly, even if compromised.

Terminal window
docker exec frontend ping -c 2 database || echo "Cannot reach database"

The ping fails as expected, because frontend is on frontend-net and database is on backend-net.

Connecting a container to an additional network

Section titled “Connecting a container to an additional network”

A running container can be attached to another network without restarting it:

Terminal window
docker network connect backend-net frontend

frontend now has interfaces on both frontend-net and backend-net, and can resolve and reach database by name:

Terminal window
docker exec frontend ping -c 2 database
Terminal window
# Detach frontend from backend-net again
docker network disconnect backend-net frontend
# Filter docker network ls to just bridge networks
docker network ls --filter driver=bridge
# Inspect a network's driver, subnet, gateway, and attached containers
docker network inspect frontend-net

A network cannot be removed while containers are attached to it — stop and remove the containers first, then remove the network:

Terminal window
docker network rm frontend-net
Terminal window
docker stop frontend database
docker rm frontend database
docker network rm frontend-net backend-net
# Create two isolated networks
docker network create frontend-net
docker network create backend-net

# Run containers on separate networks
docker run -d --name frontend --network frontend-net nginx:alpine
docker run -d --name database --network backend-net -e POSTGRES_PASSWORD=example postgres:alpine

# frontend cannot reach database (different networks)
docker exec frontend ping -c 2 database || echo "Cannot reach database"

# Connect frontend to backend-net so it can reach the database
docker network connect backend-net frontend

# Now frontend can reach database
docker exec frontend ping -c 2 database

# Inspect a network
docker network inspect frontend-net

# Disconnect
docker network disconnect backend-net frontend

# Cleanup
docker stop frontend database && docker rm frontend database
docker network rm frontend-net backend-net
OptionBenefitCost
Put everything on one networkFast to set up, no network topology to think aboutEvery container can see and reach every other one, needlessly widening the attack surface
Separate networks per tier (frontend/backend/data)Limits communication to what is necessary, reduces the impact of a compromised containerHave to manage several networks and connect/disconnect containers yourself
  • Putting unrelated containers on the same network (or leaving them on the default bridge), so a database becomes reachable from any container running on the same machine.
  • Forgetting that docker network connect only adds a network — you still have to docker network disconnect it yourself; a container does not drop it automatically when it stops.
  • Reusing the same network across every environment (dev/staging/prod), so containers from different environments end up mixed together unintentionally.

💡 Real-world example

Docker Compose automatically creates a separate user-defined network per project (named after the project directory, e.g. myapp_default), so services in one project cannot collide with or see containers from another project on the same dev machine — the standard pattern for multi-project local development.

Two containers are on different user-defined bridge networks. Can they communicate by default?
You have a running container named 'api' and you want to connect it to an existing network named 'data-net' without restarting it. Which command do you use?
You try to run docker network rm mynet but get an error saying the network has active endpoints. What must you do first?
In a three-tier app, you want the database container to be reachable by the API server but not by the frontend proxy. What is the correct network design?