Deploy on GCP (GKE)
End-to-end walkthrough to deploy Ensemble on Google GKE with Terraform, the Helm chart, GCE ingress, and a Google-managed certificate.
This walkthrough deploys the platform on Google GKE. It assumes you have read Prerequisites and have a PostgreSQL database and a Temporal endpoint ready.
1. Set your environment and authenticate
gcloud auth login
export PROJECT_ID=your-project
export REGION=us-west1
export CLUSTER_NAME=workflows-prod
export NAMESPACE=workflows
export KSA=workflows-sa
export DOMAIN=workflows.example.com
gcloud config set project $PROJECT_ID2. Create the GKE cluster
Autopilot with Workload Identity (enabled by default on Autopilot):
gcloud container clusters create-auto $CLUSTER_NAME \
--region $REGION --project $PROJECT_ID
gcloud container clusters get-credentials $CLUSTER_NAME --region $REGION --project $PROJECT_ID
kubectl get nodeskubectl and helm act on your kubeconfig's current-context, which persists
across shells. The get-credentials command sets GKE as current-context. If it is
left on an EKS cluster, GCP commands hit the wrong cluster; verify with
kubectl config current-context.
3. Install the External Secrets Operator
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets \
-n external-secrets --create-namespaceThe GCE ingress controller is built into GKE, so no load balancer controller install is needed.
4. Provision supporting resources with Terraform
This creates the buckets, KMS keys, the Workload Identity service account and binding, the Secret Manager secret, Firebase (Identity Platform), and Artifact Registry.
cd infrastructure/terraform-gcp
terraform init # add -backend-config for the GCS state bucket
terraform apply -var-file=environments/prod.tfvars -var="project_id=$PROJECT_ID"
terraform output helm_config_values
terraform output service_account_annotation
terraform output app_secret_id5. Complete the Firebase setup
Terraform configures Identity Platform but two steps are manual:
- Enable Identity Platform in the Google Cloud console (a one-time product toggle).
- Register a Firebase web app to obtain
FIREBASE_API_KEY,FIREBASE_AUTH_DOMAIN,FIREBASE_APP_ID, and the otherFIREBASE_*values.
Add those values to your Helm *.config (they are non-secret client config).
6. Populate the secret
Write the real values into the Secret Manager secret Terraform created (empty placeholders by default). Do this out-of-band, never in Git:
echo -n '{
"PG_BASE_URL": "postgresql://user:pass@your-db-host:5432/workflows?sslmode=require",
"TEMPORAL_API_KEY": "...",
"OPENAI_API_KEY": "...",
"ANTHROPIC_API_KEY": "..."
}' | gcloud secrets versions add workflows-prod-app-secrets --data-file=-7. Reserve a static IP and managed certificate
gcloud compute addresses create workflows-ip --global
gcloud compute addresses describe workflows-ip --global --format="value(address)"Create a ManagedCertificate for your domain:
# managed-cert.yaml
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
name: workflows-cert
namespace: workflows
spec:
domains:
- workflows.example.comkubectl create namespace workflows
kubectl apply -f managed-cert.yaml8. Create the service account and SecretStore, then deploy
On GCP the chart expects the Kubernetes service account to already exist (so the migration hook and External Secrets can use it before the chart's main resources render). Create it with the Workload Identity annotation from Terraform:
kubectl create serviceaccount $KSA -n $NAMESPACE
kubectl annotate serviceaccount $KSA -n $NAMESPACE \
iam.gke.io/gcp-service-account=workflows-prod-workloads@$PROJECT_ID.iam.gserviceaccount.comCreate a SecretStore pointing at Secret Manager (example in the chart's
examples/secret-store-gcp.yaml). A minimal values overlay:
# my-values.yaml
global:
imageRegistry: "us-west1-docker.pkg.dev/your-project/workflows/"
serviceAccount:
create: false # created above with the Workload Identity annotation
name: workflows-sa
externalSecrets:
enabled: true
secretStoreRef: { name: gcp-secret-manager, kind: SecretStore }
remoteRef: workflows-prod-app-secrets
# Attach the synced secret and set non-secret config per component.
# Fill each `config` from the Terraform helm_config_values output, the FIREBASE_*
# values, and the Configuration reference; keys omitted here for brevity.
server:
envFrom:
- secretRef: { name: app-secrets }
config: {} # server env vars + FIREBASE_*
worker:
envFrom:
- secretRef: { name: app-secrets }
config: {} # worker env vars (a subset of the server's)
web:
config: {} # auth + URLs + FIREBASE_*
migrations:
envFrom:
- secretRef: { name: app-secrets }
ingress:
enabled: true
className: gce
annotations:
kubernetes.io/ingress.global-static-ip-name: workflows-ip
networking.gke.io/managed-certificates: workflows-cert
kubernetes.io/ingress.allow-http: "false"
hosts:
- host: workflows.example.com
paths:
- { path: /api, pathType: Prefix, service: server }
- { path: /, pathType: Prefix, service: web }
tls: { enabled: true }Fill *.config from the helm_config_values Terraform output (which sets
CLOUD_PROVIDER=gcp, AUTH_PROVIDER=firebase, project, region, bucket names, and
KMS references) plus the Configuration reference. Then:
helm upgrade --install workflows infrastructure/helm/workflows \
-f infrastructure/helm/workflows/gcp-values.yaml \
-f my-values.yaml \
-n workflows9. Configure DNS
Point your domain at the reserved static IP with an A record. The Google-managed certificate provisions only after DNS resolves to the load balancer, which can take up to an hour.
10. Verify
kubectl get pods -n workflows
kubectl describe managedcertificate workflows-cert -n workflows # Status: Active when ready
kubectl logs -f -l app.kubernetes.io/component=server -n workflows
curl -sSf https://$DOMAIN/api/healthAll pods should be Running, the migration job Completed, and the certificate
Active. If not, see Troubleshooting.