Docker Compose Stack — Postgres + Redis + App
Production-ready Docker Compose template with PostgreSQL, Redis, health checks, persistent volumes, and environment management.
A common stack for web apps: your application, a PostgreSQL database, and Redis for caching/sessions. This template includes health checks, persistent volumes, restart policies, and environment variable management.
The Full Compose File
| 1 | services: |
| 2 | app: |
| 3 | build: . |
| 4 | ports: |
| 5 | - "3000:3000" |
| 6 | environment: |
| 7 | - DATABASE_URL=postgres://appuser:${DB_PASSWORD}@db:5432/myapp |
| 8 | - REDIS_URL=redis://cache:6379 |
| 9 | - NODE_ENV=production |
| 10 | depends_on: |
| 11 | db: |
| 12 | condition: service_healthy |
| 13 | cache: |
| 14 | condition: service_healthy |
| 15 | restart: unless-stopped |
| 16 | |
| 17 | db: |
| 18 | image: postgres:17-alpine |
| 19 | volumes: |
| 20 | - pgdata:/var/lib/postgresql/data |
| 21 | environment: |
| 22 | POSTGRES_DB: myapp |
| 23 | POSTGRES_USER: appuser |
| 24 | POSTGRES_PASSWORD: ${DB_PASSWORD} |
| 25 | ports: |
| 26 | - "5432:5432" |
| 27 | healthcheck: |
| 28 | test: ["CMD-SHELL", "pg_isready -U appuser -d myapp"] |
| 29 | interval: 5s |
| 30 | timeout: 5s |
| 31 | retries: 5 |
| 32 | restart: unless-stopped |
| 33 | |
| 34 | cache: |
| 35 | image: redis:7-alpine |
| 36 | volumes: |
| 37 | - redisdata:/data |
| 38 | command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru |
| 39 | ports: |
| 40 | - "6379:6379" |
| 41 | healthcheck: |
| 42 | test: ["CMD", "redis-cli", "ping"] |
| 43 | interval: 5s |
| 44 | timeout: 3s |
| 45 | retries: 5 |
| 46 | restart: unless-stopped |
| 47 | |
| 48 | volumes: |
| 49 | pgdata: |
| 50 | redisdata: |
Environment File
Store secrets in a .env file next to your docker-compose.yml:
| 1 | DB_PASSWORD=change-me-to-something-strong |
Never commit `.env` files to version control. Add .env to your .gitignore immediately. Use .env.example with placeholder values for documentation.
Key Patterns Used
Health Checks
| Service | Health Check | Why It Matters |
|---|---|---|
| PostgreSQL | pg_isready | App won't start until the DB is accepting connections |
| Redis | redis-cli ping | Ensures Redis is responsive, not just running |
| App | (add your own) | Compose waits for dependencies via depends_on: condition |
Volume Persistence
pgdata— PostgreSQL data survives container restarts and rebuildsredisdata— Redis AOF persistence so cached data survives restarts
Restart Policies
| Policy | Behavior |
|---|---|
no | Never restart (default) |
always | Always restart, even if manually stopped |
unless-stopped | Restart unless you explicitly docker compose stop — best for most cases |
on-failure | Only restart if the container exits with a non-zero code |
Common Commands
| 1 | # Start everything (detached) |
| 2 | docker compose up -d |
| 3 | |
| 4 | # Rebuild after code changes |
| 5 | docker compose up -d --build |
| 6 | |
| 7 | # View logs (follow mode) |
| 8 | docker compose logs -f app |
| 9 | |
| 10 | # Stop everything (keeps volumes) |
| 11 | docker compose down |
| 12 | |
| 13 | # Stop and DELETE all data (nuclear option) |
| 14 | docker compose down -v |
docker compose down -v deletes your named volumes — that means all your database data is gone. Only use this for a full reset.
Accessing the Database
| 1 | # Via psql (if installed locally) |
| 2 | psql -h localhost -U appuser -d myapp |
| 3 | |
| 4 | # Via docker exec (no local psql needed) |
| 5 | docker compose exec db psql -U appuser -d myapp |
For database GUIs, connect to localhost:5432 with the credentials from your .env. Works with pgAdmin, DBeaver, TablePlus, and DataGrip.
If you're converting between docker run commands and Compose files, try the Docker Run to Compose converter on this site.