· Sysadmin.id · Docker · 7 min read
How to Configure Docker Compose Networks and Set Static IPs
Learn how Docker Compose networking works — from the default bridge network to custom networks, static IP assignment, network aliases, and multi-network setups for real-world infrastructure.
Networking is one of the most important — and most misunderstood — parts of Docker Compose. By default, Docker Compose creates a network for you automatically, but understanding how to control that network gives you precise control over how containers communicate, what IPs they use, and how they’re isolated.
This guide covers everything from the default network behavior to assigning static IPs and building multi-network architectures.
Prerequisites
Before you start, make sure you have:
- Docker Engine installed and running
- Docker Compose v2 (
docker composecommand available) - Basic familiarity with
docker-compose.ymlsyntax
Note: This guide uses Docker Compose v2 syntax (
docker composewithout the hyphen). If you’re on an older system with Compose v1, replacedocker composewithdocker-compose.
How Docker Compose Networking Works by Default
When you run docker compose up, Compose automatically creates a bridge network for your project. Every service defined in your docker-compose.yml is attached to that network and can reach other services by their service name as a hostname.
For example, with this minimal setup:
services:
web:
image: nginx
db:
image: postgresThe web container can connect to the database at hostname db, and db can reach the web server at web. Docker’s internal DNS handles the resolution automatically.
The auto-created network is named <project>_default, where <project> is the directory name of your project.
Step 1: Define a Custom Network
Instead of relying on the auto-created default network, define your own. This gives you control over the network name, driver, subnet, and IP range.
services:
web:
image: nginx
networks:
- appnet
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
networks:
- appnet
networks:
appnet:
driver: bridgeNow both containers share the appnet network and can reach each other by service name. The network name in Docker will be <project>_appnet.
Use an External Name
If you want the Docker network to have a specific name regardless of the project name, use name:
networks:
appnet:
driver: bridge
name: my-app-networkStep 2: Assign a Static IP to a Container
To assign a static IP, you must define a subnet on the network first. Docker needs to know the address pool before it can assign a specific IP within it.
services:
web:
image: nginx
networks:
appnet:
ipv4_address: 172.20.0.10
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
networks:
appnet:
ipv4_address: 172.20.0.11
networks:
appnet:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/24
gateway: 172.20.0.1Key points:
- The
ipamblock defines the IP Address Management config subnetsets the network range — here172.20.0.0/24gives you addresses from172.20.0.1to172.20.0.254gatewayis optional but good to be explicit about- Each container’s
ipv4_addressmust fall within the defined subnet - Reserve
.1for the gateway — start assigning containers from.10or higher to avoid conflicts
Verify the assigned IPs after starting:
docker compose up -d
docker inspect <container_name> | grep IPAddressStep 3: Use Network Aliases
A network alias lets a container be reachable under an additional hostname on a network. This is useful when you want a service to be accessible under a different name — for example, when migrating services or when a client expects a specific hostname.
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
networks:
appnet:
aliases:
- database
- postgres
networks:
appnet:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/24Now other containers can reach the database as db, database, or postgres — all resolve to the same container.
Step 4: Connect a Container to Multiple Networks
A real-world setup often requires isolation — for example, a web container that can reach both the app tier and a monitoring network, while the database is only on the internal network.
services:
web:
image: nginx
networks:
- frontend
- backend
app:
image: myapp:latest
networks:
- backend
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
networks:
- backend
prometheus:
image: prom/prometheus
networks:
- frontend
- monitoring
networks:
frontend:
driver: bridge
ipam:
config:
- subnet: 172.21.0.0/24
backend:
driver: bridge
ipam:
config:
- subnet: 172.22.0.0/24
monitoring:
driver: bridge
ipam:
config:
- subnet: 172.23.0.0/24In this setup:
dbis only onbackend— not directly reachable fromweborprometheuswebbridgesfrontendandbackend— it can talk to users and toappprometheusbridgesfrontendandmonitoring— it can scrape metrics but can’t touchdbdirectly
This is a clean, secure network topology for a typical multi-tier application.
Step 5: Combine Static IPs with Multiple Networks
You can assign specific IPs per network when a container is on multiple networks:
services:
web:
image: nginx
networks:
frontend:
ipv4_address: 172.21.0.10
backend:
ipv4_address: 172.22.0.10
app:
image: myapp:latest
networks:
backend:
ipv4_address: 172.22.0.11
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
networks:
backend:
ipv4_address: 172.22.0.20
networks:
frontend:
driver: bridge
ipam:
config:
- subnet: 172.21.0.0/24
gateway: 172.21.0.1
backend:
driver: bridge
ipam:
config:
- subnet: 172.22.0.0/24
gateway: 172.22.0.1Every container now has a predictable, fixed IP on each network it belongs to.
Step 6: Use an Existing External Network
Sometimes you want your Compose stack to join a network that already exists in Docker — for example, a shared monitoring or proxy network created by another stack.
First, create the network manually:
docker network create \
--driver bridge \
--subnet 172.30.0.0/24 \
--gateway 172.30.0.1 \
shared-proxyThen reference it in your Compose file with external: true:
services:
web:
image: nginx
networks:
- shared-proxy
- appnet
networks:
shared-proxy:
external: true
appnet:
driver: bridge
ipam:
config:
- subnet: 172.22.0.0/24Docker will not try to create or destroy shared-proxy — it just attaches containers to it. This is common when using a reverse proxy like Traefik or Nginx Proxy Manager that manages its own network.
Step 7: Full Production-Ready Example
Here is a complete docker-compose.yml that combines everything — custom networks, static IPs, aliases, and an external proxy network:
services:
nginx:
image: nginx:alpine
container_name: web
ports:
- "80:80"
- "443:443"
networks:
proxy:
frontend:
ipv4_address: 172.21.0.10
app:
image: myapp:1.0
container_name: app
restart: unless-stopped
networks:
frontend:
ipv4_address: 172.21.0.11
backend:
ipv4_address: 172.22.0.10
aliases:
- appserver
db:
image: postgres:16-alpine
container_name: postgres
restart: unless-stopped
environment:
POSTGRES_DB: appdb
POSTGRES_USER: appuser
POSTGRES_PASSWORD: strongpassword
volumes:
- pgdata:/var/lib/postgresql/data
networks:
backend:
ipv4_address: 172.22.0.20
aliases:
- database
redis:
image: redis:7-alpine
container_name: redis
restart: unless-stopped
networks:
backend:
ipv4_address: 172.22.0.30
aliases:
- cache
volumes:
pgdata:
networks:
proxy:
external: true
frontend:
driver: bridge
name: app-frontend
ipam:
config:
- subnet: 172.21.0.0/24
gateway: 172.21.0.1
backend:
driver: bridge
name: app-backend
ipam:
config:
- subnet: 172.22.0.0/24
gateway: 172.22.0.1This setup:
nginxhandles incoming traffic, sits on the proxy and frontend networksappbridges frontend and backend — talks to both nginx and the databasesdbandredisare isolated to the backend network — not directly reachable from outside- All IPs are static and predictable
- Service aliases provide clean hostnames (
database,cache,appserver)
Useful Networking Commands
| Command | Description |
|---|---|
docker network ls | List all Docker networks |
docker network inspect <name> | Show network details, subnets, and connected containers |
docker network create <name> | Create a new network manually |
docker network rm <name> | Remove a network |
docker network connect <net> <container> | Connect a running container to a network |
docker network disconnect <net> <container> | Disconnect a container from a network |
docker inspect <container> | Show container details including all IPs |
Check which IP a running container has on each network:
docker inspect <container_name> \
--format '{{range $net, $cfg := .NetworkSettings.Networks}}{{$net}}: {{$cfg.IPAddress}}{{"\n"}}{{end}}'Common Mistakes to Avoid
1. Assigning an IP without defining a subnet
Docker will reject ipv4_address if there’s no ipam.config.subnet defined. Always define the subnet first.
2. IP outside the subnet range
If your subnet is 172.20.0.0/24, valid host addresses are 172.20.0.1 – 172.20.0.254. Assigning 172.20.1.10 will fail.
3. Subnet conflicts with the host
Avoid subnets that overlap with your host’s network interfaces or existing Docker networks. Use docker network ls and docker network inspect to check existing ranges before picking a new subnet.
4. Forgetting to restart after network changes
Changes to the networks block require bringing the stack down and back up — docker compose restart is not enough:
docker compose down
docker compose up -d5. Using the default network with static IPs
You cannot assign static IPs to the auto-created default network. You must define a custom network with ipam config to use ipv4_address.
Summary
Here’s what you covered:
- How Docker Compose creates a default network automatically
- Defining custom named networks with explicit subnets
- Assigning static IPs using
ipv4_addressandipamconfig - Adding network aliases for flexible hostname resolution
- Connecting containers to multiple networks for proper tier isolation
- Joining external networks created outside of Compose
- A full production-ready multi-service example
Controlling your Docker network layout is the difference between a fragile, hard-to-debug stack and a clean, predictable infrastructure. Static IPs and named networks make your setup easier to document, firewall, and monitor.
Need help designing or debugging your Docker infrastructure? Get in touch — I’m happy to help.
- docker
- docker-compose
- networking
- devops
- linux