Docker Compose Basics: Services, Networks, Volumes
Define multi-service stacks declaratively with Compose: services, custom networks, and named volumes for stateful apps most VPS workloads actually need.
Netbay Infrastructure Team
Netbay Engineering
On this page
A single docker run is fine for a throwaway container, but a real service — a web app, a sidecar cache, a database, a worker — is a system of cooperating containers that must share networks, storage, and restart policies. Docker Compose turns that system into one YAML file you can commit, review, and reproduce on any host. This guide walks through the three pillars of every stack — services, networks, and volumes — and builds a small but realistic web-plus-cache-plus-database example that runs on any VPS with Docker installed.
The compose.yaml Anatomy
A Compose file has a version-independent top level that stays stable: name (the project name), services, networks, and volumes. Everything else — secrets, configs, env — hangs off these. The project name scopes resources: mycompose_stack's containers, its network, and its volumes all carry that prefix, so you can run several stacks on one host without collisions.
name: shop
services:
web:
image: nginx:alpine
ports:
- "80:3000"
cache:
image: redis:7-alpine
networks:
- internal
db:
image: postgres:16-alpine
volumes:
- db-data:/var/lib/postgresql/data
networks:
internal:
driver: bridge
volumes:
db-data:Services Are the Units You Run
Each service declares what to run: an image (or a build context), commands, environment, and where it connects. Services on the same compose project can resolve each other by service name — the web service above can reach db:5432 and cache:6379 without knowing IP addresses, because Compose wires a default project network. Port exposure is explicit: the 80:3000 mapping is host:container, and a service without a ports entry is only reachable inside the stack, which is exactly the isolation you want for a cache or a worker.
Networks: Default, Custom, and Internal
Compose gives every project a default bridge network, and most small stacks never need more. The moment you want tighter isolation — a Redis that only the app can reach, or a DMZ segment in front of an internal backend — you declare explicit networks and attach services to them. A service attached to multiple networks joins all of them; attach control is one of the few clean ways Docker gives you to express "this service talks to these two things and nothing else".
name: shop
services:
web:
image: nginx:alpine
ports:
- "80:3000"
networks:
- front
- internal
cache:
image: redis:7-alpine
networks:
- internal
networks:
front:
internal:
internal: trueThe internal: true flag builds the network with no external connectivity at all — no gateway, no outbound route. An internal network is the right home for dependencies that never need the internet, and it makes container escapes from that segment far less useful to an attacker.
Volumes Keep State Alive
Containers are disposable; databases are not. A named volume survives container replacement, recreate, and even the delete of the container that used it. The volume above, db-data, is detached from the lifecycle of the Postgres container, so docker compose up -d --force-recreate after a config change does not touch a byte of stored data. Declaring the volume at the top level is what makes it reusable across services and backup jobs.
Eight Commands That Cover 90% of Daily Use
docker compose up -d
docker compose down
docker compose logs -f web
docker compose ps
docker compose exec web sh
docker compose restart web
docker compose config
docker compose up -d --builddocker compose config renders the fully-resolved config (including defaults and interpolated values), which makes it the best review tool before you trust a file in production. Combining --build with up -d rebuilds changed images and recreates only the services whose config shifted. If a service ever stops resolving a sibling by name, the first suspect is a second network: a service attached to several networks only answers on the ones the caller shares.
Takeaway
Docker Compose turns "run my app plus its dependencies" into a reviewable artifact instead of a fragile sequence of manual commands. Three concepts — services, networks, volumes — cover the bulk of what real project files contain, and internal networks plus named volumes handle the two places stack design usually goes wrong. A VPS from Netbay gives you a fresh Docker-ready OS in under a minute, which is the fastest way to stop running docker run superstitions and start committing a compose file — see netbayhosts.in.
Keep reading
Follow along on a real VPS
Deploy Linux in under 60 seconds
These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.
Deploy an instance