Docker Compose Development Stack
A complete Docker Compose development environment with hot reload, PostgreSQL, Redis, and email testing. One command to start coding.
Description
A batteries-included Docker Compose setup for local development. Includes Node.js app with hot reload, PostgreSQL with health checks, Redis for caching, and Mailpit for email testing.
docker-compose.yml
# docker-compose.yml
services:
# ════════════════════════════════════════════════════════════
# Application
# ════════════════════════════════════════════════════════════
app:
build:
context: .
dockerfile: Dockerfile.dev
container_name: myapp-dev
restart: unless-stopped
ports:
- "3000:3000"
- "9229:9229" # Node.js debugger
volumes:
- .:/app
- /app/node_modules # Don't override node_modules
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://dev:devpass@db:5432/myapp
- REDIS_URL=redis://redis:6379
- SMTP_HOST=mailpit
- SMTP_PORT=1025
- LOG_LEVEL=debug
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
command: npm run dev
# ════════════════════════════════════════════════════════════
# PostgreSQL Database
# ════════════════════════════════════════════════════════════
db:
image: postgres:16-alpine
container_name: myapp-db
restart: unless-stopped
ports:
- "5432:5432"
environment:
POSTGRES_USER: dev
POSTGRES_PASSWORD: devpass
POSTGRES_DB: myapp
volumes:
- pgdata:/var/lib/postgresql/data
- ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dev"]
interval: 5s
timeout: 3s
retries: 5
# ════════════════════════════════════════════════════════════
# Redis Cache
# ════════════════════════════════════════════════════════════
redis:
image: redis:7-alpine
container_name: myapp-redis
restart: unless-stopped
ports:
- "6379:6379"
command: redis-server --appendonly yes
volumes:
- redisdata:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
# ════════════════════════════════════════════════════════════
# Email Testing (Mailpit)
# ════════════════════════════════════════════════════════════
mailpit:
image: axllent/mailpit:latest
container_name: myapp-mail
restart: unless-stopped
ports:
- "8025:8025" # Web UI
- "1025:1025" # SMTP
environment:
MP_SMTP_AUTH_ACCEPT_ANY: 1
MP_SMTP_AUTH_ALLOW_INSECURE: 1
# ════════════════════════════════════════════════════════════
# Adminer (DB Management UI)
# ════════════════════════════════════════════════════════════
adminer:
image: adminer:latest
container_name: myapp-adminer
restart: unless-stopped
ports:
- "8080:8080"
environment:
ADMINER_DEFAULT_SERVER: db
volumes:
pgdata:
redisdata:
Dockerfile.dev
# Dockerfile.dev
FROM node:20-alpine
WORKDIR /app
# Install dependencies for native modules
RUN apk add --no-cache python3 make g++
# Install dependencies first (caching layer)
COPY package*.json ./
RUN npm install
# Copy source (overridden by bind mount in dev)
COPY . .
# Expose app + debugger ports
EXPOSE 3000 9229
# Start with hot reload
CMD ["npm", "run", "dev"]
Environment File
# .env.development
NODE_ENV=development
PORT=3000
# Database
DATABASE_URL=postgresql://dev:devpass@localhost:5432/myapp
# Redis
REDIS_URL=redis://localhost:6379
# Email (Mailpit)
SMTP_HOST=localhost
SMTP_PORT=1025
# Auth
JWT_SECRET=dev-secret-do-not-use-in-production
SESSION_SECRET=another-dev-secret
# External Services (use dev/sandbox keys)
STRIPE_SECRET_KEY=sk_test_xxx
AWS_ACCESS_KEY_ID=your-dev-key
Makefile
# Makefile
.PHONY: dev dev-build dev-down dev-reset dev-logs dev-shell dev-db test
# Start development environment
dev:
docker compose up
# Start with rebuild
dev-build:
docker compose up --build
# Stop all services
dev-down:
docker compose down
# Reset everything (including data volumes)
dev-reset:
docker compose down -v
docker compose up --build
# View application logs
dev-logs:
docker compose logs -f app
# Shell into app container
dev-shell:
docker compose exec app sh
# Connect to database
dev-db:
docker compose exec db psql -U dev myapp
# Run tests
test:
docker compose exec app npm test
# Run database migrations
migrate:
docker compose exec app npm run migrate
# Seed database
seed:
docker compose exec app npm run seed
Init SQL Script
-- scripts/init.sql
-- This runs on first container startup
-- Create extensions
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
-- Create additional schemas
CREATE SCHEMA IF NOT EXISTS audit;
-- Create audit log table
CREATE TABLE IF NOT EXISTS audit.logs (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
table_name VARCHAR(100) NOT NULL,
action VARCHAR(10) NOT NULL,
old_data JSONB,
new_data JSONB,
user_id UUID,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Grant permissions
GRANT ALL PRIVILEGES ON SCHEMA audit TO dev;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA audit TO dev;
Package.json Scripts
{
"scripts": {
"dev": "tsx watch --inspect=0.0.0.0:9229 src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"test": "vitest",
"test:coverage": "vitest --coverage",
"migrate": "prisma migrate dev",
"seed": "tsx prisma/seed.ts",
"lint": "eslint src/",
"type-check": "tsc --noEmit"
}
}
Usage
# Start everything
make dev
# Access services:
# App: http://localhost:3000
# Mailpit: http://localhost:8025
# Adminer: http://localhost:8080
# DB: localhost:5432
# Redis: localhost:6379
# Run commands
make dev-shell # Shell into container
make dev-db # Connect to PostgreSQL
make dev-logs # View logs
make migrate # Run migrations
make test # Run tests
# Reset everything (fresh start)
make dev-reset