Defining Services
[!NOTE] This module explores the core principles of Defining Services, deriving solutions from first principles and hardware constraints to build world-class, production-ready expertise.
1. What is a Service?
In Docker Compose, a Service is a configuration for a container. It tells Docker:
- Which image to use (or how to build it).
- What ports to open.
- What volumes to mount.
- How to talk to other services.
Ideally, one service equals one process (e.g., web, worker, db).
2. Core Configuration
1. Build vs Image
You can either pull an image or build one from source.
services:
# Option A: Pull from Registry
redis:
image: redis:alpine
# Option B: Build from Dockerfile
api:
build:
context: ./api
dockerfile: Dockerfile.dev
args:
GO_VERSION: 1.21
2. Startup Order (depends_on)
By default, Compose starts services in parallel. This causes race conditions (e.g., the API tries to connect to the DB before the DB is ready).
depends_on defines dependency order.
Interactive: Dependency Graph
Toggle the dependencies to see how startup order changes.
3. Healthchecks (The “Real” dependency)
depends_on only waits for the container to start, not to be ready. If Postgres takes 10 seconds to boot, your API will crash if it tries to connect immediately.
Solution: Healthchecks.
services:
db:
image: postgres
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
api:
depends_on:
db:
condition: service_healthy # Wait for GREEN status
3. Production Patterns
Restart Policies
What happens when your container crashes?
no: Do not restart (default).always: Always restart.on-failure: Restart only if exit code ≠ 0.unless-stopped: Always restart unless explicitly stopped by user.
services:
worker:
image: my-worker
restart: on-failure
Resource Limits (Deploy)
Prevent a memory leak from crashing your entire server.
services:
app:
deploy:
resources:
limits:
cpus: '0.50'
memory: 512M
4. Code Example: Robust Go Service
Here is how you structure a Go service in Compose with all best practices.
# docker-compose.yaml
services:
# The Application
app:
build: .
ports:
- "8080:8080"
environment:
- DB_HOST=db
- DB_PORT=5432
depends_on:
db:
condition: service_healthy
restart: always
# The Database
db:
image: postgres:15-alpine
environment:
- POSTGRES_PASSWORD=secret
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
pgdata:
This configuration ensures:
- Zero Downtime: The app waits for the DB to be ready.
- Resilience: Both services restart automatically on failure.
- Persistence: Data is saved in a named volume.