# services/openclaw/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: openclaw
  namespace: openclaw
  labels:
    app: openclaw
spec:
  replicas: 1
  revisionHistoryLimit: 2
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: openclaw
  template:
    metadata:
      labels:
        app: openclaw
      annotations:
        ai.bstein.dev/model: qwen2.5:7b-instruct-q4_0
        ai.bstein.dev/instructions: kubectl-field-selectors
        ai.bstein.dev/role: testing-triage
        ai.bstein.dev/placement: arm64 gateway lane (rpi5 preferred)
        ai.bstein.dev/config-rev: "20260521-oauth-device-bypass"
    spec:
      serviceAccountName: openclaw-triage
      automountServiceAccountToken: true
      securityContext:
        fsGroup: 1000
        seccompProfile:
          type: RuntimeDefault
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: kubernetes.io/arch
                    operator: In
                    values:
                      - arm64
                  - key: node-role.kubernetes.io/worker
                    operator: In
                    values:
                      - "true"
                  - key: kubernetes.io/hostname
                    operator: NotIn
                    values:
                      - titan-13
                      - titan-15
                      - titan-17
                      - titan-19
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              preference:
                matchExpressions:
                  - key: atlas.bstein.dev/spillover
                    operator: DoesNotExist
            - weight: 90
              preference:
                matchExpressions:
                  - key: hardware
                    operator: In
                    values:
                      - rpi5
            - weight: 50
              preference:
                matchExpressions:
                  - key: hardware
                    operator: In
                    values:
                      - rpi4
      initContainers:
        - name: init-permissions
          image: busybox:1.37
          imagePullPolicy: IfNotPresent
          command:
            - sh
            - -c
            - |
              set -e
              chown -R 1000:1000 /home/node/.openclaw /home/node/.local/bin /tmp
          securityContext:
            runAsUser: 0
            runAsGroup: 0
          volumeMounts:
            - name: home
              mountPath: /home/node/.openclaw
            - name: tmp
              mountPath: /tmp
            - name: tools
              mountPath: /home/node/.local/bin
          resources:
            requests:
              cpu: 25m
              memory: 32Mi
            limits:
              cpu: 100m
              memory: 64Mi
        - name: init-config
          image: busybox:1.37
          imagePullPolicy: IfNotPresent
          command:
            - sh
            - -c
            - |
              set -e
              cp /config/openclaw.json /home/node/.openclaw/openclaw.json
              mkdir -p /home/node/.openclaw/workspace
              cp /config/AGENTS.md /home/node/.openclaw/workspace/AGENTS.md
          securityContext:
            runAsUser: 1000
            runAsGroup: 1000
          volumeMounts:
            - name: home
              mountPath: /home/node/.openclaw
            - name: config
              mountPath: /config
          resources:
            requests:
              cpu: 25m
              memory: 32Mi
            limits:
              cpu: 100m
              memory: 64Mi
        - name: install-kubectl
          image: bitnami/kubectl@sha256:554ab88b1858e8424c55de37ad417b16f2a0e65d1607aa0f3fe3ce9b9f10b131
          imagePullPolicy: IfNotPresent
          command:
            - /bin/sh
            - -c
            - |
              set -e
              cp "$(command -v kubectl)" /tools/kubectl
              chmod 0755 /tools/kubectl
          volumeMounts:
            - name: tools
              mountPath: /tools
          resources:
            requests:
              cpu: 25m
              memory: 32Mi
            limits:
              cpu: 100m
              memory: 64Mi
      containers:
        - name: gateway
          image: ghcr.io/openclaw/openclaw:slim@sha256:ca134367c291b982ec301038af4602f397c7f90cc6c6d263c12a78a5fe1bde57
          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: PATH
              value: /home/node/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
            - name: VICTORIA_METRICS_URL
              value: http://victoria-metrics-single-server.monitoring.svc.cluster.local:8428
            - name: ARIADNE_BASE_URL
              value: http://ariadne.maintenance.svc.cluster.local
            - name: JENKINS_BASE_URL
              value: http://jenkins.jenkins.svc.cluster.local:8080
            - name: GITEA_BASE_URL
              value: https://scm.bstein.dev
            - name: GRAFANA_BASE_URL
              value: https://metrics.bstein.dev
          volumeMounts:
            - name: home
              mountPath: /home/node/.openclaw
            - name: tmp
              mountPath: /tmp
            - name: tools
              mountPath: /home/node/.local/bin
          readinessProbe:
            exec:
              command:
                - node
                - -e
                - "require('http').get('http://127.0.0.1:18789/readyz', r => process.exit(r.statusCode < 400 ? 0 : 1)).on('error', () => process.exit(1))"
            initialDelaySeconds: 20
            periodSeconds: 10
            timeoutSeconds: 5
          livenessProbe:
            exec:
              command:
                - node
                - -e
                - "require('http').get('http://127.0.0.1:18789/healthz', r => process.exit(r.statusCode < 400 ? 0 : 1)).on('error', () => process.exit(1))"
            initialDelaySeconds: 60
            periodSeconds: 30
            timeoutSeconds: 10
          securityContext:
            runAsNonRoot: true
            runAsUser: 1000
            runAsGroup: 1000
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop:
                - ALL
          resources:
            requests:
              cpu: 250m
              memory: 512Mi
            limits:
              cpu: "1"
              memory: 2Gi
      volumes:
        - name: home
          persistentVolumeClaim:
            claimName: openclaw-home
        - name: config
          configMap:
            name: openclaw-config
        - name: tmp
          emptyDir: {}
        - name: tools
          emptyDir: {}
