Skip to main content

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:
  • GKE cluster with Workload Identity enabled
  • Gateway API enabled (gcloud container clusters update --gateway-api=standard)
  • GCP APIs enabled (see below)
  • GCP Service Account created with required IAM roles
  • Workload Identity binding created for platform and crews namespaces
  • Cloud SQL instance created with IAM authentication enabled
  • All four databases created: crewai_plus_production, crewai_plus_cable_production, crewai_plus_oauth_production, wharf
  • SQL privileges granted to IAM user on all four databases
  • GCS bucket created
  • Artifact Registry repository created (mutable tags, ends in /crewai-enterprise)
  • Entra ID App Registration complete with redirect URI configured
  • Redirect URI https://<YOUR_DOMAIN>/auth/entra_id/callback registered in Azure before helm install
  • factory-admin App Role created in Azure and assigned to admin users
  • values.yaml assembled with all placeholders filled in
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

Set Shell Variables

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

Grant IAM Roles

Bind Workload Identity


Part 2: Cloud SQL Setup

Create the Cloud SQL Instance

Enable IAM Authentication on the Instance

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

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

Grant SQL Privileges

Connect to the instance as the postgres superuser:
Then run the following SQL (replace GSA_NAME@GCP_PROJECT_ID.iam with your actual IAM user, e.g., crewai-platform@my-project.iam):

Part 3: GCS and Artifact Registry

Create GCS Bucket

Create Artifact Registry Repository

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


Part 4: Microsoft Entra ID App Registration

Create the App Registration

  1. Go to portal.azure.com and navigate to Microsoft Entra ID → App registrations → New registration
  2. Set the application name (suggested: CrewAI)
  3. Under Supported account types, select Accounts in this organizational directory only
  4. Under Redirect URI, select platform Web and enter: https://<YOUR_DOMAIN>/auth/entra_id/callback
  5. 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

  1. In the left sidebar under Manage, click Certificates & secrets
  2. Click New client secret, set a description and expiration, then click Add
  3. Copy the secret Value immediately — you cannot view it again after leaving the page
  4. This is ENTRA_ID_CLIENT_SECRET
  1. Navigate to Enterprise applications → All applications and select your app
  2. In the left sidebar under Security, click Permissions
  3. Click Grant admin consent for <your organization>
  4. Confirm that Microsoft Graph User.Read is granted

Configure App Roles

In the left sidebar under Manage, click App roles, then Create app role. Create both of the following roles: Ensure “Do you want to enable this app role?” is checked for each role before saving.

Assign Users

  1. Go to Enterprise applications → <your app> → Users and groups
  2. Click Add user/group
  3. Assign regular users the Member role
  4. 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:
values.yaml
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)

Log In to the Helm Registry

Run Helm Install

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:
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:
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)

  1. Log in to the platform at https://<YOUR_DOMAIN>
  2. Navigate to Settings → LLM Connections
  3. Click New Connection
  4. Set the name to exactly studio-v2 (lowercase, no spaces — this exact string is required)
  5. Select your LLM provider, enter the model name and API key
  6. 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)

  1. Navigate to Settings → Crew Studio
  2. Under Default Connection, select studio-v2
  3. Click Save

Step 3: Run Install Commands (kubectl)

Run these commands in order. Each must complete successfully before running the next.
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

Platform Health

Gateway and DNS

Entra ID Authentication

  1. Open https://<YOUR_DOMAIN> in a browser
  2. Click Sign in — you should be redirected to the Microsoft login page
  3. Log in with an Entra ID account that has been assigned either the Member or Factory Admin App Role
  4. After login, verify you are redirected back to the platform and successfully authenticated

Wharf Trace Collection

Studio V2

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.
  1. Verify autoIamAuthn: true is set in cloudSqlProxy:
  2. Verify cloudsql.iam_authentication=on is set on the Cloud SQL instance:
  3. Verify the IAM database user exists:
  4. 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:
If missing, re-run the annotation command from Part 6.

Workload Identity Not Working

Verify the cluster has Workload Identity enabled:
Verify the ServiceAccount annotation:
Test from a pod: