Skip to content

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:

bash
# Start all services
docker-compose up -d

# Services running:
# - PostgreSQL: localhost:5433
# - Keycloak: localhost:8081

Keycloak Configuration

Configure Keycloak via Terraform:

bash
bash scripts/setup-local-keycloak.sh

This script:

  1. Waits for Keycloak to be ready
  2. Initializes Terraform
  3. Creates realm, roles, clients
  4. Outputs service account credentials

Manual Terraform:

bash
cd terraform/keycloak
terraform init
terraform apply

Database Setup

bash
cd backend

# Run migrations
alembic upgrade head

# Seed with templates and demo data
python seed_db.py
python scripts/seed_features.py

Production 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

  1. Google Cloud Project with billing enabled
  2. APIs enabled: Cloud Run, Cloud SQL, Secret Manager, VPC, Artifact Registry
  3. gcloud CLI installed and authenticated
  4. Terraform installed (v1.0+)
bash
# 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.com

Infrastructure Setup

Terraform manages all infrastructure:

bash
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 apply

Resources 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:

bash
# 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:PROJECT_NUMBER-compute@developer.gserviceaccount.com \
  --role=roles/secretmanager.secretAccessor

Database Migration

  1. Connect to Cloud SQL via Cloud SQL Proxy:
bash
# 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:pass@127.0.0.1:5432/dbname" alembic upgrade head
  1. Or use Cloud Build (automated):
yaml
# cloudbuild.yaml includes migration step
steps:
  - name: 'migrate'
    entrypoint: 'bash'
    args: ['alembic', 'upgrade', 'head']

Deployment Methods

Push to develop branch triggers automatic deployment:

bash
git push origin develop

Cloud Build:

  1. Builds Docker image
  2. Pushes to Artifact Registry
  3. Runs database migrations
  4. Deploys to Cloud Run (Canada + US in parallel)
  5. Health checks validate deployment

cloudbuild.yaml handles this automatically.

Method 2: Manual Deployment

bash
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-unauthenticated

Frontend Deployment

Frontend is deployed to Cloudflare Pages:

  1. Build for production:
bash
cd frontend
npm run build:prod
  1. 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-frontend

Keycloak Deployment

Keycloak runs on Cloud Run (separate service):

bash
# 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 3

After deployment, configure via Terraform:

bash
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:

bash
# 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-northeast1

Database Rollback

Downgrade migrations:

bash
# 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:

bash
# Backup Cloud SQL
gcloud sql backups create --instance INSTANCE_NAME

# Restore from backup
gcloud sql backups restore BACKUP_ID --backup-instance INSTANCE_NAME

Monitoring & Health Checks

Health Check Endpoint

Backend exposes /health:

bash
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:

bash
# 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 50

Monitoring

Cloud Monitoring automatically tracks:

  • Request count
  • Request latency (p50, p95, p99)
  • Error rate
  • Instance count
  • CPU/memory utilization

Set up alerts:

bash
# 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.05

Zero-Downtime Deployment

Cloud Run handles this automatically:

  1. New revision created
  2. Health checks pass
  3. Traffic gradually shifted (0% → 100%)
  4. Old revision scales down

Gradual rollout (optional):

bash
# Split traffic 50/50
gcloud run services update-traffic noumaris-backend-canada \
  --to-revisions=noumaris-backend-canada-00043=50,noumaris-backend-canada-00042=50

Environment Comparison

EnvironmentPurposeBackendDatabaseKeycloak
LocalDevelopmentlocalhost:8000Docker (5433)Docker (8081)
StagingTestingCloud Run (staging)Cloud SQL (staging)Cloud Run (staging)
ProductionLiveCloud 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

  1. Cloud Run: Use scale-to-zero for dev/staging
  2. Cloud SQL: Use smaller instance for non-prod
  3. Backups: Retain only 7 days for dev
  4. Logs: Set 30-day retention policy
bash
# Update log retention
gcloud logging sinks update _Default --log-filter='resource.type="cloud_run_revision"' --retention-days=30

Troubleshooting Deployment

Common Issues

IssueCauseFix
Service won't startMissing secretsVerify Secret Manager permissions
Database connection failsVPC connector issueCheck VPC connector in same region
Health check failsApp not listening on $PORTUse PORT environment variable
502/503 errorsContainer crashCheck logs for startup errors
Slow cold startsLarge imageOptimize Dockerfile, use smaller base image

Debugging Failed Deployment

bash
# 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-northeast1

CI/CD Pipeline

Cloud Build Triggers

Configured in cloudbuild.yaml:

Trigger: Push to develop branch Steps:

  1. Build Docker image
  2. Push to Artifact Registry
  3. Run migrations (via Cloud SQL Proxy)
  4. Deploy to Cloud Run Canada
  5. Deploy to Cloud Run US (parallel)
  6. Health check validation
  7. Slack notification (optional)

Substitution variables:

  • $PROJECT_ID: GCP project
  • $_REGION_CANADA: northamerica-northeast1
  • $_REGION_US: us-central1

Next Steps

Internal documentation for Noumaris platform