# services/jenkins/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: jenkins namespace: jenkins labels: app: jenkins spec: replicas: 1 selector: matchLabels: app: jenkins strategy: type: Recreate template: metadata: labels: app: jenkins annotations: vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/role: "jenkins" vault.hashicorp.com/agent-inject-secret-jenkins-env: "kv/data/atlas/jenkins/jenkins-oidc" vault.hashicorp.com/agent-inject-template-jenkins-env: | {{ with secret "kv/data/atlas/jenkins/jenkins-oidc" }} OIDC_CLIENT_ID={{ .Data.data.clientId }} OIDC_CLIENT_SECRET={{ .Data.data.clientSecret }} OIDC_AUTH_URL={{ .Data.data.authorizationUrl }} OIDC_TOKEN_URL={{ .Data.data.tokenUrl }} OIDC_USERINFO_URL={{ .Data.data.userInfoUrl }} OIDC_LOGOUT_URL={{ .Data.data.logoutUrl }} {{ end }} {{ with secret "kv/data/atlas/jenkins/harbor-robot-creds" }} HARBOR_ROBOT_USERNAME={{ .Data.data.username }} HARBOR_ROBOT_PASSWORD={{ .Data.data.password }} {{ end }} {{ with secret "kv/data/atlas/jenkins/gitea-pat" }} GITEA_PAT_USERNAME={{ .Data.data.username }} GITEA_PAT_TOKEN={{ .Data.data.token }} {{ end }} {{ with secret "kv/data/atlas/jenkins/webhook-tokens" }} TITAN_IAC_WEBHOOK_TOKEN={{ .Data.data.titan_iac_quality_gate }} {{ end }} bstein.dev/restarted-at: "2026-01-20T13:10:00Z" spec: serviceAccountName: jenkins nodeSelector: kubernetes.io/arch: arm64 node-role.kubernetes.io/worker: "true" affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - key: hardware operator: In values: ["rpi5"] - weight: 50 preference: matchExpressions: - key: hardware operator: In values: ["rpi4"] hostAliases: - ip: 38.28.125.112 hostnames: - sso.bstein.dev securityContext: fsGroup: 1000 initContainers: - name: install-plugins image: jenkins/jenkins:2.528.3-jdk21 imagePullPolicy: IfNotPresent command: - sh - -c - | set -euo pipefail jenkins-plugin-cli --plugin-file /plugins/plugins.txt volumeMounts: - name: plugins mountPath: /plugins/plugins.txt subPath: plugins.txt - name: plugin-dir mountPath: /usr/share/jenkins/ref/plugins containers: - name: jenkins image: jenkins/jenkins:2.528.3-jdk21 imagePullPolicy: IfNotPresent command: - /bin/sh - -c - | set -e exec env $(cat /vault/secrets/jenkins-env) /usr/bin/tini -- /usr/local/bin/jenkins.sh ports: - name: http containerPort: 8080 - name: agent-listener containerPort: 50000 env: - name: JAVA_OPTS value: "-Xms512m -Xmx2048m" - name: JENKINS_OPTS value: "--webroot=/var/jenkins_cache/war" - name: JENKINS_SLAVE_AGENT_PORT value: "50000" - name: CASC_JENKINS_CONFIG value: /config/jcasc - name: ENABLE_OIDC value: "true" - name: OIDC_ISSUER value: "https://sso.bstein.dev/realms/atlas" resources: requests: cpu: 750m memory: 1536Mi limits: cpu: 1500m memory: 3Gi livenessProbe: httpGet: path: /login port: http initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 5 readinessProbe: httpGet: path: /login port: http initialDelaySeconds: 20 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 startupProbe: httpGet: path: /login port: http initialDelaySeconds: 30 periodSeconds: 10 failureThreshold: 20 volumeMounts: - name: jenkins-home mountPath: /var/jenkins_home - name: jenkins-cache mountPath: /var/jenkins_cache - name: jcasc mountPath: /config/jcasc - name: init-scripts mountPath: /usr/share/jenkins/ref/init.groovy.d - name: plugin-dir mountPath: /usr/share/jenkins/ref/plugins - name: tmp mountPath: /tmp volumes: - name: jenkins-home persistentVolumeClaim: claimName: jenkins - name: jenkins-cache emptyDir: {} - name: plugin-dir emptyDir: {} - name: plugins configMap: name: jenkins-plugins - name: jcasc configMap: name: jenkins-jcasc - name: init-scripts configMap: name: jenkins-init-scripts - name: tmp emptyDir: {}