Overview
This guide walks through a complete production deployment of CrewAI Platform on GKE using:
- Cloud SQL for PostgreSQL (four databases including Wharf) via Cloud SQL Auth Proxy with IAM authentication
- Google Cloud Storage for object storage via Workload Identity
- Artifact Registry for crew container images
- Microsoft Entra ID for SSO authentication
- Wharf for OTLP trace collection (enabled by default)
- Studio V2 configured post-install
This is a self-contained guide. All required Helm values are included — no cross-references to other guides are needed for a complete deployment.
This guide assumes:
- A GKE cluster running Kubernetes 1.28+ with Workload Identity enabled
- Gateway API enabled on the cluster
gcloud CLI, kubectl, and Helm 3.10+ installed
- An active Microsoft Entra ID (Azure AD) tenant
- Basic familiarity with GCP services (Cloud SQL, GCS, Artifact Registry)
Prerequisites Checklist
Complete each item before running helm install:
The Entra ID redirect URI must be registered in Azure before running helm install. Authentication will fail at login if it is missing. The redirect URI format is exactly: https://<YOUR_DOMAIN>/auth/entra_id/callback
Part 1: GCP Infrastructure Setup
Enable Required APIs
gcloud services enable \
container.googleapis.com \
sqladmin.googleapis.com \
storage-api.googleapis.com \
artifactregistry.googleapis.com \
iam.googleapis.com \
certificatemanager.googleapis.com
Set Shell Variables
export GCP_PROJECT_ID="<YOUR_GCP_PROJECT_ID>"
export GCP_REGION="<YOUR_REGION>" # e.g. us-central1
export GSA_NAME="crewai-platform" # GCP Service Account name
export GKE_NAMESPACE="crewai" # Kubernetes namespace for the Helm release
export KSA_NAME="crewai-sa" # Chart default SA name when release name is 'crewai'
export GKE_CLUSTER="<YOUR_CLUSTER_NAME>"
export SQL_INSTANCE="crewai-db"
export AR_REPO="crewai"
export GCS_BUCKET="crewai-prod-storage"
KSA_NAME must match the ServiceAccount the chart creates. The chart default is {release-name}-sa. This guide uses release name crewai, producing crewai-sa. If your release name differs, either update KSA_NAME to match, or pin serviceAccount.name: crewai-sa in your Helm values.
Create GCP Service Account
gcloud iam service-accounts create $GSA_NAME \
--display-name="CrewAI Platform" \
--project=$GCP_PROJECT_ID
Grant IAM Roles
# GCS — object storage read/write
gcloud projects add-iam-policy-binding $GCP_PROJECT_ID \
--member="serviceAccount:${GSA_NAME}@${GCP_PROJECT_ID}.iam.gserviceaccount.com" \
--role="roles/storage.objectAdmin"
# GCS — signed URL generation via IAM signBlob (required for Workload Identity)
gcloud projects add-iam-policy-binding $GCP_PROJECT_ID \
--member="serviceAccount:${GSA_NAME}@${GCP_PROJECT_ID}.iam.gserviceaccount.com" \
--role="roles/iam.serviceAccountTokenCreator"
# Artifact Registry — push/pull crew container images
gcloud projects add-iam-policy-binding $GCP_PROJECT_ID \
--member="serviceAccount:${GSA_NAME}@${GCP_PROJECT_ID}.iam.gserviceaccount.com" \
--role="roles/artifactregistry.writer"
# Cloud SQL — connect via Auth Proxy
gcloud projects add-iam-policy-binding $GCP_PROJECT_ID \
--member="serviceAccount:${GSA_NAME}@${GCP_PROJECT_ID}.iam.gserviceaccount.com" \
--role="roles/cloudsql.client"
# Cloud SQL — IAM database authentication
gcloud projects add-iam-policy-binding $GCP_PROJECT_ID \
--member="serviceAccount:${GSA_NAME}@${GCP_PROJECT_ID}.iam.gserviceaccount.com" \
--role="roles/cloudsql.instanceUser"
# Node pool GCE SA — pull crew images from Artifact Registry
GCE_SA="$(gcloud projects describe $GCP_PROJECT_ID --format='value(projectNumber)')-compute@developer.gserviceaccount.com"
gcloud projects add-iam-policy-binding $GCP_PROJECT_ID \
--member="serviceAccount:${GCE_SA}" \
--role="roles/artifactregistry.reader"
Bind Workload Identity
# Platform namespace — web, worker, buildkit daemon pods
gcloud iam service-accounts add-iam-policy-binding \
${GSA_NAME}@${GCP_PROJECT_ID}.iam.gserviceaccount.com \
--role="roles/iam.workloadIdentityUser" \
--member="serviceAccount:${GCP_PROJECT_ID}.svc.id.goog[${GKE_NAMESPACE}/${KSA_NAME}]"
# Crews namespace — build pods that push images to Artifact Registry
gcloud iam service-accounts add-iam-policy-binding \
${GSA_NAME}@${GCP_PROJECT_ID}.iam.gserviceaccount.com \
--role="roles/iam.workloadIdentityUser" \
--member="serviceAccount:${GCP_PROJECT_ID}.svc.id.goog[crewai-crews/default]"
Part 2: Cloud SQL Setup
Create the Cloud SQL Instance
gcloud sql instances create $SQL_INSTANCE \
--database-version=POSTGRES_16 \
--tier=db-custom-2-16384 \
--region=$GCP_REGION \
--storage-size=100GB \
--storage-type=SSD \
--storage-auto-increase \
--no-assign-ip
Enable IAM Authentication on the Instance
gcloud sql instances patch $SQL_INSTANCE \
--database-flags=cloudsql.iam_authentication=on
cloudsql.iam_authentication is off by default. The Cloud SQL Auth Proxy cannot authenticate via IAM tokens until this flag is enabled — pods will fail with authentication errors.
Create the Four Required Databases
gcloud sql databases create crewai_plus_production --instance=$SQL_INSTANCE
gcloud sql databases create crewai_plus_cable_production --instance=$SQL_INSTANCE
gcloud sql databases create crewai_plus_oauth_production --instance=$SQL_INSTANCE
gcloud sql databases create wharf --instance=$SQL_INSTANCE
The database names above must be used exactly as shown. In particular, crewai_plus_oauth_production overrides the chart default of oauth_db. You must set POSTGRES_OAUTH_DB: "crewai_plus_oauth_production" in envVars: — if omitted, the OAuth service attempts to connect to a database that does not exist and fails silently.
Create the IAM Database User
gcloud sql users create ${GSA_NAME}@${GCP_PROJECT_ID}.iam \
--instance=$SQL_INSTANCE \
--type=CLOUD_IAM_SERVICE_ACCOUNT
Grant SQL Privileges
Connect to the instance as the postgres superuser:
gcloud sql connect $SQL_INSTANCE --user=postgres --database=postgres
Then run the following SQL (replace GSA_NAME@GCP_PROJECT_ID.iam with your actual IAM user, e.g., crewai-platform@my-project.iam):
-- Grant connect privileges
GRANT ALL PRIVILEGES ON DATABASE crewai_plus_production TO "GSA_NAME@GCP_PROJECT_ID.iam";
GRANT ALL PRIVILEGES ON DATABASE crewai_plus_cable_production TO "GSA_NAME@GCP_PROJECT_ID.iam";
GRANT ALL PRIVILEGES ON DATABASE crewai_plus_oauth_production TO "GSA_NAME@GCP_PROJECT_ID.iam";
GRANT ALL PRIVILEGES ON DATABASE wharf TO "GSA_NAME@GCP_PROJECT_ID.iam";
-- Schema and table privileges for each database
\c crewai_plus_production
GRANT ALL ON SCHEMA public TO "GSA_NAME@GCP_PROJECT_ID.iam";
GRANT ALL ON ALL TABLES IN SCHEMA public TO "GSA_NAME@GCP_PROJECT_ID.iam";
GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO "GSA_NAME@GCP_PROJECT_ID.iam";
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "GSA_NAME@GCP_PROJECT_ID.iam";
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO "GSA_NAME@GCP_PROJECT_ID.iam";
\c crewai_plus_cable_production
GRANT ALL ON SCHEMA public TO "GSA_NAME@GCP_PROJECT_ID.iam";
GRANT ALL ON ALL TABLES IN SCHEMA public TO "GSA_NAME@GCP_PROJECT_ID.iam";
GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO "GSA_NAME@GCP_PROJECT_ID.iam";
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "GSA_NAME@GCP_PROJECT_ID.iam";
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO "GSA_NAME@GCP_PROJECT_ID.iam";
\c crewai_plus_oauth_production
GRANT ALL ON SCHEMA public TO "GSA_NAME@GCP_PROJECT_ID.iam";
GRANT ALL ON ALL TABLES IN SCHEMA public TO "GSA_NAME@GCP_PROJECT_ID.iam";
GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO "GSA_NAME@GCP_PROJECT_ID.iam";
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "GSA_NAME@GCP_PROJECT_ID.iam";
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO "GSA_NAME@GCP_PROJECT_ID.iam";
\c wharf
GRANT ALL ON SCHEMA public TO "GSA_NAME@GCP_PROJECT_ID.iam";
GRANT ALL ON ALL TABLES IN SCHEMA public TO "GSA_NAME@GCP_PROJECT_ID.iam";
GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO "GSA_NAME@GCP_PROJECT_ID.iam";
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "GSA_NAME@GCP_PROJECT_ID.iam";
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO "GSA_NAME@GCP_PROJECT_ID.iam";
Part 3: GCS and Artifact Registry
Create GCS Bucket
gsutil mb -p $GCP_PROJECT_ID -l $GCP_REGION -b on gs://$GCS_BUCKET
gsutil versioning set on gs://$GCS_BUCKET
Create Artifact Registry Repository
gcloud artifacts repositories create $AR_REPO \
--repository-format=docker \
--location=$GCP_REGION \
--description="CrewAI Platform crew images"
The CREW_IMAGE_REGISTRY_OVERRIDE value for this repository is:
${GCP_REGION}-docker.pkg.dev/${GCP_PROJECT_ID}/${AR_REPO}
For example: us-central1-docker.pkg.dev/my-project/crewai
Do NOT include /crewai-enterprise in CREW_IMAGE_REGISTRY_OVERRIDE. The platform appends /crewai-enterprise automatically. Including the suffix causes image push failures to a double-suffixed path.Also ensure the Artifact Registry repository has mutable tags. CrewAI overwrites image tags for crew versions — immutable tags will cause build failures.
Enable Gateway API on the Cluster
gcloud container clusters update $GKE_CLUSTER \
--gateway-api=standard \
--region=$GCP_REGION \
--project=$GCP_PROJECT_ID
Part 4: Microsoft Entra ID App Registration
Create the App Registration
- Go to portal.azure.com and navigate to Microsoft Entra ID → App registrations → New registration
- Set the application name (suggested:
CrewAI)
- Under Supported account types, select Accounts in this organizational directory only
- Under Redirect URI, select platform Web and enter:
https://<YOUR_DOMAIN>/auth/entra_id/callback
- Click Register
The redirect URI must be registered in Azure before running helm install. The exact format required is https://<YOUR_DOMAIN>/auth/entra_id/callback. Authentication will fail silently at login if this is missing or incorrect.
Collect Credentials
From the app’s Overview page, copy:
- Application (client) ID → this is
ENTRA_ID_CLIENT_ID
- Directory (tenant) ID → this is
ENTRA_ID_TENANT_ID
Create a Client Secret
- In the left sidebar under Manage, click Certificates & secrets
- Click New client secret, set a description and expiration, then click Add
- Copy the secret Value immediately — you cannot view it again after leaving the page
- This is
ENTRA_ID_CLIENT_SECRET
Grant Admin Consent
- Navigate to Enterprise applications → All applications and select your app
- In the left sidebar under Security, click Permissions
- Click Grant admin consent for <your organization>
- Confirm that Microsoft Graph User.Read is granted
In the left sidebar under Manage, click App roles, then Create app role. Create both of the following roles:
| Display name | Value | Allowed member types |
|---|
| Member | member | Users/Groups |
| Factory Admin | factory-admin | Users/Groups |
Ensure “Do you want to enable this app role?” is checked for each role before saving.
Assign Users
- Go to Enterprise applications → <your app> → Users and groups
- Click Add user/group
- Assign regular users the Member role
- Assign admin users the Factory Admin role
With Entra ID, admin access is granted via the factory-admin App Role in Azure. Do not run the factory:grant_admin Rails task — that command is for WorkOS/Okta/local auth only. For Entra ID, assign the factory-admin App Role to the user in the Azure portal instead.
Part 5: Complete values.yaml
The following is a complete, production-ready values.yaml. Fill in all <PLACEHOLDER> values before deploying.
Generate your SECRET_KEY_BASE:
openssl rand -base64 64 | tr -d '\n'
# ============================================================
# CrewAI Platform — GCP GKE + Entra ID + Wharf + Studio V2
# ============================================================
# ServiceAccount annotated for Workload Identity
serviceAccount:
annotations:
iam.gke.io/gcp-service-account: crewai-platform@<YOUR_GCP_PROJECT_ID>.iam.gserviceaccount.com
# Namespace for crew workloads
crewNamespace: "crewai-crews"
# Image pull credentials for images.crewai.com (Replicated proxy)
# Required for direct Helm installs — use your license email and token
image:
registries:
- host: "images.crewai.com"
username: "<YOUR_LICENSE_EMAIL>"
password: "<YOUR_REPLICATED_LICENSE_TOKEN>"
# Disable built-in PostgreSQL — using Cloud SQL
postgres:
enabled: false
# Disable built-in MinIO — using GCS
minio:
enabled: false
# -------------------------------------------------------
# Cloud SQL Auth Proxy (sidecar on web, worker, job pods)
# -------------------------------------------------------
cloudSqlProxy:
enabled: true
instanceConnectionName: "<YOUR_GCP_PROJECT_ID>:<YOUR_REGION>:<YOUR_SQL_INSTANCE>" # e.g. my-project:us-central1:crewai-db
port: 5432
privateIp: true # Use private IP for VPC-peered instances (recommended)
autoIamAuthn: true # IAM-based auth — no DB_PASSWORD needed
# -------------------------------------------------------
# Environment Variables (non-sensitive configuration)
# -------------------------------------------------------
envVars:
# --- Database (Cloud SQL Auth Proxy) ---
DB_HOST: "127.0.0.1" # Proxy runs as localhost sidecar
DB_PORT: "5432"
DB_USER: "crewai-platform@<YOUR_GCP_PROJECT_ID>.iam" # GSA email WITHOUT .gserviceaccount.com suffix
POSTGRES_DB: "crewai_plus_production"
POSTGRES_CABLE_DB: "crewai_plus_cable_production"
POSTGRES_OAUTH_DB: "crewai_plus_oauth_production" # Must override — chart default is "oauth_db"
# --- GCS Object Storage ---
STORAGE_SERVICE: "google"
GCS_PROJECT_ID: "<YOUR_GCP_PROJECT_ID>"
GCS_BUCKET: "<YOUR_GCS_BUCKET>" # e.g. crewai-prod-storage
GCS_IAM_SIGNING: "true" # REQUIRED with Workload Identity — omitting causes SignedUrlUnavailable errors
# --- Artifact Registry (crew images) ---
CREW_IMAGE_REGISTRY_OVERRIDE: "<YOUR_REGION>-docker.pkg.dev/<YOUR_GCP_PROJECT_ID>/<YOUR_AR_REPO>"
# Example: us-central1-docker.pkg.dev/my-project/crewai
# Do NOT include /crewai-enterprise — the platform appends it automatically
# --- Entra ID SSO ---
AUTH_PROVIDER: "entra_id" # Exact value required — lowercase, underscore
ENTRA_ID_CLIENT_ID: "<APPLICATION_CLIENT_ID>" # From Azure App Registration Overview
ENTRA_ID_TENANT_ID: "<DIRECTORY_TENANT_ID>" # From Azure App Registration Overview
# Note: CLIENT_ID and TENANT_ID are non-sensitive identifiers — they belong here in envVars, NOT in secrets
# --- Application ---
APPLICATION_HOST: "<YOUR_DOMAIN>" # e.g. crewai.your-company.com (no https://)
RAILS_LOG_LEVEL: "info"
# -------------------------------------------------------
# Secrets (sensitive credentials)
# -------------------------------------------------------
secrets:
SECRET_KEY_BASE: "<OUTPUT_OF: openssl rand -base64 64 | tr -d '\n'>"
# Entra ID client secret — belongs here in secrets (not envVars)
ENTRA_ID_CLIENT_SECRET: "<CLIENT_SECRET_VALUE>" # From Azure Certificates & Secrets
# DB_PASSWORD is NOT needed when autoIamAuthn: true — the proxy handles auth via Workload Identity
# -------------------------------------------------------
# Wharf (OTLP trace collection) — enabled by default
# -------------------------------------------------------
wharf:
enabled: true # Explicit for clarity — this is the chart default
# -------------------------------------------------------
# Gateway API
# -------------------------------------------------------
gateway:
enabled: true
create: true
gatewayClassName: gke-l7-regional-external-managed # Use gke-l7-global-external-managed for multi-region or CDN
annotations:
networking.gke.io/certmap: "<YOUR_CERT_MAP_NAME>" # GCP-managed certificate map
listeners:
- name: https
protocol: HTTPS
port: 443
- name: http
protocol: HTTP
port: 80
# -------------------------------------------------------
# Web
# Note: Merge all web: settings under this single top-level web: key.
# Helm silently drops all but the last occurrence of a duplicate top-level key.
# -------------------------------------------------------
web:
replicaCount: 3
resources:
requests:
cpu: "1000m"
memory: "6Gi"
limits:
cpu: "6"
memory: "12Gi"
gateway:
enabled: true
hostnames:
- "<YOUR_DOMAIN>" # Must match APPLICATION_HOST above
# -------------------------------------------------------
# Worker
# -------------------------------------------------------
worker:
replicaCount: 3
resources:
requests:
cpu: "1000m"
memory: "6Gi"
limits:
cpu: "6"
memory: "12Gi"
# -------------------------------------------------------
# BuildKit (crew image builds)
# -------------------------------------------------------
buildkit:
enabled: true
replicaCount: 1
resources:
requests:
cpu: "500m"
memory: "2Gi"
limits:
cpu: "4"
memory: "8Gi"
# -------------------------------------------------------
# OAuth microservice (Built-In Integrations — optional)
# Remove this block if you do not need tool integrations
# (Microsoft 365, Google Workspace, HubSpot, etc.)
# -------------------------------------------------------
oauth:
enabled: true
gateway:
enabled: true
pathPrefix: "/oauthsvc"
# -------------------------------------------------------
# RBAC
# -------------------------------------------------------
rbac:
create: true
The image.registries block is required for direct Helm installs. It provides credentials for pulling platform images (busybox, redis, buildkit, wharf, etc.) from the Replicated proxy at images.crewai.com. Use the same email and token used for helm registry login registry.crewai.com.When installing via Replicated KOTS, these credentials are injected automatically — image.registries is not needed.
DB_USER format for Cloud SQL IAM authentication: Set this to the GSA email without the .gserviceaccount.com suffix. The Cloud SQL Auth Proxy strips this suffix during IAM token exchange. Including the full suffix causes authentication to fail silently.
- Correct:
crewai-platform@my-project.iam
- Wrong:
crewai-platform@my-project.iam.gserviceaccount.com
Part 6: Install
Create GCP-Managed TLS Certificate (if using certmap)
gcloud certificate-manager certificates create crewai-cert \
--domains="<YOUR_DOMAIN>"
gcloud certificate-manager maps create crewai-cert-map
gcloud certificate-manager maps entries create crewai-cert-entry \
--map=crewai-cert-map \
--certificates=crewai-cert \
--hostname="<YOUR_DOMAIN>"
Log In to the Helm Registry
helm registry login registry.crewai.com \
--username "<YOUR_LICENSE_EMAIL>" \
--password "<YOUR_REPLICATED_LICENSE_TOKEN>"
Run Helm Install
helm install crewai-platform \
oci://registry.crewai.com/crewai/stable/crewai-platform \
--values values.yaml \
--namespace crewai \
--create-namespace
Annotate the Crews Namespace (Post-Install, Required)
After the first install creates the crewai-crews namespace, annotate the default ServiceAccount so build pods can push images to Artifact Registry via Workload Identity:
kubectl annotate serviceaccount default -n crewai-crews --overwrite \
iam.gke.io/gcp-service-account=${GSA_NAME}@${GCP_PROJECT_ID}.iam.gserviceaccount.com
Until this annotation is applied, all crew image builds will fail with a permission denied error when pushing to Artifact Registry.
Part 7: Post-Install (Required for All Deployments)
Wait for all pods to reach Running status, then run the following commands:
# Initialize the internal organization
kubectl exec -it deploy/crewai-web -- bin/rails studio:install_internal_organization
# Set up default permissions
kubectl exec -it deploy/crewai-web -- bin/rails factory:setup_permissions_defaults
# Add the initial platform owner (replace with your admin's email)
kubectl exec -it deploy/crewai-web -- bin/rails 'factory:add_owner[2,admin@your-company.com]'
For Entra ID deployments, do not run factory:grant_admin. Admin access is controlled entirely by the factory-admin App Role assigned in the Azure portal. Running factory:grant_admin is only needed for WorkOS, Okta, and local auth deployments.
Part 8: Studio V2 Setup (Post-Install)
Studio V2 has no Helm values and cannot be configured in values.yaml. Adding studioV2.enabled, STUDIO_V2_ENABLED, or any similar key to your values has no effect — Helm silently ignores unrecognized keys.
Setup is always performed post-install in this order:
Step 1: Create the LLM Connection (UI)
- Log in to the platform at
https://<YOUR_DOMAIN>
- Navigate to Settings → LLM Connections
- Click New Connection
- Set the name to exactly
studio-v2 (lowercase, no spaces — this exact string is required)
- Select your LLM provider, enter the model name and API key
- Click Save
The connection name must be exactly studio-v2. The install commands in Step 3 look up this name specifically — any variation in spelling or capitalization will cause them to fail.
Step 2: Set as Default Connection (UI)
- Navigate to Settings → Crew Studio
- Under Default Connection, select
studio-v2
- Click Save
Step 3: Run Install Commands (kubectl)
Run these commands in order. Each must complete successfully before running the next.
# 1. Install the Studio agent (requires studio-v2 LLM Connection to exist)
kubectl exec -it deploy/crewai-web -- bin/rails studio:agent:install
# 2. Sync tools
kubectl exec -it deploy/crewai-web -- \
bin/rails studio:tools:sync_crewai_tools studio:tools:sync_enterprise_tools
# 3. Install the Studio runner
kubectl exec -it deploy/crewai-web -- bin/rails studio:runner:install
studio:agent:install will fail if the studio-v2 LLM Connection does not already exist. Complete Steps 1 and 2 before running any kubectl commands.
Part 9: Verify
# Check all pods are running
kubectl get pods -n crewai
# Check web application health
kubectl exec -it deploy/crewai-web -- bin/rails runner "puts 'OK'"
# Verify Wharf is running
kubectl get pods -n crewai -l app.kubernetes.io/component=wharf
Gateway and DNS
# Get the Gateway external IP — update your DNS A record to point to it
kubectl get gateway -n crewai
# Verify the application is reachable
curl -I https://<YOUR_DOMAIN>/health
Entra ID Authentication
- Open
https://<YOUR_DOMAIN> in a browser
- Click Sign in — you should be redirected to the Microsoft login page
- Log in with an Entra ID account that has been assigned either the Member or Factory Admin App Role
- After login, verify you are redirected back to the platform and successfully authenticated
Wharf Trace Collection
# Verify Wharf database tables were created
kubectl exec -it deploy/crewai-web -- \
bin/rails runner "puts ActiveRecord::Base.connection.tables.grep(/wharf/).inspect"
# Check Wharf pod logs
kubectl logs -l app.kubernetes.io/component=wharf --tail=30 -n crewai
Studio V2
# Verify Studio deployments are running
kubectl get deploy -n crewai | grep studio
# Check Studio assistant logs
kubectl logs -l app=studio-assistant --tail=20 -n crewai
Both the studio-assistant and studio-runner deployments should show Running.
Troubleshooting
Entra ID: Silent Auth Failure (No Error, Login Loops)
Cause: ENTRA_ID_CLIENT_ID or ENTRA_ID_TENANT_ID were placed under secrets: instead of envVars:.
The chart template does not inject Entra ID values from the secrets: section into pod environment variables. The pod starts normally but cannot authenticate because the identifiers are absent from the environment.
Fix: Move ENTRA_ID_CLIENT_ID and ENTRA_ID_TENANT_ID to envVars:. Only ENTRA_ID_CLIENT_SECRET belongs under secrets:.
Entra ID: “Redirect URI mismatch” Error
Cause: The redirect URI registered in Azure does not exactly match https://<YOUR_DOMAIN>/auth/entra_id/callback.
Fix: In the Azure portal, go to App registrations → <your app> → Authentication and verify the redirect URI matches exactly, including the https:// prefix, exact domain, and the path /auth/entra_id/callback.
Cloud SQL: Auth Proxy Authentication Failure
Symptoms: Pods show issue connecting with your username/password or fe_sendauth: no password supplied.
- Verify
autoIamAuthn: true is set in cloudSqlProxy:
- Verify
cloudsql.iam_authentication=on is set on the Cloud SQL instance:
gcloud sql instances describe $SQL_INSTANCE \
--format='value(settings.databaseFlags)'
- Verify the IAM database user exists:
gcloud sql users list --instance=$SQL_INSTANCE --format="table(name,type)"
- Verify
DB_USER uses the truncated format (without .gserviceaccount.com)
GCS: SignedUrlUnavailable Error
Symptoms: Logs show Google::Cloud::Storage::SignedUrlUnavailable.
Fix: Ensure GCS_IAM_SIGNING: "true" is set in envVars: and the GSA has roles/iam.serviceAccountTokenCreator. This setting is required whenever Workload Identity is used.
Cloud SQL: Missing OAuth Database
Symptoms: OAuth service fails to start; logs show database does not exist.
Fix: Verify POSTGRES_OAUTH_DB: "crewai_plus_oauth_production" is set in envVars:. The chart default is oauth_db. If you created the database as crewai_plus_oauth_production but did not override this value, the service connects to the wrong database name.
Studio V2: studio:agent:install Fails
Cause: The studio-v2 LLM Connection does not exist in the database yet.
Fix: Complete the UI steps (Settings → LLM Connections → New Connection named exactly studio-v2, then Settings → Crew Studio → set as Default Connection) before running any Rails tasks.
Artifact Registry: Build Pod Push Failures
Symptoms: Crew image builds fail with unauthorized or denied during push.
Fix: Verify the Workload Identity annotation on the crews namespace ServiceAccount:
kubectl get serviceaccount default -n crewai-crews -o jsonpath='{.metadata.annotations}'
If missing, re-run the annotation command from Part 6.
Workload Identity Not Working
Verify the cluster has Workload Identity enabled:
gcloud container clusters describe $GKE_CLUSTER \
--region=$GCP_REGION \
--format='value(workloadIdentityConfig.workloadPool)'
# Expected: <GCP_PROJECT_ID>.svc.id.goog
Verify the ServiceAccount annotation:
kubectl get serviceaccount $KSA_NAME -n $GKE_NAMESPACE -o yaml | grep gcp-service-account
Test from a pod:
kubectl run -it --rm wi-test \
--namespace=$GKE_NAMESPACE \
--image=google/cloud-sdk:slim \
--serviceaccount=$KSA_NAME \
--restart=Never -- \
gcloud auth print-access-token