apiVersion: apps/v1 kind: Deployment metadata: name: openclaw labels: app: openclaw spec: replicas: 1 selector: matchLabels: app: openclaw strategy: type: Recreate template: metadata: labels: app: openclaw spec: automountServiceAccountToken: false securityContext: fsGroup: 1000 seccompProfile: type: RuntimeDefault initContainers: - name: init-config image: busybox:1.37 imagePullPolicy: IfNotPresent command: - sh - -c - | # Seed-if-missing: the PVC copy owns config after first boot so edits made # through OpenClaw (onboard, channels add, doctor --fix, Control UI) survive # restarts. ConfigMap edits need an explicit reseed (see docs/install/kubernetes.md). [ -f /home/node/.openclaw/openclaw.json ] || cp /config/openclaw.json /home/node/.openclaw/openclaw.json mkdir -p /home/node/.openclaw/workspace [ -f /home/node/.openclaw/workspace/AGENTS.md ] || cp /config/AGENTS.md /home/node/.openclaw/workspace/AGENTS.md securityContext: runAsUser: 1000 runAsGroup: 1000 resources: requests: memory: 32Mi cpu: 50m limits: memory: 64Mi cpu: 100m volumeMounts: - name: openclaw-home mountPath: /home/node/.openclaw - name: config mountPath: /config containers: - name: gateway # Bump this immutable versioned tag when upgrading OpenClaw. image: ghcr.io/openclaw/openclaw:2026.7.1-2-slim imagePullPolicy: IfNotPresent command: - node - /app/dist/index.js - gateway - run ports: - name: gateway containerPort: 18789 protocol: TCP env: - name: HOME value: /home/node - name: OPENCLAW_CONFIG_DIR value: /home/node/.openclaw - name: NODE_ENV value: production - name: OPENCLAW_GATEWAY_TOKEN valueFrom: secretKeyRef: name: openclaw-secrets key: OPENCLAW_GATEWAY_TOKEN - name: ANTHROPIC_API_KEY valueFrom: secretKeyRef: name: openclaw-secrets key: ANTHROPIC_API_KEY optional: true - name: OPENAI_API_KEY valueFrom: secretKeyRef: name: openclaw-secrets key: OPENAI_API_KEY optional: true - name: GEMINI_API_KEY valueFrom: secretKeyRef: name: openclaw-secrets key: GEMINI_API_KEY optional: true - name: OPENROUTER_API_KEY valueFrom: secretKeyRef: name: openclaw-secrets key: OPENROUTER_API_KEY optional: true resources: requests: memory: 512Mi cpu: 250m limits: memory: 2Gi cpu: "1" # Probes assert the JSON probe contract, not just the status code. The # Control UI serves a catch-all 200 for unknown paths, so a status-only # check would pass forever against an image whose probe route does not # exist yet. Failing closed makes that mismatch loud instead of silent. # /startupz (startup admission, ignores channel health) needs an image # built from the release that added it; switch the startup and # readiness probes to it after pinning such an image. startupProbe: exec: command: - node - -e - "require('http').get('http://127.0.0.1:18789/readyz', r => { let b = ''; r.on('data', d => b += d); r.on('end', () => { try { process.exit(r.statusCode < 400 && JSON.parse(b).ready === true ? 0 : 1); } catch { process.exit(1); } }); }).on('error', () => process.exit(1))" periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 30 livenessProbe: exec: command: - node - -e - "require('http').get('http://127.0.0.1:18789/healthz', r => { let b = ''; r.on('data', d => b += d); r.on('end', () => { try { process.exit(r.statusCode < 400 && JSON.parse(b).ok === true ? 0 : 1); } catch { process.exit(1); } }); }).on('error', () => process.exit(1))" initialDelaySeconds: 60 periodSeconds: 30 timeoutSeconds: 10 readinessProbe: exec: command: - node - -e - "require('http').get('http://127.0.0.1:18789/readyz', r => { let b = ''; r.on('data', d => b += d); r.on('end', () => { try { process.exit(r.statusCode < 400 && JSON.parse(b).ready === true ? 0 : 1); } catch { process.exit(1); } }); }).on('error', () => process.exit(1))" initialDelaySeconds: 15 periodSeconds: 10 timeoutSeconds: 5 volumeMounts: - name: openclaw-home mountPath: /home/node/.openclaw - name: tmp-volume mountPath: /tmp securityContext: runAsNonRoot: true runAsUser: 1000 runAsGroup: 1000 allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL volumes: - name: openclaw-home persistentVolumeClaim: claimName: openclaw-home-pvc - name: config configMap: name: openclaw-config - name: tmp-volume emptyDir: {}