Production Operations
Server and infrastructure maintenance for sanctumhq.gg.
Architecture Summary
- EC2 t3.medium (us-east-2) runs the app stack via Docker Compose
- ECS Fargate Spot runs SimC worker on-demand
- Route 53 manages DNS for
sanctumhq.gg - Cloudflare Pages hosts docs at
docs.sanctumhq.gg - SES sends transactional email from
noreply@sanctumhq.gg - S3 stores uploads, game data icons, and sim results
- SQS queues SimC simulation jobs
Connecting to the Server
ssh -i ~/.ssh/sanctum-key.pem ubuntu@YOUR_ELASTIC_IP
cd ~/sanctum
Deploying Code Changes
Standard Deploy (after pushing to master)
ssh -i ~/.ssh/sanctum-key.pem ubuntu@YOUR_ELASTIC_IP
cd ~/sanctum
git pull origin master
docker compose -f docker-compose.yml -f docker-compose.prod.yml up --build -d
The --build flag rebuilds only containers whose source changed. Unchanged containers restart instantly.
Deploy API Only (skip GDL rebuild)
docker compose -f docker-compose.yml -f docker-compose.prod.yml up --build -d api
Deploy Frontend Only
docker compose -f docker-compose.yml -f docker-compose.prod.yml up --build -d web
docker compose -f docker-compose.yml -f docker-compose.prod.yml restart nginx
Rebuild Everything from Scratch
docker compose -f docker-compose.yml -f docker-compose.prod.yml down
docker compose -f docker-compose.yml -f docker-compose.prod.yml up --build -d
This preserves database data (stored in Docker volumes). To also wipe data, add -v to the down command.
Database Operations
Run Prisma Migrations
Migrations run automatically on API container startup. To run manually:
docker compose exec api npx prisma migrate deploy
Check Migration Status
docker compose exec api npx prisma migrate status
Connect to PostgreSQL Directly
docker compose exec postgres psql -U sanctum_prod -d sanctum
For GDL databases:
docker compose exec gdl-postgres psql -U sanctum_prod -d sanctum_catalog
docker compose exec gdl-postgres psql -U sanctum_prod -d sanctum_characters
Manual Database Backup
docker compose exec postgres pg_dump -U sanctum_prod sanctum > backup-$(date +%Y%m%d).sql
To back up to S3:
docker compose exec postgres pg_dump -U sanctum_prod sanctum | \
aws s3 cp - s3://sanctum-app/backups/sanctum-$(date +%Y%m%d).sql
Restore from Backup
docker compose exec -T postgres psql -U sanctum_prod sanctum < backup-20260313.sql
SSL Certificate Management
Certbot runs as a container and auto-renews every 12 hours. To check status:
docker compose exec certbot certbot certificates
Force Renewal
docker compose -f docker-compose.yml -f docker-compose.prod.yml run --rm certbot \
renew --force-renewal
docker compose -f docker-compose.yml -f docker-compose.prod.yml restart nginx
First-Time SSL Setup
If setting up SSL on a fresh server:
./scripts/init-ssl.sh admin@sanctumhq.gg
Monitoring
View Logs
# All services
docker compose logs -f
# Specific service
docker compose logs -f api
docker compose logs -f gdl
docker compose logs -f nginx
# Last 100 lines
docker compose logs --tail 100 api
Check Container Status
docker compose ps
Check Disk Usage
df -h
docker system df
Clean Up Docker Disk Space
docker system prune -f
docker image prune -a -f
Check Memory and CPU
free -h
htop
SimC Worker
Update SimC Worker Image
After code changes to services/simc/:
cd ~/sanctum
# Re-login to ECR (tokens expire after 12 hours)
aws ecr get-login-password --region us-east-2 | \
docker login --username AWS --password-stdin \
YOUR_ACCOUNT_ID.dkr.ecr.us-east-2.amazonaws.com
# Rebuild and push
docker build --target production \
-t YOUR_ACCOUNT_ID.dkr.ecr.us-east-2.amazonaws.com/sanctum-simc-worker:latest \
services/simc/
docker push YOUR_ACCOUNT_ID.dkr.ecr.us-east-2.amazonaws.com/sanctum-simc-worker:latest
New ECS tasks will automatically use the latest image.
Check SimC Job Status
- SQS Console: Check
sim-jobsqueue for pending messages - ECS Console: Check
sanctumcluster for running/stopped tasks - CloudWatch Logs: Check
/ecs/sanctum-simc-workerlog group - Dead letter queue: Check
sim-jobs-dlqfor failed jobs
Environment Variables
Editing Production Config
nano ~/sanctum/apps/api/.env
After changing env vars, restart the API:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d api
Rotating Secrets
Generate new secrets:
openssl rand -base64 32 # JWT_SECRET, SESSION_SECRET
openssl rand -hex 32 # ENCRYPTION_KEY
Update apps/api/.env, then restart the API. Note: rotating JWT_SECRET invalidates all existing user sessions.
DNS (Route 53)
Current records:
| Record | Type | Value |
|---|---|---|
| sanctumhq.gg | A | EC2 Elastic IP |
| www.sanctumhq.gg | CNAME | sanctumhq.gg |
| docs.sanctumhq.gg | CNAME | sanctum-docs.pages.dev |
| SES DKIM records | CNAME | (auto-generated) |
Common Issues
Container Won't Start
docker compose logs api # Check for errors
docker compose ps # Check status
docker compose restart api # Try restarting
Out of Disk Space
docker system prune -a -f # Remove unused images
docker volume prune -f # Remove unused volumes (careful!)
SSL Certificate Expired
docker compose -f docker-compose.yml -f docker-compose.prod.yml run --rm certbot renew
docker compose -f docker-compose.yml -f docker-compose.prod.yml restart nginx
Can't SSH In
Check that your IP hasn't changed -- the security group limits SSH to "My IP". Update the inbound rule in the EC2 Console if your IP changed.
API Health Check Failing
docker compose exec api wget -qO- http://localhost:3001/api/v1/health
If it fails, check logs and ensure the database is healthy:
docker compose exec postgres pg_isready -U sanctum_prod -d sanctum
Cost Monitoring
Check your AWS bill: Billing > Bills in the AWS Console. Expected ~$33-38/month:
- EC2 t3.medium: ~$30
- Route 53: $0.50
- S3 + ECR: < $2
- SQS + SES: Free tier
- ECS Fargate Spot: $0-5 (per sim usage)