Environment Reference

Environment Reference #

Every project ID, cluster name, context, hostname, and endpoint.

Migration status (2026-08-11). Staging runs fully on GCP. Production runs on AWS ECS + Aurora. The production GCP stack is up and serves a view-only canary at app-gcp.funnelstory.ai; its Cloud SQL instance is still a demoted replica of Aurora. Every production GCP row below is real but not yet customer-facing.

Who can do what #

Access is tiered on purpose. Database credentials and secret payloads stay with the GCP admins. Everyone else gets read-only.

Tier Who Can Cannot
GCP admin Vivek, Preetam everything, including DB logins and secret values
Developer (read-only) everyone else, on request read pods, pod logs, ConfigMaps, Cloud SQL and Secret Manager metadata, Grafana read secret values, log in to a database, exec into a pod, port-forward, restart a workload

The developer tier is these four roles. Ask in #back-end to be added:

roles/viewer                  # project-wide read
roles/gkehub.gatewayReader    # read-only kubectl through Connect Gateway
roles/gkehub.viewer           # list memberships, run get-credentials
roles/container.viewer        # in-cluster RBAC read

roles/viewer deliberately excludes secretmanager.versions.access, cloudsql.instances.login, container.secrets.*, container.pods.exec, and container.pods.portForward. Secret values stay unreadable, and the database stays unusable without a role, while ordinary debugging still works.

Developers also hold roles/aiplatform.user for Vertex AI, and roles/browser for org and folder traversal.

Granted on both projects as of 2026-08-11: Pulkit, Mamta, Nitin, Avadhut. Members are Terraform-owned in gcp/envs/<env>/terraform.tfvars under developer_members. Add someone by extending that list and applying, so the next plan keeps the binding instead of proposing to destroy it.

Two rules follow from this and are worth stating plainly:

  • Logs → Grafana. Backend app logs live in the non-default backend-app-logs bucket, and reading a custom bucket view needs roles/logging.viewAccessor on top of roles/viewer. Grafana holds that grant through its own service account, so it needs no extra IAM for you.
  • Deploys → an ops repo Helm PR. Nobody deploys from a laptop. Config and image changes go through k8s/deploy/<env>/values.yaml and the deploy workflow — see update env vars.

Projects and regions #

Staging Production
GCP project ID funnelstory-infra-staging funnelstory-infra-production
Project number see command below 905900603049
Region us-west1 us-west1
Zone (bastion) us-west1-a us-west1-a
gcloud projects describe funnelstory-infra-staging --format='value(projectNumber)'

GKE #

Staging Production
Cluster funnelstory-staging funnelstory-production
Mode Autopilot, private control plane Autopilot, private control plane
Release channel REGULAR STABLE
Fleet membership funnelstory-staging funnelstory-production
kubectl context connectgateway_funnelstory-infra-staging_global_funnelstory-staging connectgateway_funnelstory-infra-production_global_funnelstory-production
App namespace fs-apps fs-apps
Grafana namespace observability observability
App hostname app.staging.funnelstory.ai app-gcp.funnelstory.ai (view-only canary)

The control plane has no public endpoint. All kubectl access goes through GKE Connect Gateway — see gcloud setup.

Cloud SQL (PostgreSQL 15) #

Endpoints are listed so you can read dashboards and Terraform. Connecting needs a database role, which is admin-only — see psql to Cloud SQL.

Staging Production
App instance funnelstory-staging funnelstory-production
Writer private IP 172.21.32.7 172.20.32.5
Read replica instance funnelstory-staging-replica not created yet
Read replica private IP 172.21.32.13
Database funnelstory funnelstory
Edition / tier Enterprise, db-custom-2-8192 Enterprise Plus, db-c4a-highmem-4
Automated backups off off — re-enable after promote
PITR off off — re-enable after promote
Micro instance funnelstory-micro-staging (172.21.32.9, db sample_db) funnelstory-micro-production
Grafana metadata instance grafana-staging (db grafana) grafana-production (db grafana)

The production read replica is created at cutover. A DMS-managed destination cannot have replicas until it is promoted.

Private IPs can change. Re-check before you trust one:

gcloud sql instances describe funnelstory-staging \
  --project=funnelstory-infra-staging --format='value(ipAddresses[0].ipAddress)'

Network ranges #

Every range is private. WARP routes all of them, so with WARP connected you can reach a Cloud SQL instance or a pod IP straight from your laptop. Network reach is not the access control — credentials and IAM are.

Range Staging Production
Primary subnet (nodes, internal LBs) 172.21.0.0/20 172.20.0.0/20
Cloud SQL private services access 172.21.32.0/20 172.20.32.0/20
GKE private control plane endpoint 172.21.48.0/28 172.20.48.0/28
GKE pods 10.8.0.0/16 10.16.0.0/16
GKE Services 10.9.0.0/20 10.17.0.0/20

VPC names are funnelstory-staging and funnelstory-production. The CIDRs avoid AWS production (172.30.0.0/16) and AWS staging (172.31.0.0/16), because an HA VPN joins them during the migration.

Ten routes are published, five per environment. A route reaches your laptop once its CIDR is in the WARP split-tunnel include list, which lives on the account-wide device profile (Zero Trust → Settings → WARP Client → Device settings → Split Tunnels) rather than in Terraform. Check yours with warp-cli settings, and check the connection with warp-cli status. A missing entry is the usual cause of a hang on a private IP.

Staging Production
Grafana grafana-staging.funnelstory.io grafana-production.funnelstory.io
PgHero gone — the Aurora cluster it read was deleted 2026-07-21 pghero-primary-production.funnelstory.io (AWS Aurora)

Both Grafana instances sit behind Cloudflare Access, which is the only gate — no GCP IAM needed. See Grafana and PgHero.

Terraform #

Staging Production
Directory gcp/envs/staging gcp/envs/production
State bucket funnelstory-tf-state-staging funnelstory-tf-state-production
State prefix envs/staging envs/production

See Terraform.

Other resources #

Staging Production
App GCS bucket fsio-staging-funnelstory fsio-production-funnelstory
Tunnel keys bucket fs-tunnel-keys-staging fs-tunnel-keys-production
Cloudflare tunnel name Staging GCP Production GCP
Cloudflare account one account for both — see cloudflare/envs/*/terraform.tfvars same account

Container images live in the staging project for both environments:

us-west1-docker.pkg.dev/funnelstory-infra-staging/apps/{back-end,front-end,highcharts-export}

Production GKE pulls from the staging Artifact Registry. Zero-copy promotion means production runs the same bytes staging validated. The production node service account holds a repository-scoped reader grant.

Deploys #

Staging Production
Trigger automatic — push to main, or repository_dispatch from the app repos manual workflow_dispatch only
Workflow .github/workflows/helm-deploy.yml .github/workflows/helm-deploy-production.yml
Approval gate none production GitHub Environment reviewers

All task guides #

Read-only, any developer:

Admin only: