The days of manually installing PostgreSQL, Redis, and your Node/Python environment on your local machine (and dealing with "it works on my machine" bugs) are long gone. Docker Compose allows you to define your entire infrastructure—backend, database, caching layer—as code.
In this guide, we will build the ultimate docker-compose.yml file that we use at Kyvronix to spin up full-stack development and production environments in a single command.
1. Anatomy of a Docker Compose File
Docker Compose uses a YAML file to define multiple "services". Each service is a container. When you run docker-compose up, Compose creates an isolated virtual network where these containers can talk to each other using their service names as hostnames.
2. The Complete docker-compose.yml
Here is a production-ready template that runs a Node.js API, PostgreSQL database, and Redis cache. It includes health checks, persistent volumes, and secure environment variable injection.
version: '3.8'
services:
# 1. The API Application
api:
build:
context: .
dockerfile: Dockerfile
image: my-fullstack-api:latest
container_name: api_service
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DB_HOST=postgres # Notice we use the service name!
- DB_PORT=5432
- DB_USER=${DB_USER} # Pull from .env file
- DB_PASSWORD=${DB_PASS}
- REDIS_HOST=redis
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
# 2. PostgreSQL Database
postgres:
image: postgres:15-alpine
container_name: pg_db
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASS}
POSTGRES_DB: myapp_db
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d myapp_db"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# 3. Redis Cache
redis:
image: redis:7-alpine
container_name: redis_cache
ports:
- "6379:6379"
volumes:
- redisdata:/data
command: redis-server --appendonly yes
restart: unless-stopped
volumes:
pgdata:
redisdata:
3. Key Concepts Explained
Internal DNS
Notice how the API connects to Postgres: DB_HOST=postgres. Docker provides automatic DNS resolution. The API container doesn't need to know the IP address of the database container; it simply connects to the hostname postgres.
depends_on & Health Checks
Your API will likely crash if it tries to connect to the database before the database is ready. The depends_on block, combined with condition: service_healthy, ensures Docker waits until PostgreSQL actually responds to pg_isready before starting the API container.
Volumes for Persistence
Containers are ephemeral. If you delete a container, its data is destroyed. The volumes block mounts a directory from your host machine into the container, ensuring that your database rows and Redis keys survive container restarts and updates.
Conclusion
By defining your infrastructure in a single docker-compose.yml file, you make onboarding new developers as easy as running docker-compose up, and deploying to a fresh EC2 instance becomes completely reproducible.
— Ankit Kumar