Skip to main content
End state: Observal running in your GCP project on Cloud Run, backed by Cloud SQL Postgres, Memorystore Redis, and a GCE instance for ClickHouse — with a Global HTTPS Load Balancer, managed SSL certificate, Secret Manager for credentials, and GCS for backups. For the overall deployment strategy and comparison with AWS, see Production deployment. If you want a simpler single-VM setup, see Single-node deployment.

What gets provisioned

A single terraform apply creates:
  • VPC with a private subnet, Cloud NAT, and a Serverless VPC Access Connector
  • Cloud Run v2 services: api, web, worker with autoscaling (min/max instance counts)
  • Cloud Run v2 job: init (one-shot migrations + seeds)
  • Cloud SQL Postgres (Open-source distribution): optional HA, encrypted, automated backups
  • Memorystore Redis: BASIC or STANDARD_HA tier
  • GCE instance (data host): ClickHouse on a persistent disk, with optional Prometheus and Grafana, accessible via IAP SSH tunnel
  • Global HTTPS Load Balancer with managed SSL certificate (when domain is supplied)
  • Cloud DNS A record pointing to the load balancer
  • Secret Manager: generated DB / ClickHouse / SECRET_KEY passwords, plus connection URLs injected into Cloud Run services
  • GCS backups bucket: versioned, lifecycle to Nearline → delete
  • Artifact Registry: for storing container images (optional)

Prerequisites

Enable required APIs:

Quickstart

Configuration

All inputs live in terraform.tfvars.

Minimal (no custom domain)

The install comes up on the Cloud Run default URL (e.g. https://observal-prod-api-xxxxx-uc.a.run.app).
Terraform provisions a Global HTTPS Load Balancer with a Google-managed SSL certificate and creates the DNS A record.

Sizing

For high-throughput installs, bump data_machine_type to e2-standard-4, db_tier to db-custom-4-16384, and redis_tier to STANDARD_HA.

Operating the install

Shell into the data host

No public SSH. Access is through IAP (Identity-Aware Proxy) — authenticated, audited, no SSH keys to manage.

View logs

Cloud Run logs go to Cloud Logging automatically:
Or use the Cloud Console: Logging → Logs Explorer, filter by resource.type="cloud_run_revision".

Re-run migrations

Read a generated secret

Upgrade to a new release

Cloud Run handles the rolling deploy. Zero downtime.

Resize the data disk

Destroy

Cloud SQL deletion protection is enabled on prod. Disable manually before destroy if you mean it.

Cost estimate (us-central1, on-demand)

Cloud Run scales to zero when idle (if min_instances = 0), which can significantly reduce costs for low-traffic deployments.

Production hardening checklist

  • Set db_ha_enabled = true for Cloud SQL high availability
  • Set redis_tier = "STANDARD_HA" for Redis failover
  • Restrict Cloud Run ingress (use ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER" if behind GCLB)
  • Enable Cloud Armor (WAF) on the Global HTTPS Load Balancer
  • Enable Security Command Center in the project
  • Set up alerting on Cloud SQL CPU, memory, and Cloud Run error rates
  • Configure SSO
  • Test backup and restore end-to-end
  • Set up Terraform remote state in GCS

Troubleshooting

Cloud Run service stuck in Revision is not ready. Check revision logs in Cloud Logging. Common causes: Secret Manager permissions missing, VPC connector not ready, or image pull failure. Init job fails.
Usually: Cloud SQL not yet reachable (transient on first apply), or missing secret access. 502 from the load balancer. The managed SSL certificate takes 10–30 minutes to provision. Check: Network Services → Load Balancing → (your LB) → Backend services → Health. Cloud SQL connection refused from Cloud Run. The VPC Access Connector must be in the same region as Cloud Run. Verify connector_cidr doesn’t overlap with your VPC subnet. For application-level issues, see Troubleshooting.

Next