Networks and Volumes in Compose
LEVEL 0
The Problem
You’ve defined services. They’re running. But how do they talk to each other? How does the web server reach the application server? How does the app reach the database?
And what about data? Your database stores data in /var/lib/postgresql/data. When the container stops, that data disappears. How do you persist it?
Networks solve service-to-service communication.
Volumes solve data persistence.
LEVEL 1
The Concept — The Apartment Building
The Concept
Imagine an apartment building.
Networks are like floors.
Floor 1 (frontend network): Web-facing apartments. The reception desk, the lobby, public spaces.
Floor 2 (backend network): Private apartments. The management office, storage rooms.
An apartment on Floor 1 can communicate with other apartments on Floor 1—they knock on each other’s doors. Same with Floor 2.
But Floor 1 can’t directly access Floor 2 unless there’s a shared hallway (an apartment on both floors).
Volumes are like personal storage units in the basement. Each apartment can rent a storage unit. When a tenant moves out, their storage unit remains. The next tenant in that apartment can access the same storage unit.
LEVEL 2
The Mechanics — Default Networking
The Mechanics
By default, Compose creates a single network for your application.
All services join this network and can communicate using service names as hostnames.
Example:
version: '3.9'
services:
web:
image: nginx
app:
image: myapp
db:
image: postgres
When you run docker compose up, Compose creates a network called <project>_default.
All three services (web, app, db) join this network.
Service discovery:
webcan reachappat hostnameappappcan reachdbat hostnamedbdbcan reachwebat hostnameweb
No manual network configuration is needed. Compose creates the expected network wiring.
LEVEL 3
Custom Networks
Sometimes you want network isolation. Not all services should talk to all other services.
Example: A three-tier application.
version: '3.9'
services:
nginx:
image: nginx:alpine
networks:
- frontend
ports:
- "80:80"
app:
image: myapp
networks:
- frontend
- backend
db:
image: postgres:15
networks:
- backend
cache:
image: redis:7
networks:
- backend
networks:
frontend:
backend:
What happens:
nginxis onfrontendnetwork onlyappis on BOTHfrontendandbackendnetworksdbandcacheare onbackendnetwork only
Who can talk to who:
✅ nginx → app (both on frontend)
✅ app → db (both on backend)
✅ app → cache (both on backend)
✅ db → cache (both on backend)
❌ nginx → db (different networks, no overlap)
❌ nginx → cache (different networks, no overlap)
This creates security isolation. The public-facing web server can’t directly access the database.
LEVEL 4
Network Configuration Options
Basic network:
networks:
mynetwork:
This creates a network with default settings (bridge driver).
Custom driver:
networks:
mynetwork:
driver: bridge
Custom subnet:
networks:
mynetwork:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
External network (use an existing network, don’t create it):
networks:
existing-net:
external: true
LEVEL 5
Volumes in Compose
Named volumes:
Define at the top level, use in services:
version: '3.9'
services:
db:
image: postgres:15
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
Compose creates a volume named <project>_db-data. Data persists even when containers are removed.
Bind mounts:
Mount a directory from your host into the container:
services:
app:
image: myapp
volumes:
- ./app:/app # Host directory ./app → container /app
- ./config.yml:/etc/app/config.yml:ro # Single file, read-only
Anonymous volumes:
services:
app:
image: myapp
volumes:
- /app/node_modules # Anonymous volume
Docker creates a volume with a random name. Useful for preventing bind mounts from overwriting certain directories.
External volumes (use an existing volume):
volumes:
existing-data:
external: true
Volumes vs. Bind Mounts — When to Use What
Use Named Volumes when:
- Storing database data
- Persisting application state
- Sharing data between containers
- You want Docker to manage the storage location
Use Bind Mounts when:
- Development (live code reloading)
- Providing configuration files
- Accessing host files/directories
- You need to see/edit files from the host
Example: Development Setup
services:
app:
build: ./app
volumes:
- ./app:/app # Bind mount: live code changes
- /app/node_modules # Anonymous: don't overwrite node_modules
- app-logs:/var/log/app # Named volume: persist logs
volumes:
app-logs:
Engine status: planned. The shell remains visible while the artifact execution is prepared.