This setup runs OpenClaw Gateway on Knative with scale-to-zero and local routing that works with strict origin/auth checks.
OpneClaw-k8s-with-Knative.1.mp4
k8s\openclaw-knative.yaml: Namespace, PVC, OpenClaw config, Knative Servicedeploy-openclaw-knative.ps1: Windows deployment scriptdeploy-openclaw-knative.sh: Linux/macOS deployment script
kubectlinstalled and pointed at your cluster- Cluster running (
minikubeorkind) - Env vars set:
GEMINI_API_KEYOPENCLAW_GATEWAY_PASSWORD
cd "D:\Projects\OpneClaw k8s"
$env:GEMINI_API_KEY="your_key_here"
$env:OPENCLAW_GATEWAY_PASSWORD="strong_password_here"
.\deploy-openclaw-knative.ps1
# Optional: use local port 80 instead of 8080
.\deploy-openclaw-knative.ps1 -LocalPort 80cd "/path/to/OpneClaw k8s"
export GEMINI_API_KEY="your_key_here"
export OPENCLAW_GATEWAY_PASSWORD="strong_password_here"
chmod +x ./deploy-openclaw-knative.sh
./deploy-openclaw-knative.sh
# Optional: use local port 80 instead of 8080
./deploy-openclaw-knative.sh --local-port 80If you prefer not to use the scripts, here are the equivalent steps:
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.22.0/serving-crds.yaml
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.22.0/serving-core.yamlkubectl apply -f https://github.com/knative-extensions/net-kourier/releases/download/knative-v1.22.0/kourier.yaml
kubectl patch configmap/config-network -n knative-serving --type merge \
--patch '{"data":{"ingress-class":"kourier.ingress.networking.knative.dev"}}'kubectl wait deployment/activator -n knative-serving --for=condition=Available --timeout=300s
kubectl wait deployment/autoscaler -n knative-serving --for=condition=Available --timeout=300s
kubectl wait deployment/controller -n knative-serving --for=condition=Available --timeout=300s
kubectl wait deployment/webhook -n knative-serving --for=condition=Available --timeout=300s
kubectl wait deployment/net-kourier-controller -n knative-serving --for=condition=Available --timeout=300skubectl patch configmap/config-features -n knative-serving --type merge \
--patch '{"data":{"kubernetes.podspec-persistent-volume-claim":"enabled","kubernetes.podspec-persistent-volume-write":"enabled"}}'kubectl patch configmap/config-domain -n knative-serving --type merge \
--patch '{"data":{"localhost":"","127.0.0.1.sslip.io":null,"sslip.io":null,"nip.io":null,"127.0.0.1.nip.io":null}}'kubectl create namespace agents --dry-run=client -o yaml | kubectl apply -f -
kubectl -n agents create secret generic openclaw-secrets \
--from-literal=GEMINI_API_KEY=your_key_here \
--from-literal=OPENCLAW_GATEWAY_PASSWORD=strong_password_here \
--dry-run=client -o yaml | kubectl apply -f -kubectl apply -f ./k8s/openclaw-knative.yamlkubectl wait ksvc/openclaw -n agents --for=condition=Ready --timeout=300skubectl -n kourier-system port-forward svc/kourier 8080:80 &http://openclaw.agents.localhost:8080
- Knative Serving provides serverless behavior on Kubernetes: request-based autoscaling, cold starts, and scale-to-zero.
- Kourier is the Knative ingress layer that receives HTTP traffic for Knative Services.
- A plain Kubernetes Deployment/Service/Ingress works for exposure, but it is not the same serverless model by default (typically at least one replica stays running).
Use (default mode):
http://openclaw.agents.localhost:8080
If you run with port 80 mode, use:
http://openclaw.agents.localhost
ksvc.status.url often omits the port. The scripts normalize output to match your selected local port.
OpenClaw may write back to openclaw.json at runtime. To avoid read-only ConfigMap file lock issues (EBUSY / rename failures), this deployment copies config from ConfigMap into the PVC-backed state directory on startup and runs from /home/node/.openclaw/openclaw.json.
Startup now force-refreshes that file from ConfigMap each boot to avoid stale/corrupted PVC config blocking startup (for example: missing gateway.mode). Runtime edits to openclaw.json are writable while running, but are reset on the next restart.
gateway.bind: "loopback"gateway.auth.mode: "password"gateway.controlUi.dangerouslyDisableDeviceAuth: true(set for local Knative/Kourier usability)gateway.controlUi.allowedOriginsincludes:http://127.0.0.1http://localhosthttp://openclaw.agents.localhosthttp://127.0.0.1:8080http://localhost:8080http://openclaw.agents.localhost:8080
dangerouslyDisableDeviceAuth: true disables Control UI device-pairing protection. This is less secure and should be used only for local development.
Safer alternatives:
- Keep device auth enabled and approve pairing requests:
$pod = kubectl -n agents get pods -l serving.knative.dev/service=openclaw -o jsonpath='{.items[0].metadata.name}'
kubectl -n agents exec $pod -- openclaw devices list
kubectl -n agents exec $pod -- openclaw devices approve <requestId>- Use Tailscale Serve identity-aware flow (
gateway.auth.allowTailscale: true) instead of disabling device auth.
Configured on the Knative revision:
autoscaling.knative.dev/min-scale: "0"autoscaling.knative.dev/max-scale: "1"autoscaling.knative.dev/scale-to-zero-pod-retention-period: "5m"
Use this for a more secure exposure path than local plain HTTP.
- Keep local Kourier forwarding:
kubectl -n kourier-system port-forward svc/kourier 8080:80If you selected local port 80 in deploy scripts, use:
kubectl -n kourier-system port-forward svc/kourier 80:80- Publish HTTPS with Tailscale:
tailscale serve --https=443 http://127.0.0.1:8080
tailscale serve statusIf you selected local port 80 in deploy scripts, use:
tailscale serve --https=443 http://127.0.0.1:80
tailscale serve status-
Add the exact
https://<host>.<tailnet>.ts.netorigin togateway.controlUi.allowedOriginsink8s\openclaw-knative.yaml, then redeploy. -
For tighter auth, enable Tailscale identity-based gateway auth in
openclaw.json:
{
gateway: {
auth: {
mode: "password",
allowTailscale: true
}
}
}Tailscale requires host-level setup outside Kubernetes manifests:
tailscaleddaemon installed/running on the machine- machine logged into your tailnet (
tailscale up) with your org policy - permission to bind Serve ports and publish HTTPS in your tailnet
Because those are environment/account-specific, the deploy scripts cannot safely/portably auto-complete them for every machine.
1. Starting Deployment
The deployment script installs Knative Serving, Kourier ingress, configures domain settings, and deploys OpenClaw.
2. Deployment Success
Once complete, the script outputs the access URL. The service is now ready at http://openclaw.agents.localhost:8080.
3. Kourier Port Forward
The script automatically starts port-forwarding from localhost:8080 to the Kourier service, enabling local access to the Knative service.
4. Control UI Login
Access the OpenClaw Control UI at the provided URL. Enter the gateway password you set in OPENCLAW_GATEWAY_PASSWORD.
5. Control UI Dashboard
Once logged in, you can interact with OpenClaw agents, manage sessions, and configure channels.
6. Pod Terminating (Scale to Zero)
After 5 minutes of inactivity, Knative automatically scales the pod to zero. The pod enters Terminating state and resources are freed.
7. Pod Creation (Scale from Zero)
When a new request arrives, Knative detects traffic and creates a new pod. The pod goes through initialization.
8. Pod Running (Ready to Serve)
The pod becomes ready and starts serving requests. Your session state persists across scale cycles thanks to the PVC.
- ✅ Serverless on Kubernetes: True scale-to-zero with Knative
- ✅ Fast Cold Start: Pod ready in ~30-60 seconds
- ✅ State Persistence: PVC preserves configs and data across restarts
- ✅ Automatic Scaling: Scales to 0 after 5 minutes idle, wakes on demand
- ✅ Local Development: Works with localhost routing and strict CORS
kubectl -n agents get ksvc openclaw
kubectl -n agents get pods
kubectl -n agents logs -l serving.knative.dev/service=openclaw --tail=200
kubectl -n agents get secret openclaw-secrets -o yaml







