Trovella Wiki

GCP Project Layout

The three-project structure, what resources live in each project, and why IAM isolation matters.

Trovella uses three GCP projects. This is an IAM isolation decision, not a complexity preference. Each project has its own IAM policies, billing budgets, and audit logs.

Project Overview

ProjectIDPurposeBudget
Productiontrovella-prodAll production workloads$50/month
Stagingtrovella-stagingPre-production validation (deferred)$20/month
Sharedtrovella-sharedCI/CD infrastructure, state, images$10/month

All projects use us-central1 as the default region.

trovella-prod

The production project contains all resources that serve live traffic and store user data.

Resources

ResourceTerraform ModuleDetails
Cloud SQL (PostgreSQL 18)modules/cloud-sqldb-g1-small, ENTERPRISE edition, ZONAL, 10GB SSD with autoresize
Compute Engine VMmodules/compute-vme2-custom-2-6144 (2 vCPU, 6GB RAM), Ubuntu 24.04 LTS, 50GB disk
Static External IPmodules/compute-vmReserved IP for the VM, used in Cloud SQL authorized networks
Secret Manager (14 secrets)modules/secret-managerEmpty shells; values managed outside Terraform
Firewall Rulesmodules/compute-vmHTTP/HTTPS (0.0.0.0/0) and IAP SSH (35.235.240.0/20)
Cloud Monitoringmodules/compute-vmCPU, memory, disk alerts + HTTPS uptime check
VM Service Accountmodules/compute-vmtrovella-vm-prod with scoped IAM roles

IAM Roles (VM Service Account)

The VM service account (trovella-vm-prod@trovella-prod.iam.gserviceaccount.com) has these roles:

RolePurpose
roles/secretmanager.secretAccessorRead secrets during deployment
roles/logging.logWriterWrite structured logs to Cloud Logging
roles/monitoring.metricWriterWrite custom metrics
roles/cloudsql.clientConnect via Cloud SQL Auth Proxy
roles/artifactregistry.reader (on trovella-shared)Pull Docker images from Artifact Registry

Secrets

All 14 secrets follow the naming pattern trovella-{name}:

trovella-anthropic-api-key
trovella-google-ai-api-key
trovella-database-url
trovella-better-auth-secret
trovella-better-auth-url
trovella-google-oauth-client-id
trovella-google-oauth-client-secret
trovella-resend-api-key
trovella-upstash-redis-url
trovella-upstash-redis-token
trovella-sentry-dsn
trovella-inngest-event-key
trovella-inngest-signing-key
trovella-typesense-api-key

See Secret Provisioning for how these are created and synced.

trovella-staging

The staging project has the GCP project created and secrets provisioned, but no compute resources deployed. This was an explicit deferral: "don't create infrastructure that has nothing to deploy to."

Resources (Currently Deployed)

ResourceDetails
Secret Manager (14 secrets)Same set as production, via modules/secret-manager

Resources (Deferred to ~Month 2)

When staging is activated, it will get:

  • Compute Engine VM (same module as prod, possibly same VM with a separate compose stack)
  • Cloud SQL instance (separate from prod)
  • Monitoring alerts

The staging Terraform config (infra/environments/staging/main.tf) has comments marking where the compute module will be added.

trovella-shared

The shared project exists to isolate CI/CD infrastructure from production resources. If these resources lived in trovella-prod, a compromised CI pipeline could modify its own IAM permissions in the production project.

Resources

ResourceTerraform ModuleDetails
Workload Identity Federation Poolmodules/wifgithub-actions pool with OIDC provider
WIF Service Accountmodules/wifgithub-actions-ci with cross-project IAM grants
Artifact RegistryDirect resourcetrovella repository, Docker format, 10-image cleanup policy
Terraform State BucketManual (not in TF)gs://trovella-terraform-state with object versioning

Artifact Registry Cleanup

The Artifact Registry repository has a cleanup policy that retains only the 10 most recent images per tag. This prevents unbounded storage growth from continuous deployments.

cleanup_policies {
  id     = "keep-recent"
  action = "KEEP"
  most_recent_versions {
    keep_count = 10
  }
}

Cross-Project IAM Grants

The WIF service account in trovella-shared has cross-project permissions. See Workload Identity Federation for the full grant list.

Why Not Two Projects?

The shared project could theoretically be merged with staging. The separation exists because:

  1. Terraform state isolation -- if state were in a per-environment project, anyone with environment access could corrupt state for all environments
  2. CI service account isolation -- the GitHub Actions service account cannot modify its own permissions because it lives in a different project than the resources it deploys to
  3. Billing clarity -- shared infrastructure costs (Artifact Registry, state storage) are tracked separately from environment-specific compute costs

On this page