Gyrinx is deployed on Google Cloud Platform using Cloud Run for the application and Cloud SQL for the database. The deployment process is fully automated through GitHub and Google Cloud Build.
graph LR
subgraph "GitHub"
Repo[Repository]
Actions[GitHub Actions]
end
subgraph "Google Cloud"
Build[Cloud Build]
Registry[Container Registry]
CloudRun[Cloud Run]
CloudSQL[Cloud SQL]
LoadBalancer[Load Balancer]
end
Repo --> Actions
Actions --> Build
Build --> Registry
Registry --> CloudRun
CloudRun --> CloudSQL
LoadBalancer --> CloudRun
- Code Push - Developer pushes to
mainbranch - GitHub Actions - Runs tests and checks
- Cloud Build Trigger - Automatically triggered on main branch changes
- Container Build - Docker image built from
Dockerfile - Deploy to Cloud Run - New image deployed automatically
- Database Migrations - Run automatically on container startup
cloudbuild.yaml
steps:
# Build Docker image
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'gcr.io/$PROJECT_ID/gyrinx:$COMMIT_SHA', '.']
# Push to Container Registry
- name: 'gcr.io/cloud-builders/docker'
args: ['push', 'gcr.io/$PROJECT_ID/gyrinx:$COMMIT_SHA']
# Deploy to Cloud Run
- name: 'gcr.io/cloud-builders/gcloud'
args: ['run', 'deploy', 'gyrinx',
'--image', 'gcr.io/$PROJECT_ID/gyrinx:$COMMIT_SHA',
'--region', 'europe-west2',
'--platform', 'managed']- Database: Cloud SQL PostgreSQL
- Static Files: Served by WhiteNoise
- Security: HTTPS enforced, secure headers
- Scaling: Automatic based on traffic
# Database configuration
DB_CONFIG='{"user": "username", "password": "password"}'
DB_NAME=gyrinx
DB_HOST=cloud_sql_proxy_ip
DB_PORT=5432
# Django settings
SECRET_KEY=production_secret_key
ALLOWED_HOSTS='["gyrinx.app", "www.gyrinx.app"]'
CSRF_TRUSTED_ORIGINS='["https://gyrinx.app"]'
# External services
GOOGLE_ANALYTICS_ID=GA_tracking_id
PATREON_HOOK_SECRET=patreon_webhook_secretMigrations run automatically when the container starts:
# In Dockerfile
ENTRYPOINT ["./docker/entrypoint.sh"]# docker/entrypoint.sh
#!/bin/bash
python manage.py migrate --noinput
python manage.py collectstatic --noinput
exec "$@"- Automatic Backups: Cloud SQL provides automatic daily backups
- Point-in-time Recovery: Available for disaster recovery
- Manual Backups: Can be triggered before major deployments
# Connect to production database (requires Cloud SQL Proxy)
gcloud sql connect gyrinx-app-bootstrap-db --user=postgres
# Run migrations manually (if needed)
gcloud run jobs execute migrate-job --region=europe-west2- Metrics: Request count, latency, memory usage
- Logs: Application logs and access logs
- Alerts: Configured for high error rates and latency
# In Django urls.py
urlpatterns = [
path('health/', health_check_view, name='health'),
]
def health_check_view(request):
# Basic health check
return JsonResponse({'status': 'healthy'})- Discord Integration: Alerts sent to
#opschannel - Error Tracking: 500 errors and exceptions
- Performance Monitoring: Slow requests and high memory usage
- Automatic SSL: Cloud Run provides automatic SSL certificates
- Security Headers: Configured in Django settings
- CSRF Protection: Enforced for all POST requests
- IAM Roles: Principle of least privilege
- Service Accounts: Separate accounts for different services
- Secret Management: Environment variables for sensitive data
- Container Scanning: Automatic vulnerability scanning
- Dependency Updates: Regular security updates
- Code Analysis: GitHub Security Advisories
# service.yaml
spec:
template:
metadata:
annotations:
autoscaling.knative.dev/maxScale: "10"
autoscaling.knative.dev/minScale: "0"
run.googleapis.com/cpu-throttling: "false"
spec:
containerConcurrency: 80
timeoutSeconds: 900
containers:
- resources:
limits:
cpu: "2"
memory: "2Gi"- Connection Pooling: Configured in Django settings
- Read Replicas: Available for read-heavy workloads
- Query Optimization: Regular performance monitoring
- Database Backups: Daily automatic backups with 7-day retention
- Point-in-time Recovery: Up to 7 days
- Code Repository: Git provides complete history
- Container Images: Stored in Container Registry
# Restore from backup (example)
gcloud sql backups restore BACKUP_ID --restore-instance=gyrinx-restored
# Rollback deployment
gcloud run deploy gyrinx --image=gcr.io/PROJECT/gyrinx:PREVIOUS_SHA- Tests pass in CI/CD
- Database migrations tested locally
- Feature flags configured (if applicable)
- Monitoring alerts updated
- Health check passes
- Key user flows tested
- Error rates monitored
- Performance metrics checked
Migration Failures
# Check migration status
python manage.py showmigrations
# Apply specific migration
python manage.py migrate app_name migration_nameContainer Start Failures
# Check Cloud Run logs
gcloud logging read "resource.type=cloud_run_revision" --limit=50
# Debug locally
docker run -it --rm gyrinx:latest /bin/bashDatabase Connection Issues
# Test database connection
python manage.py dbshell
# Check Cloud SQL status
gcloud sql instances describe gyrinx-app-bootstrap-db- Monitor Cloud Run metrics for CPU/memory usage
- Check database query performance
- Review Django debug toolbar output locally
- Auto-scaling: Scales to zero when not in use
- Right-sizing: Monitor resource usage and adjust limits
- Database Sizing: Regular review of database performance tiers
- Budget Alerts: Set up billing alerts
- Resource Usage: Regular review of Cloud Console metrics
- Optimization Opportunities: Identify unused resources
- Consider adding a staging environment for testing
- Could use Cloud Run with separate database
- Automated testing on staging before production
- Application Performance Monitoring (APM)
- Real User Monitoring (RUM)
- Error tracking with detailed stack traces
- Canary deployments
- Blue-green deployments
- Automated rollback on failures