> ## Documentation Index
> Fetch the complete documentation index at: https://docs.2501.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Kubernetes

> Run the 2501 engine on any Kubernetes cluster with the Kubernetes pod executor backend

2501 runs on any Kubernetes distribution — vanilla k8s, OpenShift, k3s, EKS, GKE, AKS, Minikube. The engine runs each tool executor in its own isolated sandbox. On Docker and Docker Swarm that sandbox is a container; on Kubernetes the engine creates a short-lived Kubernetes Pod instead — a pod is created when work starts, the engine attaches to it over stdin and stdout, and the pod is deleted when the task ends.

This page covers how to generate the deployment manifests with the `2501` CLI, how the Kubernetes backend selects itself, and the RBAC the engine needs.

<Note>
  Docker Swarm remains the default deployment target, with a fully managed `deploy` lifecycle. The Kubernetes target is generate-only: the CLI renders a complete manifest tree and you apply it with `kubectl` (or your distribution's equivalent CLI). This keeps you in control of how changes reach the cluster, including through GitOps tooling.
</Note>

## Generating the manifests

Run the deploy command with the Kubernetes target:

```bash theme={null}
2501 infra deploy --target kubernetes
```

The CLI prompts for a stack name, namespace, image tag, Command Center public URL, and your Elasticsearch and AWS (registry pull) values, then renders a ready-to-apply manifest tree under `<configDir>/kubernetes/`:

```
kubernetes/
├── namespace.yml
├── configmap/        # engine, command-center
├── secret/           # engine, command-center, postgres, ecr-pull-secret, license-public-key
├── deployment/       # engine, command-center, postgres
├── service/          # engine, command-center, postgres
├── pvc/              # postgres
└── rbac/             # executor ServiceAccount, Role, RoleBinding
```

For non-interactive use, pass the values as flags:

```bash theme={null}
2501 infra deploy --target kubernetes --image-tag <tag> \
  --stack-name ai-2501 --namespace ai-2501 --yes
```

`--image-tag` (alias `--tag`) is required with `--yes`. `--stack-name` and `--namespace` both default to `ai-2501`, and both are validated as DNS-1123 labels.

### Flags

```text 2501 infra deploy -h theme={null}
Usage: 2501 infra deploy [options]

Deploy 2501 stack

Options:
  --remove                       Remove the deployed stack
  --stop                         Stop the deployed stack
  -t, --tag <tag>                Image tag to deploy (overrides config, e.g. for
                                 testing a build)
  --target <target>              Deployment target: swarm or kubernetes
                                 (overrides config)
  --stack-name <name>            Kubernetes: stack name for Service names/labels
                                 (default: ai-2501)
  --namespace <name>             Kubernetes: target namespace (default: ai-2501)
  --image-tag <tag>              Kubernetes: image tag to deploy (required for
                                 --target kubernetes; alias of --tag)
  -r, --restart [service]        Restart with env reload (optional: engine,
                                 command-center)
  --history                      Show deployment history
  -d, --dir <path>               Config directory (default: /etc/2501)
  -y, --yes                      Non-interactive mode - skip prompts and
                                 seeding, fail on errors
  --cleanup-resources            Clean up unused images and resources before
                                 deployment
  --cleanup-docker-resources     Alias for --cleanup-resources (deprecated)
  --disable-services [services]  Disable services (comma-separated: engine,
                                 command-center, or omit to disable all)
  -h, --help                     display help for command
```

`--remove`, `--stop`, `--restart`, `--history`, `--cleanup-resources`, and `--disable-services` drive the Swarm lifecycle; on the Kubernetes target the relevant options are `--target`, `--stack-name`, `--namespace`, `--image-tag`, `-d, --dir`, and `-y, --yes`.

## Filling in secrets

Secret manifests use `stringData`, so every value is plain text and the cluster encodes it server-side on apply. Before applying, fill in any blank values directly in the YAML:

* `AWS_*` and `ELASTICSEARCH_*` in `secret/engine.yml` and `secret/command-center.yml`
* `BASE_URL` in `configmap/command-center.yml` (your real ingress hostname)
* The license public key in `secret/license-public-key.yml`, unless a PEM file already exists at `<configDir>/license.public.pem`

Generation is safe to re-run. Auto-generated secrets (the PostgreSQL password, credential encryption key, engine API key, auth secret, and log encryption key) are created once and read back from the existing YAML on subsequent runs, and values you typed into the files by hand are preserved. An empty prompt answer never overwrites a previously supplied value.

## Applying

```bash theme={null}
kubectl apply -f <configDir>/kubernetes/ -R
```

(Substitute your distribution's CLI if you don't use `kubectl` — `oc`, `k3s kubectl`, etc.)

<Warning>
  The registry pull secret is a short-lived ECR token that expires after roughly 12 hours. Recreate it before each apply:

  ```bash theme={null}
  kubectl create secret docker-registry ecr-pull-secret \
    --docker-server=<registry> --docker-username=AWS \
    --docker-password=$(aws ecr get-login-password --region <region>) \
    -n <namespace> --dry-run=client -o yaml | kubectl apply -f -
  ```
</Warning>

## Executor backend selection

The engine picks an executor backend automatically:

* When it detects that it is running **inside a Kubernetes cluster**, it uses the Kubernetes pod backend.
* Otherwise it falls back to **Docker / Swarm**.

You can force a backend with the `EXECUTOR_MCP_BACKEND` environment variable, set to `kube` or `docker`.

<Note>
  The engine talks to the in-cluster Kubernetes API directly using the pod's auto-mounted service-account token. It does not shell out to `kubectl`, and you do not supply a kubeconfig.
</Note>

## Required access (RBAC)

The generated `rbac/executor.yml` provisions a dedicated `engine-executor` ServiceAccount with a namespace-scoped Role and RoleBinding granting:

| Resource      | Verbs                                      |
| ------------- | ------------------------------------------ |
| `pods`        | `create`, `get`, `list`, `watch`, `delete` |
| `pods/attach` | `create`, `get`                            |
| `pods/status` | `get`                                      |
| `pods/log`    | `get`                                      |

Executor pods spawn in the same namespace as the engine. To use a separate sandbox namespace, change the RoleBinding's namespace and override `EXECUTOR_MCP_K8S_NAMESPACE` on the engine pod, which is otherwise set to the engine's own namespace via the Downward API.

<Warning>
  gMSA-bound Windows targets are not supported on the Kubernetes executor backend. They depend on a Docker-volume Kerberos sidecar that this backend does not provide. Use the Docker or Swarm backend for those targets. See [Docker Swarm](/0.7/deployment/docker-swarm).
</Warning>

<Note>
  For the full set of engine and Command Center environment variables (database, security keys, LLM providers, optional integrations), see [Configuration](/0.7/deployment/configuration).
</Note>
