Skip to main content

Overview

The CrewAI Platform Helm chart provides a production-ready deployment with:
  • PostgreSQL database (internal StatefulSet or external)
  • MinIO object storage (optional S3-compatible storage)
  • Background workers (Rails ActiveJob for async processing)
  • Web application (Rails-based with flexible ingress support)
  • Intelligent secret management with auto-population from Replicated license fields
  • Multi-provider ingress support (AWS ALB, NGINX, Istio)
  • BuildKit service for container image building

Quick Start for Production

Minimum Required Configuration

For a production deployment with external database and object storage:
my-values.yaml
# Minimum required configuration — replace all placeholders with real values
image:
  tag: latest # Use a specific version tag in production

postgres:
  enabled: false # Use an external managed PostgreSQL

minio:
  enabled: false # Use external object storage

envVars:
  # Database
  DB_HOST: "<your-db-host>"
  DB_PORT: "5432"
  DB_USER: "<your-db-user>"
  POSTGRES_DB: "crewai_plus_production"
  POSTGRES_CABLE_DB: "crewai_plus_cable_production"
  POSTGRES_OAUTH_DB: "crewai_plus_oauth_production"

  # Object storage — choose the provider that matches your cloud
  # STORAGE_SERVICE: "<amazon|microsoft|google>"

  # Application hostname
  APPLICATION_HOST: "<your-domain.example.com>"

  # Authentication provider
  AUTH_PROVIDER: "local" # or "entra_id", "okta", "workos", "keycloak"

secrets:
  DB_PASSWORD: "<your-db-password>"

web:
  ingress:
    enabled: true
    className: nginx
    host: "<your-domain.example.com>"
    nginx:
      tls:
        enabled: true
        secretName: "crewai-tls-secret"
This is a generic template. For cloud-specific values and infrastructure setup, see the AWS Guide, GCP Guide, or Azure Guide.
Do not set envVars.K8S_NAMESPACE. The chart automatically derives this value from crewNamespace (default: crewai-crews) and injects it into every pod. Setting it manually overrides the chart value and can cause crew workloads to schedule into the wrong namespace.
Helm silently ignores unrecognized keys. If you add a top-level key that does not exist in values.yaml (for example, adminUsers or studioV2.enabled), helm install and helm lint will not report an error, but the value has no effect — adding these keys has no effect. Always verify configuration keys against the Chart Values Reference.Features that cannot be pre-configured in values.yaml and require post-installation steps:
  • Admin users and org ownership — configure via kubectl exec after install. See the Post-Installation Guide.
  • Studio V2 — requires interactive UI setup plus kubectl exec commands. See the Post-Installation Guide and Studio V2 Setup Guide.

Critical Production Configuration Areas

1. Database Configuration

For External PostgreSQL (Recommended):
postgres:
  enabled: false # Do not create internal PostgreSQL

envVars:
  DB_HOST: "your-postgres-host.amazonaws.com"
  DB_PORT: "5432"
  DB_USER: "crewai"
  POSTGRES_DB: "crewai_plus_production"
  POSTGRES_CABLE_DB: "crewai_plus_cable_production"
  POSTGRES_OAUTH_DB: "crewai_plus_oauth_production"  # Chart default is "oauth_db" — set explicitly to match the database you created

secrets:
  DB_PASSWORD: "your-secure-password"
Database Pre-creation Required: When using an external PostgreSQL instance, you must manually create all required databases before deploying: primary, cable, OAuth, and Wharf (wharf by default, enabled by default). See the AWS or GCP guides for provider-specific setup instructions.
The internal PostgreSQL option (postgres.enabled: true) is not intended for production use. It lacks high-availability features, automated backups, and enterprise-grade reliability. Always use an external managed PostgreSQL service (AWS RDS, Azure Database, Google Cloud SQL, etc.) for production deployments.
Internal PostgreSQL (Development/Testing Only):
postgres:
  enabled: true
  persistence:
    size: 20Gi
    storageClassName: "your-storage-class"
  resources:
    limits:
      cpu: "2"
      memory: "4Gi"

2. Object Storage Configuration

For External S3 (Recommended):
minio:
  enabled: false # Disable internal MinIO

envVars:
  STORAGE_SERVICE: "amazon" # or "microsoft" for Azure
  AWS_REGION: "us-west-2"
  AWS_BUCKET: "your-crewai-bucket"
  # For S3-compatible services:
  # AWS_ENDPOINT: "https://s3.your-provider.com"

secrets:
  AWS_ACCESS_KEY_ID: "your-access-key"
  AWS_SECRET_ACCESS_KEY: "your-secret-key"
For Azure Blob Storage:
envVars:
  STORAGE_SERVICE: "microsoft"
  AZURE_STORAGE_ACCOUNT_NAME: "youraccountname"

secrets:
  AZURE_STORAGE_ACCESS_KEY: "your-azure-key"
For Google Cloud Storage (GCS):
envVars:
  STORAGE_SERVICE: "google"
  GCS_PROJECT_ID: "your-gcp-project-id"
  GCS_BUCKET: "your-crewai-bucket"
  GCS_IAM_SIGNING: "true"
GCS_IAM_SIGNING: "true" enables Workload Identity-based signing for signed URL generation, which avoids the need to supply a service account key. Requires the platform’s Kubernetes service account to be bound to a GCP service account with Storage Object Admin and Service Account Token Creator roles. See the GCP Integration Guide for IAM setup.
The internal MinIO option (minio.enabled: true) is not intended for production use. Use external S3-compatible storage for production deployments.

3. Secret Management

The chart supports three secret management approaches:

Option A: Direct Secret Values (Simple)

secrets:
  DB_PASSWORD: "your-db-password"
  AWS_ACCESS_KEY_ID: "your-aws-key"
  AWS_SECRET_ACCESS_KEY: "your-aws-secret"
  # Other secrets...
Secrets are base64-encoded automatically. Some secrets like SECRET_KEY_BASE are auto-generated with upgrade persistence using the lookup function if not provided. ArgoCD users must set these values explicitly — see ArgoCD Deployment Guide.
Automatic Pod Restarts: When you update secret values and run helm upgrade, pods automatically restart with a rolling update to pick up the new credentials. This ensures credential rotation is applied without manual intervention or downtime.
# Enable external secret store integration
externalSecret:
  enabled: true
  secretStore: "crewai-secret-store"
  secretPath: "crewai/platform"
  databaseSecretPath: "crewai/database"
  includes_aws_credentials: true
  includes_azure_credentials: false

# Configure AWS Secrets Manager
secretStore:
  enabled: true
  provider: "aws"
  aws:
    region: "us-west-2"
    role: "arn:aws:iam::ACCOUNT:role/crewai-secrets-role"
    auth:
      serviceAccount:
        enabled: true
        name: "crewai-secret-reader"

4. Ingress and Networking

AWS Application Load Balancer

web:
  ingress:
    enabled: true
    className: "alb"
    host: "crewai.your-company.com"
    alb:
      scheme: "internet-facing"
      targetType: "ip"
      certificateArn: "arn:aws:acm:us-west-2:ACCOUNT:certificate/CERT-ID"
      sslPolicy: "ELBSecurityPolicy-TLS-1-2-2017-01"

NGINX Ingress Controller

web:
  ingress:
    enabled: true
    className: "nginx"
    host: "crewai.your-company.com"
    nginx:
      sslRedirect: true
      tls:
        enabled: true
        secretName: "crewai-tls-secret"
      enableCors: true
      sessionAffinity: true
      whitelistSourceRange: "10.0.0.0/8,172.16.0.0/12"

Application-Level TLS (Self-Signed)

For environments without ingress TLS, enable application-level HTTPS:
web:
  useHttps: true
  service:
    type: LoadBalancer
  tls:
    autoGenerate: true # Generates self-signed certificate
    duration: 365 # Certificate validity in days
    hosts:
      - localhost
      - crewai.internal

5. Authentication Configuration

Local Authentication

envVars:
  AUTH_PROVIDER: "local"

Microsoft Entra ID (Azure AD)

envVars:
  AUTH_PROVIDER: "entra_id"
  # Client ID and Tenant ID are non-sensitive identifiers — must be in envVars, not secrets
  ENTRA_ID_CLIENT_ID: "your-application-client-id"
  ENTRA_ID_TENANT_ID: "your-tenant-id"

secrets:
  # Only the client secret is a credential and belongs under secrets
  ENTRA_ID_CLIENT_SECRET: "your-client-secret"
ENTRA_ID_CLIENT_ID and ENTRA_ID_TENANT_ID are non-sensitive identifiers and must be placed under envVars:, not secrets:. Only ENTRA_ID_CLIENT_SECRET is a credential and belongs under secrets:.
Setup: You need the Application (client) ID, Directory (tenant) ID, and a client secret from your Azure App Registration. For Azure portal setup steps, see Entra ID SSO.

Okta

envVars:
  AUTH_PROVIDER: "okta"
  OKTA_SITE: "https://your-domain.okta.com"         # non-sensitive — must be in envVars
  OKTA_CLIENT_ID: "your-okta-client-id"             # non-sensitive — must be in envVars
  OKTA_AUTHORIZATION_SERVER: "default"               # non-sensitive — must be in envVars
  OKTA_AUDIENCE: "api://default"                     # non-sensitive — must be in envVars

secrets:
  OKTA_CLIENT_SECRET: "your-okta-client-secret"     # credential — belongs under secrets
OKTA_SITE, OKTA_CLIENT_ID, OKTA_AUTHORIZATION_SERVER, and OKTA_AUDIENCE are non-sensitive configuration identifiers. They must be placed under envVars:, not secrets:. The chart template does not render Okta values from secrets: — placing them there silently excludes them from pod environment variables, causing authentication to fail with no error message referencing the missing keys.
Setup: You need a Client ID, authorization server ID, and audience from an OIDC Web Application integration in the Okta Admin Console. For Okta admin console setup steps, see Okta SSO.

WorkOS

envVars:
  AUTH_PROVIDER: "workos"
  WORKOS_CLIENT_ID: "your-workos-client-id"
  WORKOS_AUTHKIT_DOMAIN: "your-authkit-domain.authkit.com"
  WORKOS_COOKIE_PASSWORD: "your-32-char-cookie-password"
  WORKOS_API_KEY: "your-workos-api-key"
WORKOS_API_KEY is placed under envVars: rather than secrets: due to a known chart template gap — the chart does not render secrets.WORKOS_API_KEY into the pod environment. The key is therefore stored in a ConfigMap rather than a Kubernetes Secret. Verify after install:
kubectl exec -it deploy/crewai-web -- env | grep WORKOS_API_KEY
WORKOS_COOKIE_PASSWORD must be 32 characters or fewer. Generate one with: openssl rand -base64 32 | cut -c -32It is stored as a plain environment variable (not a Kubernetes Secret) by the chart. Supply it via --set or a gitignored values file to avoid committing it.
For the complete WorkOS Dashboard setup, see WorkOS SSO. Setup: You need a Client ID, AuthKit domain, and API key from the WorkOS Dashboard, plus a generated cookie password for session encryption.

Keycloak

envVars:
  AUTH_PROVIDER: "keycloak"
  KEYCLOAK_CLIENT_ID: "your-keycloak-client-id"
  KEYCLOAK_SITE: "https://keycloak.your-domain.com"
  KEYCLOAK_REALM: "your-realm-name"
  KEYCLOAK_AUDIENCE: "account" # Default value, change only if custom audience configured
  KEYCLOAK_DEVICE_AUTHORIZATION_CLIENT_ID: "your-cli-client-id" # Optional, for CLI authentication

secrets:
  # Required for Keycloak authentication
  KEYCLOAK_CLIENT_SECRET: "your-keycloak-client-secret"
Setup: You need a Client ID, Client Secret, realm name, and Keycloak server URL from an OpenID Connect client configured in the Keycloak Admin Console. For Keycloak realm configuration steps, see Keycloak SSO.

6. Resource Sizing

Production-grade resource defaults are included, but adjust based on your workload:
web:
  replicaCount: 2 # Increase for high availability
  resources:
    limits:
      cpu: "6"
      memory: "12Gi"
    requests:
      cpu: "1000m" # Increase for production
      memory: "6Gi"

worker:
  replicaCount: 2 # Increase based on job volume
  resources:
    limits:
      cpu: "6"
      memory: "12Gi"
    requests:
      cpu: "1000m"
      memory: "6Gi"

buildkit:
  enabled: true
  replicaCount: 1
  rootless:
    enabled: true  # Enable for enhanced security
  resources:
    limits:
      cpu: "4"
      memory: "8Gi"
    requests:
      cpu: "500m"
      memory: "2Gi"
The default resource requests are conservative. For production workloads, increase requests to ensure reliable scheduling and performance.

7. High Availability Configuration

For production HA deployments:
web:
  replicaCount: 3  # Multiple replicas for zero-downtime
  terminationGracePeriodSeconds: 60  # Allow graceful shutdown

worker:
  replicaCount: 3  # Distribute background job processing

# Use external, HA-enabled services
postgres:
  enabled: false  # Use AWS RDS Multi-AZ or similar

minio:
  enabled: false  # Use S3 or similar

# Configure node placement for multi-AZ
web:
  nodeSelector:
    topology.kubernetes.io/zone: us-west-2a

worker:
  nodeSelector:
    topology.kubernetes.io/zone: us-west-2b

8. Image Registry Configuration

Using Replicated Proxy (Default)

image:
  host: "images.crewai.com"
  name: "proxy/crewai/crewai/crewai-enterprise-platform"
  tag: "latest"
  pullPolicy: Always
  pullSecret: "docker-registry" # Automatically created by Replicated

Private Registry with Simplified Paths

Choosing the right registry override — key distinction:
SettingScopeWhen to use
CREW_IMAGE_REGISTRY_OVERRIDECrew build images onlyAlways set this when using a private registry for crew deployments. Safe to set without a full mirror.
global.imageRegistryALL platform images (Redis, BuildKit, Busybox, Wharf, main app, etc.)Only set if you have mirrored every platform image to your private registry. Setting this without a full mirror causes ImagePullBackOff on every pod.
If you only need crew build images in your private registry, set CREW_IMAGE_REGISTRY_OVERRIDE and omit global.imageRegistry and global.imageNamePrefixOverride.
When mirroring images to a private registry like AWS ECR or Azure ACR, use global.imageNamePrefixOverride to simplify image paths:
global:
  imageRegistry: "123456789012.dkr.ecr.us-west-2.amazonaws.com"
  imageNamePrefixOverride: "crewai/"

image:
  registries:
    - host: "123456789012.dkr.ecr.us-west-2.amazonaws.com"
      credHelper: "ecr-login"

envVars:
  CREW_IMAGE_REGISTRY_OVERRIDE: "123456789012.dkr.ecr.us-west-2.amazonaws.com"
envVars.CREW_IMAGE_REGISTRY_OVERRIDE is required for crew automation deployments. CrewAI Platform builds crew images internally and pushes them to this registry after each build, so the registry must support both push and pull operations from build and runtime pods.
GCP Artifact Registry: Do NOT use global.imageRegistry for standard GCP/GAR deployments. Setting it to your GAR host redirects all platform image pulls (Redis, BuildKit, Wharf) away from images.crewai.com and causes ImagePullBackOff on every pod. For GCP, use only CREW_IMAGE_REGISTRY_OVERRIDE. See the GCP Integration Guide for the correct configuration.
Supported targets include AWS ECR, Azure Container Registry (ACR), Google Artifact Registry (GAR), and JFrog Artifactory. For canonical requirements, see Requirements. Permission and access checklist:
  • Registry endpoint is reachable from build and runtime pods
  • Credentials grant image push and pull permissions
  • The platform automatically appends /crewai-enterprise; ensure the resulting repository path exists and accepts pushes
For detailed validation behavior and format examples, see envVars.CREW_IMAGE_REGISTRY_OVERRIDE. How it works:
  • Without override: images.crewai.com/proxy/crewai/dockerhub/library/postgres:16
  • With override: 123456789012.dkr.ecr.us-west-2.amazonaws.com/crewai/postgres:16
The imageNamePrefixOverride replaces complex multi-level paths (like proxy/crewai/dockerhub/library/) with a simple prefix. It extracts only the final image name component (e.g., postgres) and applies the override prefix to it. This is not a simple prepend operation - it completely replaces the path structure, making image management in private registries easier. Affected components:
  • Main application, PostgreSQL, MinIO, BuildKit, Redis, Busybox images
  • Environment variables: CONTAINER_REGISTRY_HOSTNAME, AUTOMATION_ECR_REPOSITORY_PREFIX
Complete Example:
global:
  imageRegistry: "123456789012.dkr.ecr.us-west-2.amazonaws.com"
  imageNamePrefixOverride: "crewai/"

image:
  registries:
    - host: "123456789012.dkr.ecr.us-west-2.amazonaws.com"
      credHelper: "ecr-login"

envVars:
  CREW_IMAGE_REGISTRY_OVERRIDE: "123456789012.dkr.ecr.us-west-2.amazonaws.com"

Additional Private Registries

image:
  registries:
    - host: "your-registry.company.com"
      username: "registry-user"
      password: "registry-password"
    - host: "docker.io"
      username: "dockerhub-user"
      password: "dockerhub-token"

Security Best Practices

1. Secret Management

  • Never commit secrets to version control
  • Use external secret stores (AWS Secrets Manager, Azure Key Vault) for production
  • Rotate secrets regularly

2. Network Security

  • Enable TLS for all external endpoints
  • Configure ingress whitelist restrictions
  • Use private subnets for database and internal services
  • Enable CORS with restrictive origin policies

3. RBAC and Service Accounts

rbac:
  create: true # Creates necessary service accounts and role bindings

serviceAccount: "crewai-platform" # Custom service account name

4. Database Security

  • Use encrypted connections to database (SSL/TLS)
  • Store database credentials in secret stores
  • Enable audit logging on database

5. BuildKit Security

For enhanced security, enable rootless BuildKit mode to run container builds without privileged containers:
buildkit:
  enabled: true
  rootless:
    enabled: true
    runAsUser: 1000
    runAsGroup: 1000
    fsGroup: 1000
Benefits:
  • No privileged container required for image builds
  • Runs as non-root user with user namespace remapping
  • Reduces attack surface for container build operations
Requirements:
  • Kubernetes nodes must allow seccompProfile: Unconfined and appArmorProfile: Unconfined
  • May not be compatible with restrictive security policies (e.g., GKE Autopilot)
Note: If rootless mode is not compatible with your environment, the chart defaults to standard privileged BuildKit mode.

6. Database Migration Management

The chart implements automatic database migrations via Helm hooks, ensuring schema changes and data migrations are applied safely during installation and upgrades. Migration Process: During Initial Installation (helm install):
  1. After deployment, a setup Job runs automatically
  2. Job waits for PostgreSQL and MinIO availability (when enabled)
  3. Executes bin/rails db:migrate for main database
  4. Executes bin/rails db:migrate:cable for ActionCable database
  5. Executes bin/rails db:seed to populate initial data
  6. Executes bin/rails data:migrate to apply data migrations
  7. Sets up default permissions and syncs feature flags
  8. Job retries up to 10 times on failure
During Upgrades (helm upgrade):
  1. Before each upgrade, a pre-upgrade Job runs automatically
  2. Job waits for PostgreSQL availability (when using internal PostgreSQL)
  3. Executes bin/rails db:migrate for main database
  4. Executes bin/rails db:migrate:cable for ActionCable database
  5. Executes bin/rails data:migrate to apply data migrations
  6. Job retries up to 3 times on failure
  7. Upgrade proceeds only after successful migration
Data migrations (bin/rails data:migrate) are automatically applied during both initial installation and upgrades, ensuring data transformations are kept in sync with schema changes.
Custom Node Placement:
# For initial setup job
setupJob:
  nodeSelector:
    node-role.kubernetes.io/worker: "true"

# For pre-upgrade migration job
migrationJob:
  nodeSelector:
    node-role.kubernetes.io/worker: "true"
Troubleshooting: View migration job logs:
# For upgrades
kubectl logs -l app.kubernetes.io/component=migration --tail=100

# For initial setup
kubectl logs -l app.kubernetes.io/component=setup --tail=100
Check migration job status:
# For upgrades
kubectl get jobs -l app.kubernetes.io/component=migration

# For initial setup
kubectl get jobs -l app.kubernetes.io/component=setup

Common Deployment Scenarios

Scenario 1: AWS Production Deployment

postgres:
  enabled: false

minio:
  enabled: false

envVars:
  DB_HOST: "crewai-prod.cluster-xyz.us-west-2.rds.amazonaws.com"
  STORAGE_SERVICE: "amazon"
  AWS_REGION: "us-west-2"
  AWS_BUCKET: "crewai-prod-storage"
  APPLICATION_HOST: "crewai.company.com"
  AUTH_PROVIDER: "entra_id"

web:
  replicaCount: 3
  ingress:
    enabled: true
    className: "alb"
    host: "crewai.company.com"
    alb:
      certificateArn: "arn:aws:acm:us-west-2:ACCOUNT:certificate/CERT-ID"

buildkit:
  enabled: true

externalSecret:
  enabled: true
  secretStore: "aws-secrets"

Scenario 2: Azure Production Deployment

my-values.yaml
postgres:
  enabled: false

minio:
  enabled: false

envVars:
  DB_HOST: "crewai-prod.postgres.database.azure.com"
  STORAGE_SERVICE: "microsoft"
  AZURE_STORAGE_ACCOUNT_NAME: "crewaistorage"
  AZURE_CONTAINER_NAME: "crewai-storage"  # Must match the blob container name created in Azure Storage
  APPLICATION_HOST: "crewai.company.com"
  AUTH_PROVIDER: "entra_id"

web:
  replicaCount: 3
  ingress:
    enabled: true
    className: "nginx"
    host: "crewai.company.com"

secretStore:
  enabled: true
  provider: "azure"
  azure:
    vaultUrl: "https://crewai-vault.vault.azure.net"
AZURE_CONTAINER_NAME must match the blob container name you created in Azure Blob Storage. If omitted, uploads will fail at runtime.

Scenario 3: GCP Production Deployment (Cloud SQL + GCS + Artifact Registry)

my-values-gcp.yaml
postgres:
  enabled: false
minio:
  enabled: false

cloudSqlProxy:
  enabled: true
  instanceConnectionName: "your-project:us-central1:crewai-db"
  port: 5432
  privateIp: true
  autoIamAuthn: true

envVars:
  APPLICATION_HOST: "crewai.your-company.com"
  AUTH_PROVIDER: "<your-auth-provider>"  # workos, entra_id, okta, keycloak, or local
  DB_HOST: "127.0.0.1"
  DB_PORT: "5432"
  DB_USER: "crewai-platform@your-project.iam"  # GSA email without .gserviceaccount.com
  POSTGRES_DB: "crewai_plus_production"
  POSTGRES_CABLE_DB: "crewai_plus_cable_production"
  POSTGRES_OAUTH_DB: "crewai_plus_oauth_production"
  STORAGE_SERVICE: "google"
  GCS_PROJECT_ID: "your-gcp-project-id"
  GCS_BUCKET: "your-crewai-bucket"
  GCS_IAM_SIGNING: "true"
  CREW_IMAGE_REGISTRY_OVERRIDE: "us-central1-docker.pkg.dev/your-project/crewai"  # Platform appends /crewai-enterprise automatically — do not include it here

serviceAccount:
  annotations:
    iam.gke.io/gcp-service-account: crewai-platform@your-project.iam.gserviceaccount.com

web:
  ingress:
    enabled: false  # Using GKE Gateway API — see cloud-providers/gcp

gateway:
  enabled: true
  name: crewai-gateway
  gatewayClassName: gke-l7-regional-external-managed
  # For full gateway listeners (TLS, ports, etc.), see the GCP Integration Guide
See the GCP Integration Guide for full infrastructure setup.

Scenario 4: Development/Testing Environment

postgres:
  enabled: true
  resources:
    limits:
      cpu: "1"
      memory: "2Gi"

minio:
  enabled: true

web:
  replicaCount: 1
  service:
    type: LoadBalancer
  tls:
    autoGenerate: true

worker:
  replicaCount: 1

envVars:
  RAILS_LOG_LEVEL: "debug"
  AUTH_PROVIDER: "local"
  STORAGE_SERVICE: "amazon"
  AWS_ENDPOINT: "http://crewai-minio:9000"

secrets:
  DB_PASSWORD: "dev-password"