Skip to content

Automation Platform > Deployment & hosting

Managed: Kubernetes backend

Open in ChatGPT ↗
Ask ChatGPT about this page
Open in Claude ↗
Ask Claude about this page
Copied!

Deploy the Automation Platform managed worker into a Kubernetes cluster with the included Helm chart. Each agent task runs as a Kubernetes Job in your cluster.

Deploy the oz-agent-worker daemon into a Kubernetes cluster with the included Helm chart. Each agent task runs as a Kubernetes Job. The Automation Platform orchestrates runs; your cluster handles compute, scheduling, and policy enforcement.

  • You already operate a Kubernetes cluster and want agents to run there.
  • You need Kubernetes-native scheduling, resource management, or policy enforcement.
  • You want to use Kubernetes Secrets, ServiceAccounts, and admission policies to control task behavior.

  1. The worker connects to the Kubernetes API server (using in-cluster auth by default, or an explicit kubeconfig).
  2. On startup, the worker runs a preflight Job with the configured task pod shape.
  3. For each assigned task, the worker creates a Kubernetes Job in the configured namespace.
  4. The worker monitors the Job and Pod status.
  5. After the task completes, the worker removes successful Jobs. Failed Jobs remain available for diagnosis for 24 hours by default.

  • Enterprise plan with self-hosting enabledContact sales if self-hosting is not yet enabled for your team.
  • A Kubernetes cluster with the worker process able to reach the API server. The cluster must:
    • Allow the task namespace to create Jobs with a root init container, unless you enable native image volumes with kubernetesBackend.useImageVolumes=true.
    • Grant the worker these namespace-scoped permissions: create, get, list, watch, delete on jobs; get, list, watch on pods; get on pods/log; list on events.
  • Helm installed locally, plus kubectl authenticated against the target cluster.
  • An agent API key — Create one in the Oz web app so the worker can authenticate to the Automation Platform. Binding the key to a cloud agent doesn’t restrict which agents can run on the worker. See API Keys.

The oz-agent-worker repository includes a namespace-scoped Helm chart at charts/oz-agent-worker. This is the recommended way to deploy the worker into a cluster.

  • A long-lived Deployment running oz-agent-worker with the Kubernetes backend.
  • A namespaced ServiceAccount for the worker.
  • A namespaced Role / RoleBinding with the minimum permissions needed to manage task Jobs and Pods.
  • A ConfigMap containing the worker config YAML.
  • An optional Secret for WARP_API_KEY (or a reference to an existing Secret).

The chart does not create CRDs or cluster-scoped RBAC resources.

Terminal window
export WARP_API_KEY="your_agent_api_key"

Create the namespace if it doesn’t exist:

Terminal window
kubectl create namespace warp-oz

If you’re not using an existing Secret, create one with the API key:

Terminal window
kubectl create secret generic oz-agent-worker \
--from-literal=WARP_API_KEY="$WARP_API_KEY" \
--namespace warp-oz

Expected outcome: kubectl get secret -n warp-oz oz-agent-worker shows the Secret.

Clone the worker repo and install the chart:

Terminal window
git clone https://github.com/warpdotdev/oz-agent-worker.git
helm install oz-agent-worker ./oz-agent-worker/charts/oz-agent-worker \
--namespace warp-oz \
--set worker.workerId=oz-k8s-worker \
--set image.tag=<version>

Expected outcome: kubectl get pods -n warp-oz shows the worker Deployment pod as Running, and the worker logs include Successfully connected to server.

To scale horizontally, deploy multiple Helm releases with distinct worker IDs rather than increasing replicas on a single release.


Required:

  • worker.workerId — The worker ID (same as --worker-id).
  • image.tag — The worker image tag to deploy.

Worker configuration:

  • worker.logLevel — Log verbosity (debug, info, warn, error). Defaults to info.
  • worker.cleanup — Whether to clean up task Jobs after execution. Defaults to true.
  • worker.maxConcurrentTasks — Maximum concurrent tasks. Defaults to 0 (unlimited).
  • worker.idleOnComplete — Duration to keep the oz process alive after task completion.
  • worker.resources — Resource requests/limits for the worker Deployment. Defaults to 100m CPU and 128Mi memory.
  • worker.livenessProbe — Liveness probe for the worker Deployment. Defaults to an exec probe (kill -0 1). Override with a custom probe or set to null to disable.
  • worker.terminationGracePeriodSeconds — Grace period for worker Deployment shutdown. Defaults to 30.
  • worker.nodeSelector, worker.tolerations, worker.affinity — Scheduling constraints for the worker Deployment pod.

Kubernetes backend:

  • kubernetesBackend.namespace — Namespace for task Jobs. Defaults to the release namespace.
  • kubernetesBackend.defaultImage — Default Docker image for task pods when no Warp environment has been supplied. Leave empty (default) to fall back to ubuntu:22.04.
  • kubernetesBackend.imagePullPolicy — Image pull policy for task pods. Defaults to IfNotPresent.
  • kubernetesBackend.useImageVolumes — Use native Kubernetes image volumes instead of root init containers to materialize sidecars. Defaults to false.
  • kubernetesBackend.preflightImage — Image for the startup preflight Job. Set this if your cluster restricts allowed registries.
  • kubernetesBackend.preflightResources — CPU and memory requests and limits for preflight containers.
  • kubernetesBackend.sidecarImage — Internal-registry override for the Warp agent sidecar image.
  • kubernetesBackend.unschedulableTimeout — How long a pod may remain unschedulable before failing. Defaults to 30s.
  • kubernetesBackend.setupCommand — Shell command to run before each task.
  • kubernetesBackend.teardownCommand — Shell command to run after each task.
  • kubernetesBackend.extraLabels — Additional labels for task Jobs and Pods.
  • kubernetesBackend.extraAnnotations — Additional annotations for task Jobs and Pods.
  • kubernetesBackend.activeDeadlineSeconds — Maximum task Job lifetime. Defaults to eight hours.
  • kubernetesBackend.ttlSecondsAfterFinished — Retention period for failed Jobs and Jobs orphaned by worker disruption. Defaults to 24 hours when cleanup is enabled.
  • kubernetesBackend.workspaceSizeLimit — Size limit for workspace emptyDir volume.
  • kubernetesBackend.podTemplate — Raw PodSpec YAML for task Jobs (same as backend.kubernetes.pod_template in the config file).

API key Secret:

  • warp.apiKeySecret.create — Set to true to have the chart create a Secret from warp.apiKeySecret.value. Defaults to false (expects a pre-existing Secret).
  • warp.apiKeySecret.value — The API key value to store in the chart-managed Secret. Only used when warp.apiKeySecret.create is true.
  • warp.apiKeySecret.name — Name of the Secret containing WARP_API_KEY. Defaults to oz-agent-worker.
  • warp.apiKeySecret.key — Key within the Secret. Defaults to WARP_API_KEY.

See the self-hosted worker reference for the full config file schema.


Cluster selection follows Kubernetes client config conventions:

  • Set backend.kubernetes.kubeconfig to use an explicit kubeconfig file.
  • If kubeconfig is omitted and the worker runs inside a Kubernetes pod, the worker uses in-cluster config automatically.
  • Otherwise, the worker falls back to the default kubeconfig loading rules and uses the current context.

namespace selects the namespace inside the chosen cluster. It defaults to default when omitted.


The pod_template field accepts standard Kubernetes PodSpec YAML and is the declarative way to configure task pod scheduling, service accounts, image pull secrets, resources, and environment variables.

When using pod_template, define a container named task to customize the main task container directly. Otherwise, the worker appends its own task container to the PodSpec.

Use valueFrom.secretKeyRef to inject Kubernetes Secret values into task container environment variables:

pod_template:
serviceAccountName: agent-task-sa
imagePullSecrets:
- name: my-registry-creds
containers:
- name: task
resources:
requests:
cpu: "2"
memory: 4Gi
limits:
memory: 8Gi
env:
- name: GITHUB_TOKEN
valueFrom:
secretKeyRef:
name: my-k8s-secret
key: github-token
tolerations:
- key: "dedicated"
operator: "Equal"
value: "agents"
effect: "NoSchedule"

The worker Deployment ServiceAccount needs RBAC to manage Jobs and Pods. The task Job serviceAccountName in pod_template controls the agent process’s runtime access.

A run’s instance shape overrides the task container’s CPU and memory values in pod_template.


On startup, the worker runs a preflight Job with the configured task PodSpec. It catches insufficient RBAC, admission-policy failures, and sidecar-loading problems. If preflight fails, the worker exits before accepting tasks. A successful preflight does not validate task-specific images, Secrets, setup commands, or network access.

The preflight image defaults to busybox:1.36. For an allowlisted or private image, set kubernetesBackend.preflightImage. The imagePullSecrets in kubernetesBackend.podTemplate also apply to preflight.


Environment variables for Kubernetes tasks

Section titled “Environment variables for Kubernetes tasks”

There are two ways to pass environment variables to Kubernetes task containers:

  1. pod_template (recommended for Kubernetes-native config) — Use standard Kubernetes env syntax in the task container, including valueFrom.secretKeyRef for Kubernetes Secrets.
  2. -e / --env flags — Backend-agnostic runtime overrides that work across all managed backends.

When configuring the Kubernetes backend via YAML or Helm, declarative task-container env belongs in pod_template rather than a separate top-level list.

For an external secrets manager, inject task secrets through a CSI driver or operator. Add the provider’s volumes, volumeMounts, and annotations to pod_template.


Use kubernetesBackend.setupCommand (Helm value) or backend.kubernetes.setup_command (config file) to run a shell command before each task. Use teardownCommand / teardown_command for cleanup after the task finishes. These run inside the task Pod and are useful for workspace bootstrapping or post-run reporting.


Terminating the worker pod normally leaves active task Jobs running. Evicting a task pod interrupts the run and deletes its pod-local emptyDir workspace. Configure node lifecycle tooling to avoid voluntary disruption of active task pods.

For Karpenter, add its pod-level disruption annotation to every task Job through the Helm values:

values.yaml
kubernetesBackend:
extraAnnotations:
karpenter.sh/do-not-disrupt: "true"

The annotation blocks Karpenter consolidation. It blocks drift only when the NodePool omits terminationGracePeriod. Expiration, interruption, node repair, and manual deletion can still terminate the node. With terminationGracePeriod, Karpenter can terminate blocking pods when the period ends. Review Karpenter’s pod-level disruption controls.

A PodDisruptionBudget (PDB) constrains tools that use the Kubernetes Eviction API. It protects a group of pods, not an individual task’s process or workspace. Direct deletion, kubelet pressure eviction, node failure, and controllers that bypass the Eviction API can still terminate a task.

For other node lifecycle tools, use the equivalent protection and verify which disruption paths bypass it. A replacement pod cannot resume an interrupted run.


Task pods need capacity for their configured requests before the worker’s unschedulable timeout expires.

  • Set worker.maxConcurrentTasks to a finite value that matches cluster capacity. The default, 0, does not cap concurrency.
  • Set task CPU and memory through a runner instance shape or the task container in kubernetesBackend.podTemplate. Reserve capacity for init containers, DaemonSets, and workload spikes.
  • Set kubernetesBackend.unschedulableTimeout longer than the slowest expected node provisioning time. The default is 30s; 0s disables the check.
  • Use worker.nodeSelector, worker.tolerations, and worker.affinity for the worker Deployment. Use the equivalent fields in kubernetesBackend.podTemplate for task pods.
  • A toleration makes a pod eligible for a tainted node; it does not reserve capacity. Pair dedicated-node tolerations with matching selectors or affinity and autoscaler capacity.

The Helm chart includes built-in support for exporting OpenTelemetry metrics from the worker. Enable metrics by setting metrics.enabled=true:

Terminal window
helm install oz-agent-worker ./charts/oz-agent-worker \
--namespace warp-oz \
--set worker.workerId=oz-k8s-worker \
--set image.tag=VERSION \
--set metrics.enabled=true

With the default metrics.exporter=prometheus, the chart creates a Service with Prometheus scrape annotations and exposes port 9464. For clusters using the Prometheus Operator, set metrics.podMonitor.create=true to create a PodMonitor.

To push metrics to an OTLP collector instead, set metrics.exporter=otlp and configure the endpoint via metrics.extraEnv.

See Monitoring for the full list of Helm values, the metric catalog, and sample PromQL queries.


  • Scaling — The chart always deploys a single replica for a given worker.workerId. To run multiple workers, deploy multiple Helm releases with distinct worker IDs rather than scaling a single release horizontally.
  • Security context — The Deployment defaults to a non-root security context (runAsUser: 10001) with allowPrivilegeEscalation: false and all capabilities dropped.
  • Liveness probe — The Deployment includes a default exec liveness probe (kill -0 1). Override worker.livenessProbe for a custom probe, or set it to null to disable.
  • In-cluster auth — The chart assumes the worker runs inside the target cluster and uses in-cluster Kubernetes auth by default.