# services/planka/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: planka namespace: planka labels: app: planka spec: replicas: 1 selector: matchLabels: app: planka strategy: type: RollingUpdate rollingUpdate: maxSurge: 0 maxUnavailable: 1 template: metadata: labels: app: planka annotations: vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/role: "planka" vault.hashicorp.com/agent-inject-secret-planka-env.sh: "kv/data/atlas/planka/planka-db" vault.hashicorp.com/agent-inject-template-planka-env.sh: | {{ with secret "kv/data/atlas/planka/planka-db" }} export DATABASE_URL="{{ .Data.data.DATABASE_URL }}" {{ end }} {{ with secret "kv/data/atlas/planka/planka-secrets" }} export SECRET_KEY="{{ .Data.data.SECRET_KEY }}" {{ end }} {{ with secret "kv/data/atlas/planka/planka-oidc" }} export OIDC_CLIENT_ID="{{ .Data.data.OIDC_CLIENT_ID }}" export OIDC_CLIENT_SECRET="{{ .Data.data.OIDC_CLIENT_SECRET }}" export OIDC_ENFORCED="{{ .Data.data.OIDC_ENFORCED }}" export OIDC_IGNORE_ROLES="{{ .Data.data.OIDC_IGNORE_ROLES }}" export OIDC_ISSUER="{{ .Data.data.OIDC_ISSUER }}" export OIDC_SCOPES="{{ .Data.data.OIDC_SCOPES }}" export OIDC_USE_OAUTH_CALLBACK="{{ .Data.data.OIDC_USE_OAUTH_CALLBACK }}" {{ end }} {{ with secret "kv/data/atlas/planka/planka-smtp" }} export SMTP_HOST="{{ .Data.data.SMTP_HOST }}" export SMTP_PORT="{{ .Data.data.SMTP_PORT }}" export SMTP_SECURE="{{ .Data.data.SMTP_SECURE }}" export SMTP_TLS_REJECT_UNAUTHORIZED="{{ .Data.data.SMTP_TLS_REJECT_UNAUTHORIZED }}" {{ end }} export SMTP_FROM="no-reply-planka@bstein.dev" {{ with secret "kv/data/atlas/shared/postmark-relay" }} export SMTP_USER="{{ index .Data.data "apikey" }}" export SMTP_PASSWORD="{{ index .Data.data "apikey" }}" {{ end }} spec: serviceAccountName: planka-vault nodeSelector: node-role.kubernetes.io/worker: "true" affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: hardware operator: In values: ["rpi4", "rpi5"] securityContext: runAsUser: 1000 runAsGroup: 1000 fsGroup: 1000 fsGroupChangePolicy: OnRootMismatch initContainers: - name: init-user-data image: docker.io/alpine:3.20 securityContext: runAsUser: 0 runAsGroup: 0 command: ["/bin/sh", "-c"] args: - | set -e mkdir -p /data/public/user-avatars \ /data/public/background-images \ /data/private/attachments chown -R 1000:1000 /data /tmp-data volumeMounts: - name: user-data mountPath: /data - name: app-data mountPath: /tmp-data containers: - name: planka image: ghcr.io/plankanban/planka:2.0.0-rc.4 command: - /bin/sh - -c args: - . /vault/secrets/planka-env.sh && exec node app.js --prod ports: - name: http containerPort: 1337 env: - name: BASE_URL value: https://tasks.bstein.dev - name: TRUST_PROXY value: "true" - name: OIDC_ADMIN_ROLES value: admin - name: OIDC_PROJECT_OWNER_ROLES value: "*" - name: OIDC_ROLES_ATTRIBUTE value: groups volumeMounts: - name: user-data mountPath: /app/public/user-avatars subPath: public/user-avatars - name: user-data mountPath: /app/public/background-images subPath: public/background-images - name: user-data mountPath: /app/private/attachments subPath: private/attachments - name: app-data mountPath: /app/.tmp readinessProbe: httpGet: path: / port: http initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 3 failureThreshold: 6 livenessProbe: httpGet: path: / port: http initialDelaySeconds: 30 periodSeconds: 20 timeoutSeconds: 3 failureThreshold: 6 resources: requests: cpu: 200m memory: 512Mi limits: cpu: "1" memory: 2Gi volumes: - name: user-data persistentVolumeClaim: claimName: planka-user-data - name: app-data persistentVolumeClaim: claimName: planka-app-data