Skip to main content

Overview

This guide covers AWS-specific integration for CrewAI Platform deployments on Amazon EKS. It focuses on how CrewAI uses AWS services and the platform-specific configuration required, rather than general AWS setup.
This guide assumes you have:
  • An EKS cluster running Kubernetes 1.32.0+
  • AWS CLI and kubectl configured
  • Helm 3.10+ installed
  • Basic familiarity with AWS services (RDS, S3, ALB)

Prerequisites

Before configuring CrewAI Platform, ensure these AWS components are in place: CrewAI Platform supports AMD64 (x86_64) Kubernetes worker nodes. ARM64 (aarch64) worker nodes are not currently supported. For full platform requirements, see the Requirements Guide.

Required AWS Infrastructure

Do not proceed with CrewAI installation until these prerequisites are met. The Helm chart will fail to deploy without them.

Amazon Aurora for PostgreSQL

CrewAI Platform requires PostgreSQL 16.8+ for production deployments. This section covers RDS-specific requirements for CrewAI.

Aurora Instance Sizing

Minimum recommended specifications based on CrewAI workload characteristics:
CrewAI’s Rails-based architecture benefits from memory-optimized instances (R6g family). Use gp3 storage with minimum 3000 IOPS for production workloads.

Network Connectivity

CrewAI pods must reach your RDS instance. Two options: Option 1: RDS in Private Subnet (Recommended)
  • Place RDS in private subnets within your EKS VPC
  • No internet exposure
  • Security group allows PostgreSQL (5432) from EKS node security group
**Option 2: RDS with Public Access **
  • Enable public accessibility on RDS instance
  • Configure security group to allow EKS NAT gateway IPs
  • Requires SSL/TLS (enforce sslmode=require)

Database Setup

CrewAI requires four databases — primary, cable, OAuth, and Wharf for OTLP trace storage:
When using an external RDS instance, you must manually create these databases before deploying CrewAI Platform. The Helm chart does not automatically create databases when postgres.enabled: false.

Helm Configuration

Amazon S3 for Object Storage

CrewAI Platform uses S3 for storing crew artifacts, tool outputs, and user uploads. This section covers S3 integration and authentication.

S3 Bucket Configuration

S3 Authentication Options

CrewAI supports three authentication methods for S3. Choose based on your security requirements: Best for: New EKS deployments (EKS 1.24+), highest security Pod Identity provides credentials without OIDC configuration or static keys. Benefits:
  • No long-lived credentials
  • Simplified setup vs IRSA — does not require an OIDC provider on the EKS cluster
  • Automatic credential rotation
  • Native EKS integration
Setup Steps:
  1. Create IAM policy for S3 access:
  1. Create IAM role and associate with Pod Identity:
Helm Configuration:
When rbac.create: true (the default), the chart automatically creates a ServiceAccount named crewai-sa. The Pod Identity association must reference this name. You do not need to set serviceAccount.name explicitly.

Option 2: Static Access Keys

Best for: Development environments, non-EKS Kubernetes clusters
Not recommended for production. Use Pod Identity or IRSA instead.

Option 3: IAM Roles for Service Accounts (IRSA)

Best for: Existing EKS clusters with OIDC provider already configured IRSA binds an IAM role to a Kubernetes ServiceAccount via an OIDC trust relationship. Unlike Pod Identity, IRSA requires an OIDC provider on the EKS cluster. Prerequisites:
  • EKS cluster with OIDC provider configured (AWS IRSA Setup)
  • IAM role with the S3 policy from Option 1 attached
Setup Steps:
  1. Get your OIDC provider URL:
  1. Create IAM role with OIDC trust relationship:
The Condition in the trust relationship must match the exact namespace and ServiceAccount name used by CrewAI pods. With rbac.create: true (default), the ServiceAccount is named crewai-sa. Adjust the namespace (crewai in the example) to match your deployment namespace.
Crew build pods also need IRSA access. When a crew is deployed, BuildKit spawns child pods in the crewai-crews namespace (or per-org namespaces like crewai-crews-org-1 when namespace isolation is enabled). These child pods use the default ServiceAccount in that namespace — not crewai-sa. If the default ServiceAccount is not included in the IRSA trust policy, crew image pushes to ECR will fail with Unauthorized.Add the crews namespace ServiceAccount to the trust policy — see Post-Install: IRSA Trust Policy for Crew Build Pods.
  1. Configure Helm values with the IRSA annotation:
Helm Configuration:
The same IAM role can grant both S3 and ECR permissions. See Combined IAM Policy for a single role covering all AWS services.

Application Load Balancer (ALB)

CrewAI Platform requires specific ALB configuration to support long-running crew executions and WebSocket connections.

CrewAI-Specific ALB Requirements

CrewAI’s architecture has specific needs:
  • Long-running requests: Crew executions can take 5+ minutes
  • WebSocket support: ActionCable requires persistent connections
  • Session affinity: Not required (stateless application)

ALB Security Group Configuration

The ALB security group should allow:
  • Inbound: HTTPS (443) from your allowed CIDR ranges (e.g., 0.0.0.0/0 for public access)
  • Outbound: HTTP to EKS worker node security group on NodePort range
EKS worker node security group should allow:
  • Inbound: HTTP from ALB security group

ACM Certificate

CrewAI requires a valid SSL certificate:

Internet-Facing vs Internal ALB

The scheme setting controls whether the ALB is publicly accessible or restricted to your internal network.
ALB terminates TLS and forwards plain HTTP to backend pods. Set web.enableSslFromPuma: false (the chart default is true). Without this, Puma expects HTTPS connections but receives HTTP from the ALB, causing 502 Bad Gateway errors and failed health checks.
The scheme value is case-sensitive. The AWS Load Balancer Controller only accepts lowercase values. Using "Internal" or "Internet-Facing" (with capital letters) will cause the ALB to silently fail to provision.
Internet-facing ALB (default):
Internal ALB:

ALB Subnet Selection

The AWS Load Balancer Controller uses subnet tags to automatically discover which subnets to place the ALB in. If the wrong subnets are tagged, the ALB will be created in unintended subnets. Required subnet tags: Ensure only the subnets where you want the ALB placed have the appropriate tag. Remove the tag from any subnets that should not host the ALB. Verify current subnet tags:
Tag the correct EKS subnets (internal ALB example):
Remove tags from incorrect subnets:

Explicit Subnet Selection

If you cannot modify subnet tags (e.g., shared VPC environments), you can explicitly specify which subnets the ALB should use via the web.ingress.annotations field:
When using explicit subnet annotations, the controller bypasses tag-based auto-discovery entirely for that ingress resource.

ALB Security Features

You can enable additional security features on the ALB using web.ingress.annotations. These annotations are passed directly to the AWS Load Balancer Controller.

Deletion Protection

Prevents accidental deletion of the ALB:
With deletion protection enabled, helm uninstall and kubectl delete ingress will not be able to remove the ALB. You must first disable deletion protection manually before teardown:

Access Logs

Enables logging of all requests to an S3 bucket:
The S3 bucket must have a bucket policy that allows the Elastic Load Balancing service to write logs. See AWS documentation on access log bucket requirements for the required policy and the ELB account ID for your region.

WAF Integration

Attach an AWS WAFv2 Web ACL to protect the ALB:
The Web ACL must already exist in the same region as the ALB. List available Web ACLs with:

Combined Example

To enable all three features together, combine the load-balancer-attributes values in a single annotation:

Pre-existing Security Groups

By default, the AWS Load Balancer Controller automatically creates a security group for the ALB. In environments with restrictive AWS IAM policies that forbid automatic security group creation, you can use a pre-existing security group instead.

Using a Pre-existing Security Group

Specify your existing security group ID via annotation:
You can specify multiple security groups as a comma-separated list:

Disabling Backend Security Group Management

By default, the AWS Load Balancer Controller also modifies the backend (node/pod) security groups to allow inbound traffic from the ALB. If your AWS policies also forbid modifying existing security groups, disable this behavior:
When manage-backend-security-group-rules is set to false, you must manually configure security group rules to allow traffic from the ALB to your EKS worker nodes or pods.Required manual rule on node/pod security group:
  • Type: Custom TCP
  • Port: Target port (default: 443 for CrewAI)
  • Source: ALB security group ID (e.g., sg-0123456789abcdef0)

Pre-existing Security Group Requirements

Your pre-existing security group must allow the following traffic:

Complete Example for Restrictive Environments

For environments where both security group creation and modification are forbidden:

Amazon ECR for Container Images

CrewAI Platform requires Amazon ECR for storing crew automation container images. When users create and deploy crews, CrewAI builds container images and pushes them to ECR.

ECR Repository Requirements

Critical Requirements:
  • Repository URI must end in /crewai-enterprise
  • Immutable tags must be disabled (CrewAI overwrites tags for crew versions)
  • Lifecycle policies recommended to manage old images
Do NOT set global.imageRegistry for standard crew-image-only ECR deployments. global.imageRegistry redirects ALL platform component image pulls (Redis, BuildKit, Wharf, busybox, and the main application) away from images.crewai.com, causing ImagePullBackOff on every pod. Use CREW_IMAGE_REGISTRY_OVERRIDE to direct crew build images to ECR. Only set global.imageRegistry if you have completed a full platform image mirror per the private-registry guide.

Create ECR Repository

Valid repository URIs:
  • 123456789012.dkr.ecr.us-east-1.amazonaws.com/crewai-enterprise
  • 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-org/crewai-enterprise
  • 123456789012.dkr.ecr.us-east-1.amazonaws.com/prod/crewai-enterprise
  • 123456789012.dkr.ecr.us-east-1.amazonaws.com/crewai (must end in /crewai-enterprise)
  • 123456789012.dkr.ecr.us-east-1.amazonaws.com/crewai-platform (wrong suffix)

ECR Authentication with Pod Identity

CrewAI pods require ECR push and pull permissions for building and deploying crew images. Create IAM policy for ECR access:
Attach ECR policy to Pod Identity role:

Combined IAM Policy (S3 + ECR)

For production deployments using Pod Identity, combine S3 and ECR permissions:

Helm Configuration for ECR

Do NOT include /crewai-enterprise in CREW_IMAGE_REGISTRY_OVERRIDE. The platform appends this suffix automatically. If your ECR repository is 123456789012.dkr.ecr.us-east-1.amazonaws.com/production/crewai-enterprise, set only 123456789012.dkr.ecr.us-east-1.amazonaws.com/production. Including the suffix results in push failures to a double-suffixed path (/.../crewai-enterprise/crewai-enterprise/<tag>).Valid values:
  • 123456789012.dkr.ecr.us-east-1.amazonaws.com/crewai-enterprise ← repository is …/crewai-enterprise
  • 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-org ← repository is …/my-org/crewai-enterprise
Invalid (do not use):
  • 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-org/crewai-enterprise ← suffix already included

ECR Authentication: credHelper vs Pod Identity / IRSA

When mirroring platform images to ECR and using image.registries[].credHelper: "ecr-login", be aware of an important distinction:
credHelper does not work for kubelet image pulls. Kubernetes kubelet only supports static auths entries from kubernetes.io/dockerconfigjson secrets — it does not execute credential helper binaries. The credHelper configuration is only effective for BuildKit, which mounts the docker config directly and can invoke the helper binary (if present in the BuildKit image).For kubelet to pull platform images from ECR, use one of these approaches:
  • Node-level ECR auth via EC2 instance profile (EKS worker nodes do this automatically for same-account ECR registries)
  • A CronJob or controller (e.g., ecr-credential-helper) that periodically refreshes static tokens in the image pull secret
  • Pod Identity or IRSA on the node (for cross-account ECR access)
For crew image builds and pushes (via BuildKit), Pod Identity or IRSA on the crewai-sa ServiceAccount is the recommended approach. BuildKit inherits the pod’s IAM credentials and can authenticate to ECR without static tokens.

Verifying ECR Access

Test ECR authentication from CrewAI pods:

Post-Install: IRSA Trust Policy for Crew Build Pods

When a crew is deployed, BuildKit spawns per-build child pods in the crewai-crews namespace. These child pods use the default ServiceAccount in that namespace — not the crewai-sa ServiceAccount used by the main platform pods. If you are using IRSA for ECR authentication, the trust policy must include both ServiceAccounts.
Without this step, the main BuildKit deployment can authenticate to ECR, but the child build pods that actually push crew images will fail with Unauthorized. This is one of the most common IRSA misconfigurations for CrewAI on AWS.
Update the IAM role trust policy to include the crews namespace:
The trust policy uses StringEquals with an array to allow multiple ServiceAccounts to assume the same role. Replace crewai with your platform namespace and crewai-crews with the value of global.crewNamespace (default: crewai-crews).
Annotate the default ServiceAccount in the crews namespace: After the initial Helm install (which creates the crewai-crews namespace automatically), annotate the default ServiceAccount so build pods inherit IRSA credentials:
If deploying via CI/CD and you need to annotate before Helm install, create the namespace in advance:
ArgoCD users: This step requires a two-Application pattern because the crewai-crews namespace does not exist until after the first sync completes. See the ArgoCD Deployment Guide.
Per-org namespace isolation: If k8s_namespace_isolation is enabled, crew pods run in per-org namespaces (e.g., crewai-crews-org-1, crewai-crews-org-2). Each org namespace’s default ServiceAccount must also be included in the IRSA trust policy and annotated. Use a wildcard condition with StringLike instead:
Verify the trust policy is correct:

AWS Secrets Manager Integration

AWS Secrets Manager provides centralized secret management with automatic rotation for CrewAI Platform.

Which Secrets to Store

Store in AWS Secrets Manager (sensitive, need rotation):
  • DB_PASSWORD - Database credentials
  • SECRET_KEY_BASE - Rails secret key
  • ENTRA_ID_CLIENT_SECRET / OKTA_CLIENT_SECRET - OAuth secrets
  • AWS_SECRET_ACCESS_KEY - If using static S3 credentials
  • GITHUB_TOKEN - For private repository access
Keep in values.yaml (configuration, not secrets):
  • DB_HOST, DB_PORT, DB_USER, POSTGRES_DB, POSTGRES_CABLE_DB
  • AWS_REGION, AWS_BUCKET
  • APPLICATION_HOST
  • AUTH_PROVIDER

Secret Structure in Secrets Manager

CrewAI expects secrets in specific formats. Two options: Option 1: Single Secret with Multiple Keys Create one secret crewai/platform with JSON structure:
Option 2: Separate Secrets Create individual secrets:
  • crewai/db-password
  • crewai/secret-key-base
  • crewai/entra-id-credentials (JSON with client_id, client_secret, tenant_id)

External Secrets Operator Setup

CrewAI uses External Secrets Operator (ESO) to sync secrets from AWS Secrets Manager to Kubernetes. Install ESO (if not already installed):
Create IAM Policy for ESO:
Helm Configuration:

Secret Mapping Example

If using single secret with JSON structure:

Complete AWS Deployment Example

If using ALB, you must set web.enableSslFromPuma: false. The chart default is true. Omitting this causes 502 Bad Gateway on every request because Puma receives plain HTTP from the ALB but expects HTTPS.
If using WorkOS authentication, place WORKOS_API_KEY under envVars: — NOT under secrets:. See the WorkOS SSO guide for the full explanation and the correct YAML placement. This is a known chart limitation.
Here’s a complete production configuration for AWS:
Deploy:

Troubleshooting AWS-Specific Issues

ALB Not Provisioning

Symptoms: Ingress shows no ADDRESS after several minutes
Common causes:
  1. Incorrect scheme casing — The scheme value is case-sensitive. Use "internal" or "internet-facing" (all lowercase). Values like "Internal" or "Internet-Facing" are silently rejected. See Internet-Facing vs Internal ALB.
  2. AWS Load Balancer Controller not installed or not running
  3. Insufficient IAM permissions for LBC
  4. Subnet tags missing for ALB discovery
Check LBC status:
Verify subnet tags (required for ALB):
  • Public subnets: kubernetes.io/role/elb=1
  • Private subnets: kubernetes.io/role/internal-elb=1

ALB Created in Wrong Subnets

Symptoms: ALB is provisioned but placed in subnets outside the EKS cluster VPC or in unintended subnets. Common causes:
  1. Multiple subnets tagged with kubernetes.io/role/elb or kubernetes.io/role/internal-elb across different VPCs or availability zones
  2. Shared VPC where non-EKS subnets also carry the discovery tag
Resolution:
  1. Identify which subnets the EKS cluster uses:
  1. Remove discovery tags from non-EKS subnets and ensure only the correct subnets are tagged. See ALB Subnet Selection for details.
  2. Alternatively, use the explicit subnet annotation to bypass auto-discovery:
  1. After fixing tags or adding the annotation, delete the ingress and re-deploy to force the LBC to re-discover subnets.

RDS Connection Timeout

Symptoms: Pods show could not connect to server: Connection timed out Check security groups:
Test connectivity from pod:

S3 Access Denied

Symptoms: Logs show Access Denied or 403 errors for S3 operations Verify authentication method: For Pod Identity:
For IRSA:
For Static Keys:

Secrets Manager Access Denied

Symptoms: ExternalSecret shows SecretSyncedError

ECR Builds Fail After Running Successfully

Symptoms: Crew builds worked initially but start failing with unauthorized or denied errors after several hours Common causes:
  1. ECR auth token expiration — ECR tokens obtained via aws ecr get-login-password expire after 12 hours. If you configured image.registries with static username/password (using an ECR token), builds will fail once the token expires.
  2. credHelper binary not present — If using credHelper: "ecr-login" in image.registries, the ecr-login binary must be available in the BuildKit image. Without it, the docker config references a helper that doesn’t exist, causing silent auth failures.
Resolution: The recommended approach is to use Pod Identity or IRSA instead of static ECR tokens. With Pod Identity or IRSA, credentials are automatically rotated and never expire:
If you must use static tokens, set up a CronJob to refresh them before expiration:

BuildKit Cannot Push to ECR

Symptoms: BuildKit logs show failed to push or unauthorized: authentication required when building crew images Diagnosis:
Common causes:
  1. Missing ECR permissions — The IAM role must include ecr:PutImage, ecr:InitiateLayerUpload, ecr:UploadLayerPart, and ecr:CompleteLayerUpload. See Combined IAM Policy.
  2. ECR repository doesn’t exist — The repository path ending in /crewai-enterprise must be pre-created in ECR. CrewAI does not auto-create ECR repositories.
  3. Pod Identity / IRSA not reaching BuildKit — Ensure the Pod Identity association or IRSA trust relationship references the correct ServiceAccount (crewai-sa) and namespace.

ServiceAccount Mismatch with Pod Identity or IRSA

Symptoms: Pods show AccessDeniedException, ExpiredTokenException, or WebIdentityErr despite correct IAM policies Common causes:
  1. Wrong ServiceAccount name in association/trust — Pod Identity associations and IRSA trust relationships must reference the exact ServiceAccount name (crewai-sa by default) and namespace. A mismatch means pods get no IAM credentials.
  2. Namespace mismatch — The namespace in the Pod Identity association or IRSA trust policy must match the Helm release namespace.
  3. Missing crew build pod ServiceAccount — The IRSA trust policy only includes crewai-sa but not crewai-crews:default. Platform pods work fine, but crew image builds fail with Unauthorized when pushing to ECR. See Post-Install: IRSA Trust Policy for Crew Build Pods.
Diagnosis: