Skip to main content

K3s provisioner backend

Run Nora agents on a lightweight self-hosted K3s cluster using Nora’s Kubernetes adapter.
K3s is the recommended self-hosted Kubernetes path when you want cluster scheduling without using a managed cloud provider. Nora still uses the same Kubernetes adapter underneath; the Admin cluster registry stores the provider label so operators can distinguish lightweight self-hosted clusters from cloud deployments in the deploy flow.

When to use K3s

Use K3s when Nora runs on your own server, homelab, edge node, or private VM fleet and you want agent workloads scheduled as Kubernetes Deployments instead of local Docker containers. Use AKS, GKE, EKS, or another cloud k8s target when you want managed control-plane operations, cloud load balancers, cloud identity, and multi-zone cluster management.
K3sCloud Kubernetes (k8s)
Provider labelK3sAKS, GKE, EKS, or Kubernetes
Operator modelYou own the cluster host and upgradesCloud provider manages the Kubernetes control plane
Typical exposurenode-port from Compose to the K3s nodeload-balancer with provider-managed Services
Best forSelf-hosted, edge, lab, and private infraProduction cloud clusters and managed networking
Adapter behaviorSame generic Kubernetes adapterSame generic Kubernetes adapter

Step-by-step setup

1. Install K3s on the cluster host

curl -sfL https://get.k3s.io | sh -
sudo k3s kubectl get nodes

2. Mint a kubeconfig Nora can mount

mkdir -p .secrets
sudo install -m 600 -o "$USER" -g "$USER" \
  /etc/rancher/k3s/k3s.yaml \
  ./.secrets/k3s-kubeconfig
If Nora runs in Docker Compose, the kubeconfig server URL must be reachable from the backend-api and worker-provisioner containers. Replace the default loopback address with the K3s node address:
K3S_NODE_IP=<k3s-node-ip>
sed -i "s/127.0.0.1/${K3S_NODE_IP}/g" ./.secrets/k3s-kubeconfig

3. Verify access from the host

kubectl --kubeconfig ./.secrets/k3s-kubeconfig get nodes

4. Configure Nora

Set the generic Kubernetes mount variable in .env:
mkdir -p ./.secrets/kubeconfigs
cp ./.secrets/k3s-kubeconfig ./.secrets/kubeconfigs/k3s-local
NORA_KUBECONFIGS_DIR=./.secrets/kubeconfigs
Use host.docker.internal when K3s and Nora’s Docker Compose stack run on the same host. Use the K3s node IP or DNS name when Nora runs on a different host. For multiple K3s or mixed Kubernetes clusters, put the kubeconfig under NORA_KUBECONFIGS_DIR instead and use an Admin path such as /kubeconfigs/k3s-local.

5. Start the stack

docker compose -f docker-compose.yml -f docker-compose.kubernetes.yml up -d --build

6. Register this cluster in Admin

Open Admin -> Kubernetes, click Add cluster, and use these values:
FieldValue
Cluster idk3s-local
LabelK3s Local
ProviderK3s
Actual cluster nameYour K3s server or cluster name
Credential modeMounted kubeconfig path
Kubeconfig path/kubeconfigs/k3s-local
Fallback namespaceopenclaw-agents
OpenClaw namespaceopenclaw-agents
Hermes namespaceopenclaw-agents
Exposure modeNodePort
Runtime hosthost.docker.internal when Nora and K3s run on the same host; otherwise the K3s node IP or DNS name
Do not put ./.secrets/k3s-kubeconfig in the Admin Kubeconfig path when Nora runs in Docker Compose. That is the host-side path; the containers see files from NORA_KUBECONFIGS_DIR under /kubeconfigs.

7. Deploy a test agent

Open the dashboard at http://127.0.0.1:8080, sign in, and create an agent with the K3s cluster label you registered.
Deploy wizard backend
picker
Deploy wizard Kubernetes
selected
Agent detail K8s

Exposure modes

For the standard Compose-hosted control plane, set the Admin exposure mode to node-port. Nora stores the allocated NodePorts for each agent and reaches them through the Admin Runtime host value. Use cluster-ip only when the Nora control plane itself runs inside the same K3s cluster or otherwise has an in-cluster route to Service DNS names. Use load-balancer only when your K3s installation has a load balancer implementation that can assign a reachable address per agent Service.

Verification

Check the Kubernetes workload:
kubectl --kubeconfig ./.secrets/k3s-kubeconfig -n openclaw-agents get deploy,svc,pods
Check Nora worker logs:
docker compose -f docker-compose.yml -f docker-compose.kubernetes.yml logs worker-provisioner
If an agent reaches running but the gateway is unreachable, confirm that the Admin Runtime host points to an address the Compose containers can reach and that your firewall allows the allocated NodePort range.
Agent logs K8s

Automated smoke

Once your K3s cluster and kubeconfig are in place, run the shared lifecycle smoke:
cd e2e
KUBECONFIG_PATH=$PWD/../.secrets/k3s-kubeconfig npm run smoke:k8s-k3s
The script brings up the Compose stack with docker-compose.kubernetes.yml, registers the K3s cluster in Admin, deploys an agent, verifies Kubernetes objects, and exercises stop/start/restart. It exits non-zero on any failure. Set KEEP_ENV=true to leave the stack running for inspection.

Promote to production

Once the smoke is green and you trust the cluster, switch from the dev-mode stack to the prod-mode stack. The only changes vs the testing setup are nginx (public config + TLS) and Compose mode (infra/docker-compose.public-tls.yml for prod Dockerfiles, TLS mounts, public ports, and restart policies).

1. Switch nginx to public + TLS

In .env:
NGINX_CONFIG_FILE=nginx.public.conf
NGINX_HTTP_PORT=80
Provision Let’s Encrypt certs once (the script wires /etc/letsencrypt into the nginx container):
sudo ./infra/setup-tls.sh <your-domain>

2. Stop the smoke-mode stack

docker compose -f docker-compose.yml -f docker-compose.kubernetes.yml down

3. Start the prod-mode stack

Use the tracked TLS compose layer, then layer the K3s backend overlay on top:
docker compose \
  -f docker-compose.yml \
  -f infra/docker-compose.public-tls.yml \
  -f docker-compose.kubernetes.yml \
  up -d --build

4. Confirm

docker compose ps
curl -fsS https://<your-domain>/api/health
docker compose logs worker-provisioner | tail -50
Deploy a real agent from the UI and confirm it reaches running and the NodePort gateway is reachable from your network.

See also

  • Kind — local Kubernetes for development
  • AKS — Azure Kubernetes Service
  • GKE — Google Kubernetes Engine
  • EKS — Amazon EKS