Docker Compose Deployment Guide¶
Complete guide for deploying the New Hires Reporting system using Docker Compose with pre-built images.
Prerequisites¶
Before deploying, ensure you have completed:
- [ ] Docker 20.10+ installed
- [ ] Docker Compose 2.0+ installed
- [ ] AWS account with Bedrock access (see AWS Setup)
- [ ] AWS IAM credentials with Bedrock invoke permissions
- [ ] ECR authentication configured
- [ ] Ports 8080 and 8000 available
- [ ] Minimum 4 GB RAM, 20 GB disk space
Quick Start (10 Minutes)¶
Step 1: Create Environment Configuration¶
Create your .env file:
Minimum required .env:
# AWS Bedrock Configuration (REQUIRED)
AWS_ACCESS_KEY_ID=your_aws_access_key_here
AWS_SECRET_ACCESS_KEY=your_aws_secret_key_here
AWS_REGION=us-east-1
# Docker Image Version (REQUIRED)
IMAGE_TAG=sha-1234567
# Database Configuration (Optional - defaults are fine)
POSTGRES_PASSWORD=change_this_in_production
POSTGRES_USER=newhires
POSTGRES_DB=newhires
See Environment Variables for complete reference.
Step 2: Authenticate to AWS ECR¶
# Install AWS CLI if not already installed
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
# Configure AWS credentials
aws configure
# Enter your AWS Access Key ID and Secret Access Key
# Region: us-east-1
# Authenticate Docker to ECR
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin \
878796852397.dkr.ecr.us-east-1.amazonaws.com
Expected output: Login Succeeded
Step 3: Pull Docker Images¶
This downloads four images: - PostgreSQL: ~150MB - Backend API: ~500MB - Workers: ~500MB - Frontend: ~100MB
Step 4: Start Services¶
Expected output:
Creating network "newhires-reporting_default" ... done
Creating volume "newhires-reporting_postgres-data" ... done
Creating newhires-db ... done
Creating newhires-backend ... done
Creating newhires-workers ... done
Creating newhires-frontend ... done
Step 5: Initialize Database¶
Expected output:
Step 6: Verify Deployment¶
# Check all containers are running
docker ps
# Test backend health
curl http://localhost:8000/health
# Expected response:
# {"status":"healthy"}
# Access web interface
# Open browser to: http://your-server:8080
Detailed Production Deployment¶
Directory Structure¶
Organize your deployment files:
# Create deployment directory
mkdir -p /opt/newhires-reporting
cd /opt/newhires-reporting
# Directory structure:
# /opt/newhires-reporting/
# ├── docker-compose.prod.yml
# ├── .env
# └── backups/ (create for database backups)
Understanding docker-compose.prod.yml¶
The production compose file defines four services:
services:
db:
image: postgres:16-alpine
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
backend:
image: 878796852397.dkr.ecr.us-east-1.amazonaws.com/newhires/backend:${IMAGE_TAG}
ports:
- "8000:8000"
depends_on:
- db
environment:
- DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
workers:
image: 878796852397.dkr.ecr.us-east-1.amazonaws.com/newhires/workers:${IMAGE_TAG}
depends_on:
- db
environment:
- DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
- AWS_REGION=${AWS_REGION}
frontend:
image: 878796852397.dkr.ecr.us-east-1.amazonaws.com/newhires/frontend:${IMAGE_TAG}
ports:
- "8080:80"
depends_on:
- backend
Key Points: - Uses pre-built ECR images (no local code needed) - Database persisted in Docker volume - Workers process jobs in background - Frontend proxies API requests to backend
Environment Configuration¶
Create a secure .env file:
Production .env template:
###############################
# Docker Image Configuration
###############################
IMAGE_TAG=sha-a85b396
###############################
# AWS Bedrock Configuration
###############################
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
AWS_REGION=us-east-1
# Optional: Override default model
# BEDROCK_MODEL_ID=us.anthropic.claude-sonnet-4-5-20250929-v1:0
###############################
# Database Configuration
###############################
POSTGRES_DB=newhires
POSTGRES_USER=newhires
POSTGRES_PASSWORD=CHANGE_THIS_SECURE_PASSWORD_123
###############################
# Worker Configuration
###############################
# Polling interval in seconds
POLL_INTERVAL=5
# Maximum Bedrock API calls at once
MAX_CONCURRENT_BEDROCK_CALLS=2
# Maximum retry attempts per job
MAX_AI_ATTEMPTS=5
###############################
# Frontend Configuration
###############################
# Backend API URL (leave default for same-host deployment)
VITE_API_URL=http://localhost:8000/api/v1
Security
- Never commit
.envfiles to version control - Use strong passwords for
POSTGRES_PASSWORD - Rotate AWS credentials every 90 days
- Set file permissions:
chmod 600 .env
Deploy Services¶
Step-by-step deployment:
# 1. Ensure you're in the deployment directory
cd /opt/newhires-reporting
# 2. Verify .env file exists and is configured
ls -la .env
cat .env | grep AWS_ACCESS_KEY_ID # Should show your key
# 3. Authenticate to ECR (credentials expire after 12 hours)
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin \
878796852397.dkr.ecr.us-east-1.amazonaws.com
# 4. Pull latest images
docker-compose -f docker-compose.prod.yml pull
# 5. Stop any running containers (if updating)
docker-compose -f docker-compose.prod.yml down
# 6. Start services
docker-compose -f docker-compose.prod.yml up -d
# 7. Wait for database to be ready (~10 seconds)
sleep 10
# 8. Run database migrations
docker exec newhires-backend alembic upgrade head
# 9. Verify all services are running
docker-compose -f docker-compose.prod.yml ps
# Expected output shows 4 containers "Up"
Verify Deployment¶
Check each service is healthy:
# 1. Check Docker containers
docker ps | grep newhires
# Should show 4 containers:
# newhires-frontend
# newhires-backend
# newhires-workers
# newhires-db
# 2. Check backend API health
curl http://localhost:8000/health
# Expected: {"status":"healthy"}
# 3. Check database connection
docker exec newhires-db psql -U newhires -d newhires -c "SELECT 1;"
# Expected: Shows "1" result
# 4. Check worker is polling
docker logs newhires-workers --tail=20
# Should see: "INFO: Worker polling for jobs..."
# 5. Check frontend is accessible
curl -I http://localhost:8080
# Expected: HTTP/1.1 200 OK
# 6. Open web interface
# Navigate to: http://your-server:8080
Common Operations¶
View Logs¶
# View all service logs (follow mode)
docker-compose -f docker-compose.prod.yml logs -f
# View specific service logs
docker-compose -f docker-compose.prod.yml logs -f backend
docker-compose -f docker-compose.prod.yml logs -f workers
docker-compose -f docker-compose.prod.yml logs -f frontend
# View last 100 lines
docker-compose -f docker-compose.prod.yml logs --tail=100 workers
# View logs with timestamps
docker-compose -f docker-compose.prod.yml logs -f -t workers
# Save logs to file
docker-compose -f docker-compose.prod.yml logs > deployment_logs.txt
Restart Services¶
# Restart all services
docker-compose -f docker-compose.prod.yml restart
# Restart specific service
docker-compose -f docker-compose.prod.yml restart backend
docker-compose -f docker-compose.prod.yml restart workers
# Full restart with rebuild
docker-compose -f docker-compose.prod.yml down
docker-compose -f docker-compose.prod.yml up -d
Stop Services¶
# Stop services (preserves data)
docker-compose -f docker-compose.prod.yml stop
# Stop and remove containers (preserves volumes)
docker-compose -f docker-compose.prod.yml down
# Stop and remove everything INCLUDING DATA (DANGEROUS!)
docker-compose -f docker-compose.prod.yml down -v
Scale Workers¶
To process more jobs concurrently, scale the workers service:
# Scale to 3 worker containers
docker-compose -f docker-compose.prod.yml up -d --scale workers=3
# Scale back to 1 worker
docker-compose -f docker-compose.prod.yml up -d --scale workers=1
# Check running workers
docker ps | grep workers
Worker Scaling
- Each worker can handle
MAX_CONCURRENT_BEDROCK_CALLSconcurrent Bedrock calls - 3 workers with
MAX_CONCURRENT_BEDROCK_CALLS=2= 6 concurrent AI calls - Monitor AWS Bedrock costs when scaling workers
Update to New Version¶
When a new version is released:
# 1. Update IMAGE_TAG in .env file
nano .env
# Change: IMAGE_TAG=sha-NEW_VERSION
# 2. Re-authenticate to ECR if needed (tokens expire after 12h)
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin \
878796852397.dkr.ecr.us-east-1.amazonaws.com
# 3. Pull new images
docker-compose -f docker-compose.prod.yml pull
# 4. Stop current containers
docker-compose -f docker-compose.prod.yml down
# 5. Start with new images
docker-compose -f docker-compose.prod.yml up -d
# 6. Run database migrations (important!)
docker exec newhires-backend alembic upgrade head
# 7. Verify health
curl http://localhost:8000/health
docker-compose -f docker-compose.prod.yml logs --tail=50
Database Operations¶
Backup Database¶
# Create backup directory
mkdir -p /opt/newhires-reporting/backups
# Create backup
docker exec newhires-db pg_dump -U newhires newhires > \
/opt/newhires-reporting/backups/backup_$(date +%Y%m%d_%H%M%S).sql
# Create compressed backup
docker exec newhires-db pg_dump -U newhires newhires | \
gzip > /opt/newhires-reporting/backups/backup_$(date +%Y%m%d_%H%M%S).sql.gz
# Automated daily backup (add to crontab)
# crontab -e
# 0 2 * * * cd /opt/newhires-reporting && docker exec newhires-db pg_dump -U newhires newhires | gzip > backups/backup_$(date +\%Y\%m\%d).sql.gz
Restore Database¶
# Stop workers to prevent job processing during restore
docker-compose -f docker-compose.prod.yml stop workers
# Restore from backup
cat backups/backup_20241124.sql | \
docker exec -i newhires-db psql -U newhires -d newhires
# Or restore from compressed backup
gunzip -c backups/backup_20241124.sql.gz | \
docker exec -i newhires-db psql -U newhires -d newhires
# Restart workers
docker-compose -f docker-compose.prod.yml start workers
Access Database CLI¶
# Connect to PostgreSQL
docker exec -it newhires-db psql -U newhires -d newhires
# Example queries:
# \dt -- List tables
# SELECT COUNT(*) FROM correction_jobs;
# SELECT * FROM job_queue WHERE status='pending';
# \q -- Quit
Monitoring¶
Check Service Status¶
# View container status
docker-compose -f docker-compose.prod.yml ps
# View resource usage
docker stats
# View network connectivity
docker network inspect newhires-reporting_default
Check Worker Processing¶
# View worker logs (live)
docker logs -f newhires-workers
# Check for Bedrock API calls
docker logs newhires-workers | grep "Bedrock"
# Check job processing
docker exec newhires-db psql -U newhires -d newhires -c \
"SELECT status, COUNT(*) FROM correction_jobs GROUP BY status;"
Health Checks¶
# Backend health
curl http://localhost:8000/health
# Frontend health
curl -I http://localhost:8080
# Database health
docker exec newhires-db pg_isready -U newhires
# All services health
docker-compose -f docker-compose.prod.yml ps
Troubleshooting¶
Services Won't Start¶
# Check logs for errors
docker-compose -f docker-compose.prod.yml logs
# Common issues:
# 1. Missing or invalid .env file
ls -la .env
cat .env | head -5
# 2. ECR authentication expired (re-authenticate)
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin \
878796852397.dkr.ecr.us-east-1.amazonaws.com
# 3. Port conflicts
sudo netstat -tlnp | grep -E '8000|8080|5432'
# 4. Insufficient disk space
df -h
docker system df
# 5. Insufficient memory
free -h
docker stats --no-stream
Workers Not Processing Jobs¶
# Check worker logs
docker logs newhires-workers --tail=50
# Verify AWS credentials are set
docker exec newhires-workers env | grep AWS
# Test Bedrock access from worker
docker exec newhires-workers python3 -c "
import boto3
client = boto3.client('bedrock-runtime', region_name='us-east-1')
print('Bedrock connection successful')
"
# Check job queue
docker exec newhires-db psql -U newhires -d newhires -c \
"SELECT * FROM job_queue WHERE status='pending';"
# Restart workers
docker-compose -f docker-compose.prod.yml restart workers
Database Connection Errors¶
# Check database is running
docker ps | grep newhires-db
# Check database logs
docker logs newhires-db --tail=50
# Test database connection
docker exec newhires-db psql -U newhires -d newhires -c "SELECT 1;"
# Verify database credentials in .env
grep POSTGRES .env
# Restart database (last resort)
docker-compose -f docker-compose.prod.yml restart db
Frontend Can't Connect to Backend¶
Symptom: Frontend shows error or blank page
Solutions:
# 1. Verify backend is running
curl http://localhost:8000/health
# 2. Check frontend logs
docker logs newhires-frontend
# 3. Verify frontend environment variable
docker exec newhires-frontend cat /etc/nginx/conf.d/default.conf | grep proxy_pass
# 4. Check network connectivity
docker exec newhires-frontend wget -O- http://backend:8000/health
# 5. Restart frontend
docker-compose -f docker-compose.prod.yml restart frontend
Out of Disk Space¶
# Check disk usage
df -h
docker system df
# Clean up unused Docker resources
docker system prune -a --volumes
# Remove old images
docker image prune -a
# Remove stopped containers
docker container prune
# Check volume sizes
docker volume ls
docker system df -v
Best Practices¶
Security¶
- ✅ Set secure
POSTGRES_PASSWORDin.env - ✅ File permissions:
chmod 600 .env - ✅ Rotate AWS credentials every 90 days
- ✅ Keep Docker and images updated
- ✅ Monitor logs for unauthorized access
- ✅ Use firewall rules to restrict port access
- ✅ Consider using HTTPS with reverse proxy
Operations¶
- ✅ Backup database daily (automated cron job)
- ✅ Monitor disk space and Docker volumes
- ✅ Review logs regularly for errors
- ✅ Test update procedures in staging first
- ✅ Keep
IMAGE_TAGversion documented - ✅ Monitor AWS Bedrock costs
- ✅ Set up alerts for service failures
Performance¶
- ✅ Scale workers based on job volume
- ✅ Tune
MAX_CONCURRENT_BEDROCK_CALLSfor workload - ✅ Monitor database size and performance
- ✅ Review Bedrock token usage
- ✅ Use
docker statsto monitor resource usage
Next Steps¶
- Configure Environment Variables - Complete configuration
- Setup Health Monitoring - Monitor your deployment
- Optional: Reverse Proxy - Add HTTPS and domain
- Troubleshooting Guide - Fix common issues
Quick Reference¶
# Deployment
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 878796852397.dkr.ecr.us-east-1.amazonaws.com
docker-compose -f docker-compose.prod.yml pull
docker-compose -f docker-compose.prod.yml up -d
docker exec newhires-backend alembic upgrade head
# Operations
docker-compose -f docker-compose.prod.yml ps # Status
docker-compose -f docker-compose.prod.yml logs -f workers # View logs
docker-compose -f docker-compose.prod.yml restart backend # Restart service
docker-compose -f docker-compose.prod.yml down # Stop all
curl http://localhost:8000/health # Health check
# Database
docker exec newhires-db pg_dump -U newhires newhires > backup.sql # Backup
docker exec -it newhires-db psql -U newhires -d newhires # CLI access
# Scaling
docker-compose -f docker-compose.prod.yml up -d --scale workers=3 # Scale workers