This guide explains how to run the complete grocery list application stack with authentication using Docker Compose.
The application consists of four main services:
┌─────────────────────────────────────────────────────────────┐
│ Docker Compose Stack │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌─────────────────┐ │
│ │ Frontend │ │ Auth Server │ │ Zero-cache │ │
│ │ (React) │ │ (Express) │ │ (Real-time) │ │
│ │ Port: 3000 │ │ Port: 3001 │ │ Port: 4848 │ │
│ └──────┬───────┘ └──────┬───────┘ └────────┬────────┘ │
│ │ │ │ │
│ └─────────────────┼────────────────────┘ │
│ │ │
│ ┌────────┴────────┐ │
│ │ PostgreSQL │ │
│ │ Port: 5432 │ │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘
- Image: postgres:16
- Port: 5432
- Purpose: Stores user data, grocery items, and authentication tokens
- Features:
- Logical replication enabled for Zero-cache sync
- Auto-initializes schema on first run
- Health checks for service dependencies
- Persistent volume for data storage
- Build: Dockerfile.server
- Port: 3001
- Purpose: Handles user authentication, JWT tokens, and API endpoints
- Features:
- JWT-based authentication
- Token refresh mechanism
- Rate limiting
- CORS configuration
- Hot-reload in development mode
- Health check endpoint at
/health
- Image: rocicorp/zero-cache:latest
- Port: 4848
- Purpose: Real-time synchronization for offline-first functionality
- Features:
- Syncs with PostgreSQL using logical replication
- Local replica storage
- Automatic conflict resolution
- Health check endpoint
- Build: Dockerfile.frontend
- Port: 3000
- Purpose: React application with Vite
- Features:
- Hot-reload in development mode
- Proxy to auth-server for API calls
- Connects to Zero-cache for real-time sync
- Production build with nginx
Start all services:
docker compose up -dView logs:
docker compose logs -fStop all services:
docker compose downStart only database:
docker compose up -d postgresStart database and auth server:
docker compose up -d postgres auth-serverStart everything except frontend (if running frontend locally):
docker compose up -d postgres auth-server zero-cacheOnce running, access services at:
- Frontend: http://localhost:3000
- Auth API: http://localhost:3001
- Auth Health Check: http://localhost:3001/health
- Auth API Docs: http://localhost:3001/api
- Zero-cache: http://localhost:4848
- PostgreSQL: localhost:5432
The docker-compose.yml includes development defaults. For production, create a .env file:
# Server Configuration
NODE_ENV=production
PORT=3001
# Database Configuration
DB_HOST=postgres
DB_PORT=5432
DB_NAME=grocery_db
DB_USER=grocery
DB_PASSWORD=<strong-password>
# JWT Configuration (CHANGE THESE!)
JWT_ACCESS_SECRET=<generate-with-openssl-rand-base64-32>
JWT_REFRESH_SECRET=<generate-with-openssl-rand-base64-32>
JWT_ACCESS_EXPIRY=15m
JWT_REFRESH_EXPIRY=7d
# Security
BCRYPT_ROUNDS=12
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=100
# CORS
CORS_ORIGIN=https://your-domain.com
# Zero Configuration
ZERO_AUTH_SECRET=<generate-with-openssl-rand-base64-32>Generate secure secrets:
openssl rand -base64 32The database schema is automatically initialized on first run via the mounted schema file.
To manually initialize or reset the database:
# Connect to the database container
docker compose exec postgres psql -U grocery -d grocery_db
# Or run schema file manually
docker compose exec -T postgres psql -U grocery -d grocery_db < server/db/schema.sqlAll services include health checks. Check service health:
# View service status
docker compose ps
# Check auth server health
curl http://localhost:3001/health
# Check zero-cache health
curl http://localhost:4848/health
# Check database health
docker compose exec postgres pg_isready -U grocery -d grocery_dbThe following volumes store persistent data:
postgres-data: PostgreSQL database fileszero-data: Zero-cache replica databaseserver-node-modules: Node modules (performance optimization)frontend-node-modules: Node modules (performance optimization)
# Create backup
docker compose exec postgres pg_dump -U grocery grocery_db > backup.sql
# Restore backup
docker compose exec -T postgres psql -U grocery -d grocery_db < backup.sqlWARNING: This will delete all data!
docker compose down -vThe docker-compose setup supports hot-reload for both frontend and backend:
- Frontend: Mount src directory with live reload
- Auth Server: Mount server directory with nodemon
To develop locally while using Docker for infrastructure only:
# Start infrastructure services only
docker compose up -d postgres zero-cache
# Run frontend locally
pnpm run dev
# Run auth server locally (in another terminal)
pnpm run server:devView logs for specific service:
docker compose logs -f auth-server
docker compose logs -f postgres
docker compose logs -f zero-cacheAccess container shell:
docker compose exec auth-server sh
docker compose exec postgres bashRestart specific service:
docker compose restart auth-serverRebuild and restart:
docker compose up -d --build auth-server# Build all services
docker compose -f docker-compose.prod.yml build
# Build specific service
docker compose -f docker-compose.prod.yml build auth-serverBefore deploying to production:
- Change all default passwords
- Generate strong JWT secrets
- Set NODE_ENV=production
- Configure proper CORS origins
- Enable HTTPS (use reverse proxy like nginx/Traefik)
- Set up database backups
- Configure log aggregation
- Enable container resource limits
- Use secrets management (Docker secrets, AWS Secrets Manager, etc.)
- Review and harden PostgreSQL configuration
- Enable security scanning (Snyk, Trivy, etc.)
Add resource limits for production (add to docker-compose.yml):
services:
auth-server:
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256MCheck logs:
docker compose logs <service-name>Check if port is already in use:
# Linux/Mac
lsof -i :3001
netstat -tuln | grep 3001
# Windows
netstat -ano | findstr :3001# Test database connection
docker compose exec postgres psql -U grocery -d grocery_db -c "SELECT 1"
# Check database logs
docker compose logs postgres
# Restart database
docker compose restart postgres# Check if service is running
docker compose ps auth-server
# Check health
curl http://localhost:3001/health
# View detailed logs
docker compose logs -f auth-server
# Restart service
docker compose restart auth-server# Check zero-cache logs
docker compose logs -f zero-cache
# Verify PostgreSQL replication slots
docker compose exec postgres psql -U grocery -d grocery_db -c "SELECT * FROM pg_replication_slots;"
# Restart zero-cache
docker compose restart zero-cacheIf you encounter persistent issues, try a clean start:
# Stop all services
docker compose down
# Remove volumes (WARNING: deletes data)
docker compose down -v
# Remove images
docker compose down --rmi all
# Rebuild and start fresh
docker compose up -d --buildThe stack uses a custom bridge network grocery-network for inter-service communication.
Services communicate using their service names:
- Frontend → Auth Server:
http://auth-server:3001 - Auth Server → PostgreSQL:
postgres:5432 - Zero-cache → PostgreSQL:
postgres:5432
External access is through exposed ports:
- Frontend: localhost:3000
- Auth Server: localhost:3001
- Zero-cache: localhost:4848
- PostgreSQL: localhost:5432
- Use volumes for node_modules: Already configured in docker-compose.yml
- Layer caching: Order Dockerfile commands from least to most frequently changed
- Multi-stage builds: Production images use multi-stage builds for smaller size
- Resource allocation: Adjust Docker Desktop/Engine memory limits if needed
- Prune unused resources: Regularly run
docker system prune -a
docker statsdocker system prune -a --volumes# Export
docker save -o auth-server.tar grocery-auth-server
# Import
docker load -i auth-server.tardocker compose exec auth-server env
docker compose exec auth-server ps aux
docker compose exec auth-server ls -la /appFor issues or questions:
- Check the logs:
docker compose logs -f - Review environment variables
- Verify database connection
- Check service health endpoints
- Ensure ports aren't already in use
After starting the services:
- Access the frontend at http://localhost:3000
- Register a new user account
- Test authentication flow
- Verify real-time sync with Zero-cache
- Create and manage grocery items
For API documentation, visit http://localhost:3001/api