Why not Helm
OpenClaw is a single container with some config files. The interesting customization is in agent content (Markdown files, skills, config overrides), not infrastructure templating. Kustomize handles overlays without the overhead of a Helm chart. Layer a Helm chart on top of these manifests if your deployment grows more complex.What you need
- A running Kubernetes cluster (AKS, EKS, GKE, k3s, kind, OpenShift, etc.)
kubectlconnected to your cluster- An API key for at least one model provider
Quick start
deploy.sh creates token auth by default. Retrieve the generated gateway token for the Control UI:
./scripts/k8s/deploy.sh --show-token prints the token after deploy.
Local testing with Kind
If you do not have a cluster, create one locally with Kind:./scripts/k8s/deploy.sh.
Step by step
1) Deploy
Option A: API key in environment (one step)--show-token to either command to print the token to stdout for local testing.
2) Access the gateway
What gets deployed
/readyz for startup and traffic readiness with a five-minute startup budget, and /healthz for liveness. Every probe asserts the JSON probe contract rather than the status code alone, because the Control UI answers unknown paths with a catch-all 200; a status-only check would pass forever against an image whose probe route does not exist yet.
/startupz is the better traffic-admission probe because it ignores channel health, so one failing channel account cannot evict an otherwise healthy Gateway from Service endpoints. It requires an image built from the release that introduced it, which is newer than the tag pinned above. After pinning such an image, switch the startup and readiness probes to /startupz and keep /readyz for monitoring that should include channel-account health.
Customization
Agent instructions
Edit theAGENTS.md in scripts/k8s/manifests/configmap.yaml and redeploy:
Gateway config
Editopenclaw.json in scripts/k8s/manifests/configmap.yaml. See Gateway configuration for the full reference.
The init container seeds openclaw.json and workspace AGENTS.md only when each file is missing from the PVC. The persisted copy is the source of truth after first boot: changes made through OpenClaw (onboard, channels add, doctor --fix, Control UI) survive pod restarts, and updating the ConfigMap does not overwrite an existing PVC copy. To intentionally reseed a file from an updated ConfigMap, delete the persisted copy and restart:
Add providers
Re-run with additional keys exported:Custom namespace
Custom image
Edit theimage field in scripts/k8s/manifests/deployment.yaml:
Expose beyond port-forward
The default manifests bind the gateway to loopback inside the pod. That works withkubectl port-forward, but not with a Kubernetes Service or Ingress path that needs to reach the pod IP directly.
To expose the gateway through an Ingress or load balancer:
- Change the gateway bind in
scripts/k8s/manifests/configmap.yamlfromloopbackto a non-loopback bind that matches your deployment model. - Keep gateway auth enabled and use a proper TLS-terminated entrypoint.
- Configure the Control UI for remote access using the supported web security model (for example HTTPS/Tailscale Serve and explicit allowed origins when needed).
Re-deploy
Teardown
openclaw namespace, this deletes the namespace and everything in it, including the PVC.
For a custom namespace, --delete removes only OpenClaw resources and preserves the namespace and unrelated workloads:
--delete-resources to request this scoped teardown explicitly in any namespace. Both scoped modes delete the OpenClaw Deployment, Service, PVC, ConfigMap, and generated Secret. Deleting the PVC removes OpenClaw’s claim and access to its persisted data; whether the backing volume and data are deleted depends on the PersistentVolume or StorageClass reclaim policy (Delete or Retain).
To delete a custom namespace and every workload in it, explicitly opt in:
Architecture notes
- The gateway binds to loopback inside the pod by default, so the included setup is for
kubectl port-forward. - No cluster-scoped resources; everything lives in a single namespace.
- Security hardening:
readOnlyRootFilesystem,drop: ALLcapabilities, non-root user (UID 1000). - The default config keeps the Control UI on the safer local-access path: loopback bind plus
kubectl port-forwardtohttp://127.0.0.1:18789. - If you move beyond localhost access, use the supported remote model: HTTPS/Tailscale plus the appropriate gateway bind and Control UI origin settings.
- Secrets are generated in a temp directory and applied directly to the cluster; no secret material is written to the repo checkout.