Multi-Service Apps
Running a web app and a database together
Section titled “Running a web app and a database together”Almost every real application needs a database. With Docker Compose you define both services in one file and Compose handles the networking automatically — no manual docker network create required.
Here is a web app backed by PostgreSQL:
services: app: image: node:24-alpine working_dir: /app command: ["node", "server.js"] ports: - "3000:3000" environment: - DATABASE_HOST=db - DATABASE_PORT=5432 - DATABASE_NAME=mydb depends_on: - db
db: image: postgres:18-alpine environment: - POSTGRES_DB=mydb - POSTGRES_USER=user - POSTGRES_PASSWORD=secretdepends_on — start order
Section titled “depends_on — start order”depends_on tells Compose to start the db service before app. This controls container start order, not readiness — the database process inside the container may not be accepting connections the instant the container starts. For production, add a healthcheck to db and use depends_on: condition: service_healthy in app.
Basic start order (sufficient for learning and development):
depends_on: - dbHealth-checked start order (production pattern):
services: app: depends_on: db: condition: service_healthy
db: image: postgres:18-alpine healthcheck: test: ["CMD-SHELL", "pg_isready -U user -d mydb"] interval: 5s timeout: 5s retries: 5 environment: - POSTGRES_DB=mydb - POSTGRES_USER=user - POSTGRES_PASSWORD=secretAutomatic networking
Section titled “Automatic networking”When you run docker compose up, Compose automatically creates a network for your project. Every service in the file is attached to that network. Services reach each other using their service name as the hostname.
In the example above, app connects to the database at host db port 5432 — exactly the service name. No IP addresses, no manual network configuration.
app --[db:5432]--> db <-- myapp-network (auto-created) -->Viewing logs from multiple services
Section titled “Viewing logs from multiple services”Stream logs from all services at once:
docker compose logs -fFollow only one service:
docker compose logs -f appHands-on practice
Section titled “Hands-on practice”The snippet below starts an app container that connects to a PostgreSQL container using the service name db.
# Write a two-service compose.yaml
cat > compose.yaml <<'EOF'
services:
app:
image: alpine
command: >
sh -c "
echo 'Waiting for db...' &&
sleep 3 &&
echo 'Connecting to db:5432 (postgres service)' &&
nc -zv db 5432 &&
echo 'Connection to db succeeded via service name DNS!'
"
depends_on:
- db
db:
image: postgres:18-alpine
environment:
- POSTGRES_PASSWORD=secret
EOF
# Start both services (app waits 3s then probes db)
docker compose up
# Clean up
docker compose downTrade-offs
Section titled “Trade-offs”| Option | Benefit | Cost |
|---|---|---|
| Docker Compose | Wires up several services with one file, get a full local run in seconds | Runs on a single host — no cross-node orchestration as the number of services grows |
| Kubernetes | Manages service-to-service communication across a cluster with self-healing | Needs separate Service, Deployment, and network policy objects — much more complex than a compose.yaml |
Common mistakes
Section titled “Common mistakes”- Assuming
depends_on: - dbguaranteesdbis ready to accept connections beforeappstarts, when it only controls container start order — usehealthcheckwithcondition: service_healthyif you need to wait for real readiness. - Hardcoding an IP address or
localhostinstead of the service name when connecting to the database, breaking networking the moment the container is recreated with a new IP. - Forgetting that the network Compose creates automatically is project-scoped — running several projects with similar directory names can accidentally create duplicate networks.
💡 Real-world example
A common pipeline in real teams runs
app+dblocally with Docker Compose during development for a fast, complete dev experience, then deploys the same image to Kubernetes in production, replacingdepends_onwith a readiness probe and the Compose network with a Kubernetes Service — the application code does not change, only the orchestration layer.