Deployment Guide
This guide covers deploying Noumaris to local, staging, and production environments.
Local Deployment
For local development setup, see Onboarding Guide. This section covers production-like local deployment.
Docker Compose Stack
Full local stack with all services:
# Start all services
docker-compose up -d
# Services running:
# - PostgreSQL: localhost:5433
# - Keycloak: localhost:8081Keycloak Configuration
Configure Keycloak via Terraform:
bash scripts/setup-local-keycloak.shThis script:
- Waits for Keycloak to be ready
- Initializes Terraform
- Creates realm, roles, clients
- Outputs service account credentials
Manual Terraform:
cd terraform/keycloak
terraform init
terraform applyDatabase Setup
cd backend
# Run migrations
alembic upgrade head
# Seed with templates and demo data
python seed_db.py
python scripts/seed_features.pyProduction Deployment
Noumaris runs on Google Cloud Run with multi-region deployment.
Architecture
┌─────────────────────────────────────┐
│ Cloudflare (Frontend) │
│ - Static hosting │
│ - CDN │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Google Cloud Load Balancer │
│ - SSL termination │
│ - Multi-region routing │
└─────────────────────────────────────┘
│
┌─────┴─────┐
│ │
▼ ▼
┌───────────┐ ┌───────────┐
│ Cloud Run │ │ Cloud Run │
│ Canada │ │ US │
│ (primary)│ │ (replica) │
└───────────┘ └───────────┘
│ │
└─────┬─────┘
▼
┌─────────────┐
│ Cloud SQL │
│ (Private IP)│
└─────────────┘Prerequisites
- Google Cloud Project with billing enabled
- APIs enabled: Cloud Run, Cloud SQL, Secret Manager, VPC, Artifact Registry
- gcloud CLI installed and authenticated
- Terraform installed (v1.0+)
# Authenticate
gcloud auth login
gcloud config set project YOUR_PROJECT_ID
# Enable APIs
gcloud services enable run.googleapis.com
gcloud services enable sqladmin.googleapis.com
gcloud services enable secretmanager.googleapis.com
gcloud services enable vpcaccess.googleapis.com
gcloud services enable artifactregistry.googleapis.comInfrastructure Setup
Terraform manages all infrastructure:
cd terraform
# Initialize
terraform init
# Create staging workspace
terraform workspace new staging
terraform workspace select staging
# Review plan
terraform plan
# Apply (creates all resources)
terraform applyResources created:
- Cloud SQL instance (PostgreSQL)
- VPC network and connector
- Cloud Run services (Canada + US)
- Secret Manager secrets
- Artifact Registry repository
- Load balancer
- Cloud Build triggers
Secrets Management
Store secrets in Google Secret Manager:
# Create secrets
echo -n "your-anthropic-key" | gcloud secrets create anthropic-api-key --data-file=-
echo -n "your-deepgram-key" | gcloud secrets create deepgram-api-key --data-file=-
echo -n "postgresql://..." | gcloud secrets create database-url --data-file=-
# Grant Cloud Run access
gcloud secrets add-iam-policy-binding anthropic-api-key \
--member=serviceAccount:[email protected] \
--role=roles/secretmanager.secretAccessorDatabase Migration
- Connect to Cloud SQL via Cloud SQL Proxy:
# Download proxy
curl -o cloud-sql-proxy https://dl.google.com/cloudsql/cloud_sql_proxy.darwin.amd64
chmod +x cloud-sql-proxy
# Start proxy
./cloud-sql-proxy PROJECT:REGION:INSTANCE &
# Run migrations
cd backend
DATABASE_URL="postgresql://user:[email protected]:5432/dbname" alembic upgrade head- Or use Cloud Build (automated):
# cloudbuild.yaml includes migration step
steps:
- name: 'migrate'
entrypoint: 'bash'
args: ['alembic', 'upgrade', 'head']Deployment Methods
Method 1: Cloud Build (Automated - Recommended)
Push to develop branch triggers automatic deployment:
git push origin developCloud Build:
- Builds Docker image
- Pushes to Artifact Registry
- Runs database migrations
- Deploys to Cloud Run (Canada + US in parallel)
- Health checks validate deployment
cloudbuild.yaml handles this automatically.
Method 2: Manual Deployment
cd backend
# Build and push image
gcloud builds submit --tag gcr.io/PROJECT_ID/noumaris-backend:latest
# Deploy to Cloud Run (Canada)
gcloud run deploy noumaris-backend-canada \
--image gcr.io/PROJECT_ID/noumaris-backend:latest \
--region northamerica-northeast1 \
--platform managed \
--vpc-connector vpc-connector-canada \
--set-env-vars KEYCLOAK_URL=https://keycloak.noumaris.com \
--set-secrets=ANTHROPIC_API_KEY=anthropic-api-key:latest \
--set-secrets=DEEPGRAM_API_KEY=deepgram-api-key:latest \
--set-secrets=DATABASE_URL=database-url:latest \
--min-instances 1 \
--max-instances 10 \
--allow-unauthenticated
# Deploy to US
gcloud run deploy noumaris-backend-us \
--image gcr.io/PROJECT_ID/noumaris-backend:latest \
--region us-central1 \
--platform managed \
--vpc-connector vpc-connector-us \
--set-env-vars KEYCLOAK_URL=https://keycloak.noumaris.com \
--set-secrets=ANTHROPIC_API_KEY=anthropic-api-key:latest \
--set-secrets=DEEPGRAM_API_KEY=deepgram-api-key:latest \
--set-secrets=DATABASE_URL=database-url:latest \
--min-instances 0 \
--max-instances 10 \
--allow-unauthenticatedFrontend Deployment
Frontend is deployed to Cloudflare Pages:
- Build for production:
cd frontend
npm run build:prod- Deploy via Cloudflare Pages:
- Connect GitHub repository
- Build command:
cd frontend && npm run build:prod - Output directory:
frontend/dist - Environment variables: Set in Cloudflare dashboard
Environment variables:
VITE_API_URL=https://api.noumaris.com
VITE_KEYCLOAK_URL=https://keycloak.noumaris.com
VITE_KEYCLOAK_REALM=noumaris
VITE_KEYCLOAK_CLIENT_ID=fastapi-frontendKeycloak Deployment
Keycloak runs on Cloud Run (separate service):
# Deploy Bitnami Keycloak image
gcloud run deploy keycloak \
--image bitnami/keycloak:latest \
--region northamerica-northeast1 \
--set-env-vars KEYCLOAK_ADMIN=admin \
--set-env-vars KEYCLOAK_ADMIN_PASSWORD=SECURE_PASSWORD \
--set-env-vars KEYCLOAK_DATABASE_VENDOR=postgres \
--set-secrets=KEYCLOAK_DATABASE_URL=keycloak-database-url:latest \
--min-instances 1 \
--max-instances 3After deployment, configure via Terraform:
cd terraform/keycloak
terraform init
terraform apply -var 'keycloak_url=https://keycloak.noumaris.com'Rollback Procedures
Cloud Run Rollback
Cloud Run keeps revision history. Rollback to previous:
# List revisions
gcloud run revisions list --service noumaris-backend-canada --region northamerica-northeast1
# Route 100% traffic to specific revision
gcloud run services update-traffic noumaris-backend-canada \
--to-revisions noumaris-backend-canada-00042-abc=100 \
--region northamerica-northeast1Database Rollback
Downgrade migrations:
# Downgrade 1 migration
alembic downgrade -1
# Downgrade to specific revision
alembic downgrade abc123def456
# View history
alembic history⚠️ Warning: Database rollbacks can cause data loss. Always backup first:
# Backup Cloud SQL
gcloud sql backups create --instance INSTANCE_NAME
# Restore from backup
gcloud sql backups restore BACKUP_ID --backup-instance INSTANCE_NAMEMonitoring & Health Checks
Health Check Endpoint
Backend exposes /health:
curl https://api.noumaris.com/health
# Response: {"status": "healthy"}Cloud Run health checks configured in Terraform:
- Initial delay: 10 seconds
- Timeout: 5 seconds
- Period: 30 seconds
- Failure threshold: 3
Logging
View logs in Google Cloud Console or CLI:
# Tail logs
gcloud run services logs tail noumaris-backend-canada --region northamerica-northeast1
# Read recent logs
gcloud run services logs read noumaris-backend-canada --region northamerica-northeast1 --limit 100
# Filter by severity
gcloud logging read "resource.type=cloud_run_revision AND severity>=ERROR" --limit 50Monitoring
Cloud Monitoring automatically tracks:
- Request count
- Request latency (p50, p95, p99)
- Error rate
- Instance count
- CPU/memory utilization
Set up alerts:
# Create alert for error rate > 5%
gcloud alpha monitoring policies create \
--notification-channels=CHANNEL_ID \
--display-name="High Error Rate" \
--condition-display-name="Error rate > 5%" \
--condition-threshold-value=0.05Zero-Downtime Deployment
Cloud Run handles this automatically:
- New revision created
- Health checks pass
- Traffic gradually shifted (0% → 100%)
- Old revision scales down
Gradual rollout (optional):
# Split traffic 50/50
gcloud run services update-traffic noumaris-backend-canada \
--to-revisions=noumaris-backend-canada-00043=50,noumaris-backend-canada-00042=50Environment Comparison
| Environment | Purpose | Backend | Database | Keycloak |
|---|---|---|---|---|
| Local | Development | localhost:8000 | Docker (5433) | Docker (8081) |
| Staging | Testing | Cloud Run (staging) | Cloud SQL (staging) | Cloud Run (staging) |
| Production | Live | Cloud Run (multi-region) | Cloud SQL (HA) | Cloud Run (HA) |
Cost Optimization
Current Costs (as of Oct 2025)
Development: $35-40/month
- Cloud Run (scale-to-zero): ~$10
- Cloud SQL (db-f1-micro): ~$15
- Networking: ~$5
- Storage: ~$5
Production (estimated): $145-195/month
- Cloud Run (2 regions, min instances): ~$60
- Cloud SQL (HA, db-n1-standard-1): ~$80
- Load Balancer: ~$20
- Networking: ~$15
- Secret Manager: ~$5
- Monitoring: ~$10
Optimization Tips
- Cloud Run: Use scale-to-zero for dev/staging
- Cloud SQL: Use smaller instance for non-prod
- Backups: Retain only 7 days for dev
- Logs: Set 30-day retention policy
# Update log retention
gcloud logging sinks update _Default --log-filter='resource.type="cloud_run_revision"' --retention-days=30Troubleshooting Deployment
Common Issues
| Issue | Cause | Fix |
|---|---|---|
| Service won't start | Missing secrets | Verify Secret Manager permissions |
| Database connection fails | VPC connector issue | Check VPC connector in same region |
| Health check fails | App not listening on $PORT | Use PORT environment variable |
| 502/503 errors | Container crash | Check logs for startup errors |
| Slow cold starts | Large image | Optimize Dockerfile, use smaller base image |
Debugging Failed Deployment
# Check service status
gcloud run services describe noumaris-backend-canada --region northamerica-northeast1
# View recent logs
gcloud run services logs read noumaris-backend-canada --region northamerica-northeast1 --limit 200
# Check revision status
gcloud run revisions describe REVISION_NAME --region northamerica-northeast1CI/CD Pipeline
Cloud Build Triggers
Configured in cloudbuild.yaml:
Trigger: Push to develop branch Steps:
- Build Docker image
- Push to Artifact Registry
- Run migrations (via Cloud SQL Proxy)
- Deploy to Cloud Run Canada
- Deploy to Cloud Run US (parallel)
- Health check validation
- Slack notification (optional)
Substitution variables:
$PROJECT_ID: GCP project$_REGION_CANADA: northamerica-northeast1$_REGION_US: us-central1
Next Steps
- Troubleshooting Guide - Common deployment issues
- Infrastructure Documentation - Detailed infrastructure
- Security Practices - Production security checklist