Kubernetes (Helm)
The Helm chart is available exclusively to Mastra Enterprise customers. It is distributed from a private registry: your Enterprise license is exchanged for short-lived pull credentials. Contact us to get an Enterprise license.
Deploy the Mastra platform — Mastra Server (your agent runtime) and Mastra Studio (the management UI) — on any Kubernetes cluster using the official mastra-projects Helm chart. The chart works on GKE, EKS, AKS, and generic or local clusters, and handles service exposure, TLS, and secret wiring for you.
The chart follows two design principles:
- Bring your own images. You build your Mastra project image with
mastra buildand push it to a registry. The chart never builds images. - Bring your own data stores. You point the chart at your PostgreSQL database and, optionally, an S3-compatible object store. Nothing stateful is bundled.
This guide covers the Helm chart, which manages Deployments, Services, Ingress or Gateway resources, and secrets for you. To write the Kubernetes manifests yourself, or to run multiple server pods with shared pub/sub, see Kubernetes.
Before you beginDirect link to Before you begin
You'll need:
- A Kubernetes 1.27+ cluster and
kubectl - Helm 3.8+ (OCI registry support)
- A container registry your cluster can pull from
- A PostgreSQL database reachable from the cluster
- A Mastra Enterprise license key — required both to pull the chart and at runtime in production
- For
ingressmode: an ingress controller, or let the chart install one - For
gatewaymode: Gateway API CRDs and a Gateway controller (for example GKE's managed Gateway)
DeployDirect link to Deploy
Configure authentication in your Mastra application before building and installing the image. The default loadBalancer mode creates externally reachable Services that may expose Server and Studio to the internet, depending on your cluster and cloud provider.
Build your Mastra project and containerize the output. Pass
--studioso the same image can serve both Server and Studio:mastra build --studioDockerfileFROM node:22-slimWORKDIR /app# Install dependencies inside the image: native modules (for example libsql)# are platform-specific, so node_modules built on your machine must not be# copied into the image (see .dockerignore below).COPY .mastra/output/package.json .mastra/output/package-lock.json* ./RUN npm install --force --prefer-offline --no-audit --no-fundCOPY .mastra/output ./EXPOSE 4111CMD ["node", "index.mjs"].dockerignore.mastra/output/node_moduleswarningDon't
COPYthe host-builtnode_modulesinto the image.mastra buildinstalls native modules for your local platform — an image built on an Apple Silicon Mac for an amd64 cluster will crash at startup with errors likeCannot find module '@libsql/linux-x64-gnu'. Installing dependencies in-image (as above) always matches the target platform.Push the image to your registry. If your machine's architecture differs from your cluster nodes (for example an arm64 Mac deploying to amd64 nodes), build with
--platform:docker buildx build --platform linux/amd64 \-t your-registry/my-mastra-app:1.0.0 --push .See Mastra server for build details.
Store your application secrets in the release namespace. The chart reads database, license, and model-provider credentials from a Secret you own:
kubectl create namespace mastrakubectl create secret generic mastra-app-env -n mastra \--from-literal=DATABASE_URL='postgresql://user:pass@host:5432/mastra' \--from-literal=MASTRA_EE_LICENSE='<your-license-key>' \--from-literal=OPENAI_API_KEY='<provider-key>'Include any other environment variables your agents need, such as model provider API keys.
Create a values file pointing the chart at your image and secret:
mastra-values.yamlglobal:cloud: generic # gke | eks | aks | generic | localmastra-server:image:repository: your-registry/my-mastra-apptag: '1.0.0'existingSecret: mastra-app-envmastra-studio:image:repository: your-registry/my-mastra-apptag: '1.0.0'existingSecret: mastra-app-envBoth components reference the same Secret because this guide runs the same application image for Server and Studio. Only use separate Secrets when the Studio deployment doesn't execute routes that need your application's provider credentials.
Set
global.cloudto your platform so the chart emits the correct LoadBalancer and Ingress annotations for that cloud.If your cluster doesn't already have access to the application image registry, create an image pull Secret:
kubectl create secret docker-registry mastra-registry -n mastra \--docker-server=your-registry \--docker-username='<username>' \--docker-password='<access-token>'Reference it from both components:
mastra-values.yamlmastra-server:imagePullSecrets:- name: mastra-registrymastra-studio:imagePullSecrets:- name: mastra-registryPublic images and registries integrated with your cluster don't need an image pull Secret.
This is only the minimal configuration. After
helm registry login, inspect the selected chart version's annotated defaults and README for every available value:CHART_VERSION=0.2.0CHART=oci://us-central1-docker.pkg.dev/mastra-cloud/mastra-helm-ee/mastra-projectshelm show values "$CHART" --version "$CHART_VERSION"helm show readme "$CHART" --version "$CHART_VERSION"Exchange your Enterprise license key for a short-lived registry access token, log in to the private chart registry, then install the chart:
CHART_VERSION=0.2.0CHART=oci://us-central1-docker.pkg.dev/mastra-cloud/mastra-helm-ee/mastra-projectsTOKEN=$(curl -s https://license.mastra.ai/v1/registry-token \-H "Authorization: Bearer $MASTRA_EE_LICENSE" | jq -r .token)printf '%s' "$TOKEN" | helm registry login us-central1-docker.pkg.dev \--username oauth2accesstoken \--password-stdinhelm install mastra "$CHART" \--version "$CHART_VERSION" \-n mastra -f mastra-values.yamlTokens expire after a short period; if an upgrade later fails with an authorization error, request a fresh token and log in again.
By default each component is exposed through a Service of type
LoadBalancer.Verify the release. Wait for the pods, then check the server's health endpoint:
kubectl -n mastra rollout status deployment -l app.kubernetes.io/instance=mastrakubectl -n mastra get svccurl http://<server-external-ip>:4111/healthA
{"success":true}response means the server is up. Open the Studio service address in a browser to reach the Mastra Studio UI.
Exposure modesDirect link to Exposure modes
The chart supports three ways to expose Server and Studio, selected once via global.exposure.mode:
| Mode | What you get | Required values |
|---|---|---|
loadBalancer (default) | One LoadBalancer Service per component with per-cloud annotations | — |
ingress | ClusterIP Services plus one Ingress per component | mastra-server.ingress.host, mastra-studio.ingress.host |
gateway | ClusterIP Services, a shared Gateway, and one HTTPRoute per component | mastra-server.httpRoute.host, mastra-studio.httpRoute.host |
Ingress with Let's Encrypt TLSDirect link to Ingress with Let's Encrypt TLS
Use ingress mode with cert-manager to serve both components over HTTPS. The chart can install ingress-nginx and cert-manager for you, or use controllers you already run:
global:
exposure:
mode: ingress
tls:
clusterIssuer: letsencrypt
ingressController:
install: true # omit if you already run an ingress controller
tls:
certManager:
install: true # omit if cert-manager is already installed
letsEncrypt:
email: ops@example.com
mastra-server:
ingress:
host: api.example.com
mastra-studio:
ingress:
host: studio.example.com
When tls.letsEncrypt.email is set, the chart renders a ClusterIssuer and annotates each Ingress so certificates are issued automatically. HTTP requests redirect to HTTPS.
On generic clusters the chart doesn't set an ingressClassName unless you configure one, so your cluster's default ingress class applies.
Gateway APIDirect link to Gateway API
Use gateway mode on clusters with Gateway API support. On GKE the chart selects the managed gke-l7-global-external-managed class automatically; on other clouds set global.gateway.className explicitly:
global:
exposure:
mode: gateway
tls:
clusterIssuer: letsencrypt
mastra-server:
httpRoute:
host: api.example.com
mastra-studio:
httpRoute:
host: studio.example.com
The chart creates a shared Gateway with HTTP and per-component HTTPS listeners. To attach to a Gateway you already run, set global.gateway.name instead.
Cloud presetsDirect link to Cloud presets
The chart ships a values preset per platform that sets global.cloud and platform-appropriate defaults:
| Platform | Preset | Notes |
|---|---|---|
| GKE | values-gke.yaml | Gateway API recommended; Workload Identity supported via serviceAccount.annotations |
| EKS | values-eks.yaml | NLB by default; ALB via ingress annotations; IRSA supported |
| AKS | values-aks.yaml | Azure LoadBalancer annotations |
| Local (kind/minikube) | values-local.yaml | Ingress on api.localhost / studio.localhost |
Object storageDirect link to Object storage
To connect an S3-compatible object store (S3, GCS with HMAC interoperability, or MinIO), set the generic object-store values on the server:
mastra-server:
externalServices:
objectStore:
endpoint: https://storage.googleapis.com
bucket: my-mastra-bucket
region: us-central1
Put S3_ACCESS_KEY_ID and S3_SECRET_ACCESS_KEY in your existing Secret rather than in values. On EKS and GKE, prefer IRSA or Workload Identity over static keys.
Scale server replicasDirect link to Scale server replicas
The chart starts one Server replica by default. A HorizontalPodAutoscaler can add pods, but it doesn't make Mastra's in-process state available across them.
Before setting replicaCount above one or enabling mastra-server.autoscaling:
- Configure a shared storage backend, such as
PostgresStore, for persisted run state. - Configure a distributed PubSub backend and shared cache. The Kubernetes guide uses
RedisStreamsPubSubandRedisServerCache, withREDIS_URLstored in the application Secret. - Use durable agents for streams, approvals, and runs that must continue when requests reach different pods.
- Start with one replica to initialize the database schema before scaling. For stricter deployments, initialize the schema in a Kubernetes Job and disable initialization in each pod.
- Review worker roles. Run only one scheduler instance when your application uses scheduled workflows.
After those requirements are in place, set resource requests and enable autoscaling:
mastra-server:
resources:
requests:
cpu: 250m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 5
targetCPUUtilizationPercentage: 80
Production checklistDirect link to Production checklist
- Reference credentials with
existingSecretinstead of plaintext values. - Set resource requests via
mastra-server.resourcesandmastra-studio.resources. - Prefer standalone cert-manager and ingress controller installs over the bundled toggles; CRD lifecycle inside an umbrella chart complicates upgrades.
- Enable multiple replicas only after configuring shared storage, distributed PubSub, shared cache, and the required process roles.
- The chart applies hardened defaults: non-root containers, read-only root filesystem, seccomp
RuntimeDefault, and no service account token automount.
Upgrade and uninstallDirect link to Upgrade and uninstall
Upgrade to a new chart version or roll out a new image tag with the same command:
CHART_VERSION=0.2.0
CHART=oci://us-central1-docker.pkg.dev/mastra-cloud/mastra-helm-ee/mastra-projects
helm upgrade mastra "$CHART" \
--version "$CHART_VERSION" \
-n mastra -f mastra-values.yaml
The chart hashes config and secret contents into pod annotations, so configuration changes trigger a rolling restart automatically.
Uninstall the release:
helm uninstall mastra -n mastra
Your database and object store are unaffected, because the chart never manages stateful services.
TroubleshootingDirect link to Troubleshooting
helm install/upgradefails with401 UnauthorizedorUNAUTHORIZED. Your registry access token is missing or expired. Request a fresh token fromhttps://license.mastra.ai/v1/registry-tokenusing your Enterprise license and runhelm registry loginagain.- Pods crash with
Cannot find module '@libsql/linux-x64-gnu'(or similar). The image containsnode_modulesbuilt for a different platform. Install dependencies inside the image and exclude host-builtnode_modulesvia.dockerignore, then rebuild with--platformmatching your nodes. - Pods crash-loop with a license error. Production mode requires a valid
MASTRA_EE_LICENSEin your secret when enterprise features are configured. - Studio shows the server API instead of the UI. The image was built without Studio assets. Rebuild with
mastra build --studio. - Ingress has no address. Confirm an ingress controller is running, or set
ingressController.install=true. - Certificates stay pending in gateway mode. Some Gateway controllers can't serve ACME challenges until a listener certificate exists. Check the chart's per-cloud presets and release notes for the bootstrap procedure on your platform.
RelatedDirect link to Related
- Kubernetes: Hand-written manifests and multi-pod scaling with shared pub/sub
- Mastra server: Build output and server behavior
- Deployment overview
- Workers: Split background processing into separate containers