Skip to content

GCP Provider

The GCP provider is the first and currently only cloud provider for Astromesh Orbit. It maps your orbit.yaml configuration to Google Cloud managed services using Terraform.

Every field in orbit.yaml maps to one or more GCP resources:

orbit.yaml fieldGCP ResourceTerraform Template
spec.compute.runtimeCloud Run v2 Servicecloud_run.tf.j2
spec.databaseCloud SQL for PostgreSQLcloud_sql.tf.j2
spec.cacheMemorystore for Redismemorystore.tf.j2
spec.secretsSecret Managersecrets.tf.j2
(automatic)Serverless VPC Connectornetworking.tf.j2
(automatic)Service Account + IAMiam.tf.j2
(automatic)GCS Bucket (Terraform state)backend.tf.j2
spec.storage.rag_documentsGCS Bucket (RAG docs)storage.tf.j2
spec.storage.artifact_registryArtifact Registryartifact_registry.tf.j2
spec.observability.dashboardCloud Monitoring dashboardmonitoring.tf.j2
spec.observability.tracingOTel Collector sidecarcloud_run.tf.j2

Orbit provisions several resources automatically that are not user-configured. These are required for the stack to function securely:

Cloud Run services need a Serverless VPC Access connector to communicate with Cloud SQL and Memorystore over private networking. Orbit creates one VPC connector for the runtime Cloud Run service.

A dedicated service account astromesh-orbit@{'{project}'} is created with the minimum required IAM roles:

RolePurpose
roles/cloudsql.clientConnect to Cloud SQL via Auth Proxy
roles/redis.editorRead/write to Memorystore
roles/secretmanager.secretAccessorRead secrets at runtime
roles/run.invokerAllow services to invoke each other
roles/cloudtrace.agent, roles/monitoring.metricWriterExport spans and metrics (only with spec.observability.tracing.enabled)

The runtime Cloud Run service runs as this dedicated service account.

The Cloud SQL instance has no public IP: it lives on the project’s default network through private services access. Cloud Run reaches it through a Cloud SQL volume mounted at /cloudsql (the built-in Auth Proxy socket), so no IP allowlist or certificates are involved.

The database user is astromesh, with a random 24-character password generated by Terraform. The connection string, password included, is set as the runtime’s ASTROMESH_DATABASE_URL.

Cloud SQL with a private IP needs a peering between the default network and Google’s service network. When GOOGLE_APPLICATION_CREDENTIALS points at a service-account key, plan and apply create it: an internal range google-managed-services (/20) and the peering. With gcloud credentials Orbit skips this step, so create it once yourself:

Terminal window
gcloud compute addresses create google-managed-services --global \
--purpose=VPC_PEERING --prefix-length=20 --network=default --project my-project-123
gcloud services vpc-peerings connect --service=servicenetworking.googleapis.com \
--ranges=google-managed-services --network=default --project my-project-123

Terraform state is stored in a GCS bucket named {'{project}'}-astromesh-orbit-state in the same region as the deployment. The bucket is created before Terraform initializes, with versioning enabled for state recovery.

Two additional resources are provisioned by default via spec.storage (see Configuration):

  • GCS bucket (storage.tf.j2) — stages RAG source documents. The runtime receives its name as ASTROMESH_RAG_BUCKET. Disable with spec.storage.rag_documents.enabled: false.
  • Artifact Registry (artifact_registry.tf.j2) — a Docker repository (<metadata.name>-images by default) for custom Astromesh container images. Disable with spec.storage.artifact_registry.enabled: false.

Orbit does not provision a separate vector database for RAG. Cloud SQL for PostgreSQL supports the pgvector extension natively, so the existing Cloud SQL instance doubles as the RAG vector store — point a *.rag.yaml pipeline at it with vector_store.backend: pgvector.

Cloud Run already reports request metrics to Cloud Monitoring and ships stdout/stderr to Cloud Logging at no extra cost — Orbit provisions nothing to get those. A Cloud Monitoring dashboard (monitoring.tf.j2) with four charts for astromesh-runtime — request count, p95 request latency, instance count, and CPU and memory utilization — is provisioned on by default; disable it with spec.observability.dashboard: false. orbit logs reads the Cloud Logging entries directly.

Cloud Trace is opt-in. Setting spec.observability.tracing.enabled: true adds an OpenTelemetry Collector sidecar to the runtime Cloud Run service and sets ASTROMESH_OTLP_ENABLED=1 on the runtime container, so the astromesh runtime exports OTLP spans to the sidecar, which forwards them to Cloud Trace under the deployment’s service account.

See Configuration for the full spec.observability schema.

The following APIs must be enabled in your GCP project. orbit plan and orbit apply check them and fail with the gcloud services enable command for each missing one; Orbit does not enable them for you.

APIServiceEnable Command
run.googleapis.comCloud Rungcloud services enable run.googleapis.com
sqladmin.googleapis.comCloud SQL Admingcloud services enable sqladmin.googleapis.com
redis.googleapis.comMemorystore for Redisgcloud services enable redis.googleapis.com
secretmanager.googleapis.comSecret Managergcloud services enable secretmanager.googleapis.com
vpcaccess.googleapis.comServerless VPC Accessgcloud services enable vpcaccess.googleapis.com
storage.googleapis.comCloud Storagegcloud services enable storage.googleapis.com
artifactregistry.googleapis.comArtifact Registrygcloud services enable artifactregistry.googleapis.com
monitoring.googleapis.comCloud Monitoringgcloud services enable monitoring.googleapis.com
cloudtrace.googleapis.comCloud Tracegcloud services enable cloudtrace.googleapis.com
logging.googleapis.comCloud Logginggcloud services enable logging.googleapis.com

Enable all at once:

Terminal window
gcloud services enable \
run.googleapis.com \
sqladmin.googleapis.com \
redis.googleapis.com \
secretmanager.googleapis.com \
vpcaccess.googleapis.com \
storage.googleapis.com \
artifactregistry.googleapis.com \
monitoring.googleapis.com \
cloudtrace.googleapis.com \
logging.googleapis.com \
--project my-project-123

The user running orbit apply needs one of:

  • roles/owner — Full access (simplest for getting started)
  • roles/editor + roles/iam.serviceAccountAdmin — Enough to create all resources and manage the service account

For the state bucket, the user also needs storage.buckets.create on the project. If the bucket already exists (e.g., from a previous deployment), this permission is not required.

Running orbit plan triggers the GCP provider’s validate() method, which checks:

  1. Authentication — gcloud is authenticated, or GOOGLE_APPLICATION_CREDENTIALS points to a service-account key file
  2. Project — The GCP project exists and the credentials can read it
  3. APIs — All 10 required APIs are enabled

IAM permissions and quotas are not checked up front; Terraform reports them if they fall short. If validation fails, Orbit prints each failed check with its remediation and exits with code 1:

Validating... FAILED
OK Authenticated as you@example.com
OK Project my-project-123 found
FAIL sqladmin.googleapis.com not enabled
-> gcloud services enable sqladmin.googleapis.com --project=my-project-123

After a successful orbit apply, Orbit writes every Terraform output to .orbit/orbit.env, upper-cased:

RUNTIME_URL=https://{runtime_cloud_run_url}
DB_CONNECTION_NAME={project}:{region}:{name}-db
REDIS_HOST={memorystore_ip}
RAG_BUCKET={project}-{name}-rag-docs
ARTIFACT_REGISTRY_REPO={region}-docker.pkg.dev/{project}/{name}-images

The file is for you (scripts, local development against the deployed stack); the runtime container gets its own configuration from the Cloud Run template, not from this file.

The Cloud Run service runs spec.images.runtime on port 8000, as the astromesh-orbit service account, with these environment variables:

VariableValueWhen
ASTROMESH_DATABASE_URLpostgresql+asyncpg://astromesh:<password>@/astromesh?host=/cloudsql/<connection>Always
ASTROMESH_REDIS_URLredis://<memorystore host>:<port>Always
ASTROMESH_RAG_BUCKETThe RAG documents bucketstorage.rag_documents.enabled
ASTROMESH_OTLP_ENABLED=1, OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317Points the runtime at the sidecarobservability.tracing.enabled
Everything in spec.envAs writtenAlways

With tracing on, a second container, otel-collector (1 vCPU, 512 Mi), receives OTLP on 4317 and exports traces to Cloud Trace; the runtime container starts after it.

  • Secrets in plain configuration. The database password travels inside ASTROMESH_DATABASE_URL, and spec.env values are plain environment variables. Anyone who can read the Cloud Run service’s configuration can read them.
  • Secret Manager entries are not wired. <name>-jwt-secret and <name>-fernet-key are created but not passed to the container.
  • deletion_protection = false on Cloud SQL, so orbit destroy deletes the database. Its automated backups go with it.

Choose a region close to your users. The init wizard offers eight (us-central1, us-east1, us-west1, europe-west1, europe-west4, asia-east1, asia-southeast1, southamerica-east1); orbit.yaml accepts any region where all the services exist.

RegionLocationNotes
us-central1Iowa, USALowest cost, most services available
us-east1South Carolina, USAGood for East Coast US
europe-west1BelgiumGDPR-friendly, European users
europe-west4NetherlandsAlternative European region
asia-east1TaiwanAsia-Pacific users
southamerica-east1São Paulo, BrazilSouth American users

All resources (Cloud Run, Cloud SQL, Memorystore, VPC Connector) are deployed in the same region to minimize latency and avoid cross-region networking costs.

Terraform state is stored remotely in GCS. Key details:

  • No locking — Orbit runs every OpenTofu/Terraform command with -lock=false, so two apply runs against the same deployment at once are not serialized. Run one at a time.
  • Prefix — State lives under metadata.name inside the bucket.
  • Versioning — The bucket has versioning enabled, allowing recovery from accidental state corruption.
  • Cleanup — orbit destroy does not delete the state bucket. It contains the record of what was destroyed. Delete it manually after confirming everything is torn down: gsutil rm -r gs://{'{project}'}-astromesh-orbit-state.