21 KiB
Docker Development Workflow
Guide to developing Changemaker Lite V2 using Docker containers for consistent, reproducible development environments.
Overview
Docker-based development provides:
- Consistency: Same environment across all developer machines
- Isolation: Services don't interfere with host system
- Production Parity: Dev environment matches production
- Easy Reset: Rebuild containers for clean state
This guide covers Docker development workflows, from basic container operations to advanced debugging techniques.
Docker vs Local Development
When to Use Docker
Advantages:
- Consistent Node.js/PostgreSQL/Redis versions
- No need to install services on host machine
- Easy onboarding for new developers
- Production-like environment
- Volume mounts still support hot reload
Disadvantages:
- Slightly slower hot reload (especially macOS/Windows)
- More complex debugging setup
- Volume mount performance overhead
- Larger disk space usage
When to Use Local npm
Advantages:
- Faster hot reload (native file system)
- Direct access to Node.js processes
- Simpler debugging (VSCode attach)
- Better performance on macOS/Windows
Disadvantages:
- Must install Node.js, PostgreSQL, Redis locally
- Version inconsistencies between developers
- Host system configuration required
Hybrid Approach (Recommended)
Run databases in Docker, API/Admin locally:
# Docker: Databases only
docker compose up -d v2-postgres redis mailhog
# Local: Development servers
cd api && npm run dev
cd admin && npm run dev
This combines benefits of both approaches.
Starting Development Services
Full Docker Development
Start all development services:
# Core services (API, Admin, Databases)
docker compose up -d api admin v2-postgres redis
# Optional: MailHog for email testing
docker compose up -d mailhog
# Optional: Media API
docker compose up -d media-api
Verify services started:
docker compose ps
Expected output:
NAME STATUS PORTS
api running 0.0.0.0:4000->4000/tcp
admin running 0.0.0.0:3000->3000/tcp
v2-postgres running 0.0.0.0:5433->5432/tcp
redis running 0.0.0.0:6379->6379/tcp
mailhog running 0.0.0.0:1025->1025/tcp, 0.0.0.0:8025->8025/tcp
Selective Service Start
Start only what you need:
# Just databases (for local npm development)
docker compose up -d v2-postgres redis
# Just API (admin running locally)
docker compose up -d api v2-postgres redis
# Just Admin (API running locally)
docker compose up -d admin
Start with Monitoring Stack
Enable monitoring services:
# Start with monitoring profile
docker compose --profile monitoring up -d
# Or specific monitoring services
docker compose up -d prometheus grafana
Watching Logs
View Service Logs
Real-time log streaming:
# All services
docker compose logs -f
# Specific service
docker compose logs -f api
# Multiple services
docker compose logs -f api admin
# Last 100 lines, then follow
docker compose logs -f --tail=100 api
Log output example (API):
api | Server running on port 4000
api | Database connected
api | Redis connected
api | BullMQ worker started
api | GET /api/users 200 45ms
Log output example (Admin):
admin | VITE v5.x.x ready in 500 ms
admin | ➜ Local: http://localhost:3000/
admin | ➜ Network: http://172.18.0.5:3000/
Filter Logs
Use grep to filter log output:
# Show only errors
docker compose logs -f api | grep ERROR
# Show only database queries
docker compose logs -f api | grep "SELECT\|INSERT\|UPDATE\|DELETE"
# Show only HTTP requests
docker compose logs -f api | grep "GET\|POST\|PUT\|DELETE"
Export Logs
Save logs to file:
# All services
docker compose logs > logs.txt
# Specific service with timestamps
docker compose logs -t api > api-logs.txt
# Last 24 hours
docker compose logs --since 24h > recent-logs.txt
Executing Commands in Containers
Using docker compose exec
Run commands inside running containers:
# General syntax
docker compose exec <service> <command>
# Examples:
docker compose exec api npm run type-check
docker compose exec admin npm run lint
docker compose exec v2-postgres psql -U changemaker_v2 -d changemaker_v2_db
Common API Commands
# Type-check
docker compose exec api npm run type-check
# Prisma migrate
docker compose exec api npx prisma migrate dev --name add_field
# Prisma Studio
docker compose exec api npx prisma studio
# Seed database
docker compose exec api npx prisma db seed
# Drizzle push (Media API)
docker compose exec api npx drizzle-kit push
# Node REPL
docker compose exec api node
# Shell access
docker compose exec api sh
Common Admin Commands
# Type-check
docker compose exec admin npm run type-check
# Build
docker compose exec admin npm run build
# Lint
docker compose exec admin npm run lint
# Shell access
docker compose exec admin sh
Database Commands
# PostgreSQL shell
docker compose exec v2-postgres psql -U changemaker_v2 -d changemaker_v2_db
# Run SQL query
docker compose exec v2-postgres psql -U changemaker_v2 -d changemaker_v2_db -c "SELECT COUNT(*) FROM users;"
# Dump database
docker compose exec v2-postgres pg_dump -U changemaker_v2 changemaker_v2_db > backup.sql
# Restore database
cat backup.sql | docker compose exec -T v2-postgres psql -U changemaker_v2 changemaker_v2_db
Redis Commands
# Redis CLI
docker compose exec redis redis-cli -a your_redis_password
# Ping
docker compose exec redis redis-cli -a your_redis_password ping
# Get all keys
docker compose exec redis redis-cli -a your_redis_password KEYS '*'
# Monitor commands
docker compose exec redis redis-cli -a your_redis_password MONITOR
Hot Reload in Containers
How Volume Mounts Enable Hot Reload
Docker Compose volume mounts sync code between host and container:
# docker-compose.yml
api:
volumes:
- ./api:/app # Syncs code changes
- /app/node_modules # Preserves container's node_modules
- /app/dist # Preserves build output
When you edit a file on host:
- File change detected by host file system
- Change synced to container via volume mount
tsx watch(API) or Vite (Admin) detects change- Service restarts (API) or HMR updates (Admin)
API Hot Reload
API uses tsx watch for auto-restart:
# Start API in Docker
docker compose up -d api
# Watch logs
docker compose logs -f api
# Edit file: api/src/modules/auth/auth.service.ts
# Logs show:
# api | File changed: src/modules/auth/auth.service.ts
# api | Restarting server...
# api | Server running on port 4000
What triggers reload:
.tsfile changes insrc/- Schema changes (after Prisma migrate)
What does NOT trigger reload:
.envchanges (restart container manually)package.jsonchanges (rebuild container)
Admin Hot Reload (Vite HMR)
Admin uses Vite Hot Module Replacement:
# Start Admin in Docker
docker compose up -d admin
# Watch logs
docker compose logs -f admin
# Edit file: admin/src/pages/UsersPage.tsx
# Logs show:
# admin | 10:30:45 AM [vite] hmr update /src/pages/UsersPage.tsx
# Browser updates WITHOUT full reload
HMR behavior:
- Component changes: Updates component only
- CSS changes: Updates styles instantly
- Store changes: May require full reload
Performance Considerations
Linux: Volume mounts are native, excellent performance.
macOS/Windows: Volume mounts use virtualization layer, slower performance.
Optimization for macOS/Windows:
- Use delegated volume mounts (docker-compose.yml):
api:
volumes:
- ./api:/app:delegated # Slightly better performance
- Reduce watched files (.dockerignore):
node_modules
dist
coverage
.git
*.log
- Use local development for intensive work:
# Stop Docker services
docker compose stop api admin
# Run locally
cd api && npm run dev
cd admin && npm run dev
Database Operations
Running Migrations in Docker
# Create migration
docker compose exec api npx prisma migrate dev --name add_user_field
# Apply migrations (production)
docker compose exec api npx prisma migrate deploy
# Check migration status
docker compose exec api npx prisma migrate status
Seeding Database
# Run seed script
docker compose exec api npx prisma db seed
# Or run custom script
docker compose exec api npx tsx prisma/custom-seed.ts
Resetting Database
WARNING: Deletes all data!
# Reset and re-seed
docker compose exec api npx prisma migrate reset
# Confirm when prompted:
# ⚠️ All data will be lost. Continue? [y/N]: y
Prisma Studio in Docker
# Start Prisma Studio
docker compose exec api npx prisma studio
# Access at http://localhost:5555
Note: Port forwarding must be configured (already set in docker-compose.yml).
Manual Database Access
# Open PostgreSQL shell
docker compose exec v2-postgres psql -U changemaker_v2 -d changemaker_v2_db
# Run queries
changemaker_v2_db=# SELECT * FROM users;
changemaker_v2_db=# \dt -- List tables
changemaker_v2_db=# \q -- Exit
Rebuilding Containers
When to Rebuild
Rebuild containers when:
package.jsondependencies changeDockerfilechanges- Base image needs update
- Container is in corrupted state
Rebuild Commands
# Rebuild all services
docker compose build
# Rebuild specific service
docker compose build api
# Rebuild without cache (clean build)
docker compose build --no-cache api
# Rebuild and restart
docker compose up -d --build api
Full Rebuild Workflow
# 1. Stop services
docker compose down
# 2. Rebuild (no cache)
docker compose build --no-cache
# 3. Start services
docker compose up -d
# 4. Verify
docker compose ps
docker compose logs -f api admin
After Package Changes
When package.json changes (new dependencies):
# Option 1: Rebuild container
docker compose build --no-cache api
docker compose restart api
# Option 2: Install in running container
docker compose exec api npm install
docker compose restart api
# Option 3: Remove and recreate
docker compose rm -sf api
docker compose up -d api
Cleaning Up
Stop Services
# Stop all services
docker compose stop
# Stop specific service
docker compose stop api
# Stop and remove containers
docker compose down
Remove Containers
# Remove containers (keeps volumes)
docker compose down
# Remove containers and volumes (DELETES DATA)
docker compose down -v
# Remove containers, volumes, and images
docker compose down -v --rmi all
Clean Docker System
# Remove stopped containers
docker container prune
# Remove unused images
docker image prune
# Remove unused volumes
docker volume prune
# Remove everything (DANGEROUS)
docker system prune -a --volumes
Clean Project Volumes
# List project volumes
docker volume ls | grep changemaker
# Remove specific volume
docker volume rm changemaker-lite_v2-postgres-data
# Remove all project volumes (DELETES DATABASE)
docker compose down -v
Reset Development Environment
Complete reset (deletes all data):
# 1. Stop and remove everything
docker compose down -v --rmi all
# 2. Clean Docker system
docker system prune -a --volumes -f
# 3. Rebuild from scratch
docker compose build --no-cache
# 4. Start services
docker compose up -d
# 5. Run migrations
docker compose exec api npx prisma migrate deploy
docker compose exec api npx prisma db seed
Debugging in Docker
Attach to Running Container
# Get shell in running container
docker compose exec api sh
# Or bash (if available)
docker compose exec api bash
# Inside container:
# - Explore file system
# - Run commands
# - Check environment variables
Inspect Container
# View container details
docker inspect api
# View container environment variables
docker inspect api | grep -A 20 "Env"
# View container mounts
docker inspect api | grep -A 50 "Mounts"
VSCode Remote Containers
Install "Remote - Containers" extension, then:
- Open Command Palette (Cmd+Shift+P / Ctrl+Shift+P)
- Select "Remote-Containers: Attach to Running Container"
- Choose
apioradmincontainer - VSCode opens new window attached to container
- Open
/appfolder in container - Set breakpoints and debug normally
Debug Logs
Enable verbose logging:
# API with debug logs
docker compose exec api npm run dev -- --inspect
# Watch logs with timestamp
docker compose logs -f -t api
# Filter errors only
docker compose logs -f api 2>&1 | grep -i error
Network Debugging
# Test container connectivity
docker compose exec api ping v2-postgres
docker compose exec api ping redis
# Check listening ports
docker compose exec api netstat -tuln
# Test API from inside container
docker compose exec api wget -O- http://localhost:4000/health
Performance Debugging
# Container stats
docker stats
# Specific service stats
docker stats api admin
# Container resource limits
docker inspect api | grep -A 10 "Memory\|Cpu"
Advanced Workflows
Multi-Stage Development
Run different service combinations:
# Frontend development (local Admin, Docker API)
docker compose up -d api v2-postgres redis
cd admin && npm run dev
# Backend development (local API, Docker Admin)
docker compose up -d admin v2-postgres redis
cd api && npm run dev
# Full-stack (everything in Docker)
docker compose up -d api admin v2-postgres redis
Custom Docker Compose Files
Create docker-compose.dev.yml for dev overrides:
# docker-compose.dev.yml
services:
api:
command: npm run dev -- --inspect=0.0.0.0:9229
ports:
- "9229:9229" # Debug port
environment:
- LOG_LEVEL=debug
Usage:
# Use both files
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
# Or set COMPOSE_FILE env var
export COMPOSE_FILE=docker-compose.yml:docker-compose.dev.yml
docker compose up -d
Docker Profiles for Optional Services
Start monitoring stack:
# With monitoring services
docker compose --profile monitoring up -d
# Without monitoring (default)
docker compose up -d
Monitoring services:
- Prometheus (port 9090)
- Grafana (port 3001)
- Alertmanager (port 9093)
- cAdvisor (port 8080)
Build Arguments
Pass build-time arguments:
# Build with Node.js version argument
docker compose build --build-arg NODE_VERSION=20.11.0 api
# Set in docker-compose.yml
services:
api:
build:
context: ./api
args:
- NODE_VERSION=${NODE_VERSION:-20}
Health Checks
Check service health:
# View health status
docker compose ps
# Inspect health check
docker inspect --format='{{json .State.Health}}' api | jq
# Wait for healthy
docker compose up -d api
docker compose exec api sh -c 'while ! wget -q -O- http://localhost:4000/health; do sleep 1; done'
Troubleshooting
Container Exits Immediately
Problem: Container starts then stops.
Solution:
# Check logs for errors
docker compose logs api
# Common causes:
# 1. Missing .env file
# 2. Database connection failed
# 3. Syntax error in code
# 4. Port already in use
# Start with interactive mode to see error
docker compose run --rm api npm run dev
Volume Mount Not Working
Problem: Code changes don't appear in container.
Solution:
# Check volume mounts
docker inspect api | grep -A 20 "Mounts"
# Verify volume path
docker compose exec api ls -la /app
# Recreate container
docker compose rm -sf api
docker compose up -d api
Permission Errors
Problem: Permission denied errors in container.
Solution:
# Check file ownership
docker compose exec api ls -la /app
# Fix permissions on host
sudo chown -R $(whoami):$(whoami) ./api
# Or run container as current user (docker-compose.yml)
services:
api:
user: "${UID}:${GID}"
Port Conflicts
Problem: Port already in use.
Solution:
# Find process using port
lsof -ti:4000 | xargs kill -9
# Or change port in docker-compose.yml
services:
api:
ports:
- "4002:4000" # Host:Container
# Or use .env
API_PORT=4002
Database Connection Failed
Problem: API cannot connect to PostgreSQL.
Solution:
# Check database is running
docker compose ps v2-postgres
# Check database logs
docker compose logs v2-postgres
# Test connection
docker compose exec api sh -c 'wget -qO- http://v2-postgres:5432 || echo "Not reachable"'
# Verify DATABASE_URL
docker compose exec api sh -c 'echo $DATABASE_URL'
# Restart database
docker compose restart v2-postgres
Out of Disk Space
Problem: No space left on device.
Solution:
# Check Docker disk usage
docker system df
# Remove unused images
docker image prune -a
# Remove unused volumes
docker volume prune
# Remove build cache
docker builder prune
# Full cleanup
docker system prune -a --volumes
Container Running Out of Memory
Problem: Container crashes with OOM.
Solution:
# Check container stats
docker stats api
# Increase Docker memory limit (Docker Desktop → Preferences → Resources)
# Or set memory limit in docker-compose.yml
services:
api:
mem_limit: 2g
memswap_limit: 2g
Slow Performance on macOS/Windows
Problem: Slow hot reload, high CPU usage.
Solution:
- Use delegated volume mounts:
services:
api:
volumes:
- ./api:/app:delegated
- Reduce file watching:
// vite.config.ts
export default {
server: {
watch: {
ignored: ['**/node_modules/**', '**/dist/**']
}
}
}
- Switch to local development:
docker compose up -d v2-postgres redis
cd api && npm run dev
cd admin && npm run dev
Best Practices
Development Workflow
-
Start services in background:
docker compose up -d api admin -
Watch logs in separate terminal:
docker compose logs -f api admin -
Make code changes:
- Hot reload picks up changes automatically
-
Type-check before commit:
docker compose exec api npm run type-check docker compose exec admin npm run type-check -
Stop services when done:
docker compose stop
Container Naming
Use meaningful service names in docker-compose.yml:
services:
api: # Not "backend" or "server"
admin: # Not "frontend" or "ui"
v2-postgres: # Not "db" (version-specific)
redis: # Standard name
Environment Variables
-
Use .env file (not docker-compose.yml):
# .env API_PORT=4000 ADMIN_PORT=3000 -
Reference in docker-compose.yml:
services: api: environment: - API_PORT=${API_PORT} -
Don't commit .env (use .env.example).
Volume Management
-
Named volumes for data:
volumes: v2-postgres-data: # Persistent database -
Bind mounts for code:
volumes: - ./api:/app # Live code sync -
Anonymous volumes for dependencies:
volumes: - /app/node_modules # Isolate from host
Log Management
-
Use log rotation:
services: api: logging: driver: "json-file" options: max-size: "10m" max-file: "3" -
Filter logs with grep:
docker compose logs -f api | grep ERROR -
Export logs for analysis:
docker compose logs > debug-logs.txt
Quick Reference
Essential Commands
# Start
docker compose up -d api admin
# Stop
docker compose stop
# Restart
docker compose restart api
# Logs
docker compose logs -f api
# Execute command
docker compose exec api npm run type-check
# Shell access
docker compose exec api sh
# Rebuild
docker compose build --no-cache api
# Clean up
docker compose down -v
Service Health Checks
# Check status
docker compose ps
# Test API
curl http://localhost:4000/health
# Test Admin
curl http://localhost:3000
# Test database
docker compose exec v2-postgres psql -U changemaker_v2 -d changemaker_v2_db -c "SELECT 1"
# Test Redis
docker compose exec redis redis-cli -a password ping
Quick Reset
# Full reset (DELETES DATA)
docker compose down -v
docker compose build --no-cache
docker compose up -d
docker compose exec api npx prisma migrate deploy
docker compose exec api npx prisma db seed
Related Documentation
- Setup: Local Development Setup
- Commands: NPM Commands Reference
- Database: Migrations Guide
- Debugging: Debugging Guide
- Deployment: Docker Compose Deployment
Summary
You now know:
- ✅ When to use Docker vs local development
- ✅ How to start and stop services
- ✅ How to watch and filter logs
- ✅ How to execute commands in containers
- ✅ How hot reload works with volume mounts
- ✅ How to perform database operations in Docker
- ✅ How to rebuild and clean up containers
- ✅ How to debug containerized services
- ✅ Advanced workflows and best practices
Quick Start:
docker compose up -d api admin v2-postgres redis
docker compose logs -f api admin
# Make changes → Hot reload!
docker compose exec api npm run type-check
docker compose stop