Creating Custom Networks
A Docker custom network is a named virtual network that you create instead of relying on Docker’s built-in defaults. It matters because real applications are made of several containers, and those containers need predictable names, controlled reachability, and isolation from unrelated services.
For most single-host projects, a custom bridge network is the right tool. It gives related containers a private network, automatic DNS by name, and a clean boundary where only the ports you publish are reachable from the host.
Overview: How Custom Networks Work
When Docker starts a container, it creates runtime isolation around the process. The image provides read-only filesystem layers, Docker adds a thin writable container layer, and the daemon also prepares namespaces for things like processes, mounts, and networking. The container’s network namespace has its own interfaces, routes, IP addresses, and idea of localhost.
A Docker network is the object that connects one or more container network namespaces together. The most common custom network is a user-defined bridge network. On Linux, Docker creates a virtual bridge device and connects each attached container through a virtual Ethernet pair. On Docker Desktop for macOS and Windows, that Linux bridge exists inside Docker Desktop’s Linux VM, but the Docker CLI behavior is intentionally similar.
User-defined bridge networks are better than the legacy default bridge network for application work. They include Docker’s embedded DNS server, so containers can resolve each other by container name, network alias, or Compose service name. They are also scoped: containers on one custom bridge network are not automatically reachable from containers on another. This lets you put a public-facing web container on a frontend network and a database on a backend network without publishing the database to the host.
Docker assigns IP addresses from a subnet attached to the network. You can let Docker choose the subnet, which is usually best, or provide one with --subnet when you need to avoid overlap with a VPN, office network, or another Docker network. Even when you choose a subnet, avoid coding container IP addresses into applications. Containers are often removed and recreated, and names are the stable service-discovery interface.
Custom networks do not publish ports. A container can listen on port 80 on a private network and still be unreachable from the host until you use -p or Compose ports:. The Dockerfile instruction EXPOSE is only metadata and documentation; it does not create a host port mapping.
Syntax
The general custom network commands are:
docker network create [OPTIONS] NETWORK_NAME
docker run --network NETWORK_NAME [OPTIONS] IMAGE [COMMAND]
docker network connect [OPTIONS] NETWORK_NAME CONTAINER
| Option or command | Meaning |
|---|---|
docker network create NAME |
Create a user-defined bridge network with Docker-selected IP settings. |
--driver bridge |
Use the bridge driver. This is the default for local custom networks. |
--subnet 172.28.0.0/16 |
Choose the address range Docker assigns container IPs from. |
--gateway 172.28.0.1 |
Choose the gateway address for the network. Usually omit this. |
--label key=value |
Add metadata for cleanup scripts, documentation, or automation. |
--network NAME |
Attach a new container to a network when it starts. |
--network-alias NAME |
Add an extra DNS name for a container on that network. |
docker network connect |
Attach an already-created container to another network. |
docker network disconnect |
Detach a container from a network. |
docker network inspect |
Show driver, subnet, gateway, labels, and connected endpoints. |
Other drivers exist. host removes much of the network isolation and is mainly Linux-specific. none gives a container only loopback networking. overlay is for multi-host networking in orchestrated environments such as Swarm. For normal local projects, use a custom bridge network.
Examples
Example 1: Create a private app network
Create a network, run Nginx on it, and reach it from a temporary curl container on the same network:
docker network create app-net
docker run -d --name app-web --network app-net nginx:1.27-alpine
docker run --rm --network app-net curlimages/curl:8.9.1 http://app-web
Output:
app-net
6f0b9d8c2a91
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
The web container and the curl container share app-net, so Docker DNS resolves app-web to the web container’s current IP address. No port is published to the host, so this communication is private to containers attached to the network.
Example 2: Choose a subnet and add a label
Most of the time Docker can choose IP ranges for you. When you need a known non-overlapping range, create the network explicitly:
docker network create --driver bridge --subnet 172.28.0.0/16 --label course=docker custom-subnet-net
docker network inspect custom-subnet-net
Output:
custom-subnet-net
[
{
"Name": "custom-subnet-net",
"Driver": "bridge",
"IPAM": {
"Config": [
{
"Subnet": "172.28.0.0/16"
}
]
},
"Labels": {
"course": "docker"
}
}
]
The subnet controls the address pool for containers attached to this network. The label does not affect connectivity; it is metadata that can help automation identify lesson or project networks later.
Example 3: Use a network alias
A network alias gives one container an additional DNS name on a specific network:
docker network create shop-net
docker run -d --name shop-cache --network shop-net --network-alias cache redis:7.2-alpine
docker run --rm --network shop-net redis:7.2-alpine redis-cli -h cache ping
Output:
shop-net
2c5d61a4f8ab
PONG
The Redis container’s real container name is shop-cache, but other containers on shop-net can also reach it as cache. Aliases are useful when application configuration expects a generic hostname such as database, cache, or api.
Example 4: Put one container on two networks
A gateway or API container sometimes needs to sit between a frontend network and a backend network:
docker network create frontend-net
docker network create backend-net
docker run -d --name gateway --network frontend-net nginx:1.27-alpine
docker network connect backend-net gateway
docker network inspect backend-net
Output:
frontend-net
backend-net
b7c3f2a91d44
[
{
"Name": "backend-net",
"Containers": {
"b7c3f2a91d44": {
"Name": "gateway",
"IPv4Address": "172.23.0.2/16"
}
}
}
]
The gateway container started on frontend-net and was then attached to backend-net. A container can have multiple network interfaces, one per attached network. Use this deliberately: it is powerful, but it also creates a path between network zones.
Example 5: Define custom networks in Compose
Compose can create named networks for a project and attach each service only where it belongs:
services:
proxy:
image: nginx:1.27-alpine
ports:
- "8080:80"
networks:
- frontend
- backend
api:
image: nginxdemos/hello:0.4
networks:
- backend
db:
image: redis:7.2-alpine
networks:
backend:
aliases:
- cache
networks:
frontend:
backend:
internal: true
docker compose up -d
docker compose down
Output:
[+] Running 5/5
✔ Network customnet_frontend Created
✔ Network customnet_backend Created
✔ Container customnet-db-1 Started
✔ Container customnet-api-1 Started
✔ Container customnet-proxy-1 Started
[+] Running 5/5
✔ Container customnet-proxy-1 Removed
✔ Container customnet-api-1 Removed
✔ Container customnet-db-1 Removed
✔ Network customnet_backend Removed
✔ Network customnet_frontend Removed
Only proxy publishes a host port. The api and db services are private to the backend network. The internal: true setting tells Docker that the backend network should not provide external connectivity for attached containers, which is useful for services that should only be reached by other containers in the project.
How It Works Step By Step
- The Docker CLI sends a network-create request to the Docker daemon with the driver, name, labels, and optional IPAM settings.
- The daemon creates the network object. For a bridge network, it prepares a Linux bridge and address pool. On Docker Desktop, this happens inside the Linux VM.
- When you start a container with
--network, Docker creates an endpoint for that container on the network. - Docker connects a virtual interface from the container’s network namespace to the bridge network and assigns an IP address.
- Docker registers container names and aliases with its embedded DNS service for that network.
- Containers on the same user-defined network can connect directly by name. Containers on different networks cannot unless a container is attached to both or traffic is published through the host.
- If you publish a port with
-p, Docker configures host forwarding to the container port. This is separate from the custom network itself. - When a container is removed, Docker removes its network endpoint. The custom network remains until you remove it, unless Compose owns it and you run
docker compose down.
Common Mistakes
Relying on the default bridge for service discovery
This is fragile because the legacy default bridge network does not provide the same automatic name-based DNS behavior as user-defined bridge networks:
docker run -d --name bad-web nginx:1.27-alpine
docker run --rm curlimages/curl:8.9.1 http://bad-web
Create a user-defined network and attach both containers to it:
docker network create good-net
docker run -d --name good-web --network good-net nginx:1.27-alpine
docker run --rm --network good-net curlimages/curl:8.9.1 http://good-web
Publishing a database port for container-to-container traffic
This exposes Redis on the host even though another container could reach it privately:
docker run -d --name public-redis -p 6379:6379 redis:7.2-alpine
Use a custom network instead, and publish only services that host users or host tools truly need:
docker network create private-data-net
docker run -d --name private-redis --network private-data-net redis:7.2-alpine
Hard-coding container IP addresses
A command such as curl http://172.28.0.5 may work once and fail after the container is recreated. Use container names, aliases, or Compose service names. IP addresses are implementation details unless you have a very specific network integration requirement.
Creating overlapping subnets
If a Docker subnet overlaps your VPN or office network, traffic may route to the wrong place or fail in confusing ways. When you choose --subnet, pick a private range that does not overlap the networks your host must reach.
Best Practices
- Create one user-defined bridge network per small standalone application, or separate frontend and backend networks when you need stronger boundaries.
- Let Docker choose subnets unless you have a real routing conflict to solve.
- Use names and aliases for service discovery; do not put container IP addresses in configuration files.
- Publish only edge services such as a web proxy or local development API.
- Keep databases, caches, and internal workers on private networks without host port mappings.
- Use Compose networks for multi-service projects so the network model lives beside the service definitions.
- Attach a container to multiple networks only when it is intentionally bridging application zones.
- Use specific image tags such as
nginx:1.27-alpineandredis:7.2-alpineso examples and deployments are reproducible. - Inspect networks with
docker network inspectbefore guessing at DNS, subnet, or attachment problems. - Remove unused networks with
docker network rmordocker network pruneafter checking that no needed containers depend on them.
Practice Exercises
- Create a network named
notes-net. Runnginx:1.27-alpineasnotes-webon it, then use a one-off curl container on the same network to requesthttp://notes-web. Expected end state: curl receives the Nginx welcome page without publishing a host port. - Create a network named
cache-net. Start Redis with a network alias ofcache, then run a temporary Redis CLI container that sendsPINGto hostnamecache. Hint: use--network-aliason the Redis server container. - Write a
compose.yamlwithfrontendandbackendnetworks. Put a proxy on both networks, an API only on backend, and a database only on backend. Expected end state: only the proxy has aports:entry.
Summary
- Custom Docker networks give related containers private connectivity and predictable DNS names.
- User-defined bridge networks are the standard choice for single-host applications.
docker network createcreates the network;--networkattaches new containers to it.- Network aliases provide additional DNS names inside a specific network.
- Containers can join multiple networks, which is useful for gateways but should be intentional.
- Custom networks do not publish ports; use
-por Composeports:for host access. - Compose can model frontend, backend, and internal networks directly in YAML.
- Use names instead of IP addresses, avoid unnecessary published ports, and inspect networks when debugging.
