apiVersion: apps/v1 kind: Deployment metadata: name: n8n namespace: n8n spec: # One replica. n8n's queue mode splits the editor from workers and needs Redis # to do it; without that, a second replica is two schedulers racing over the # same cron triggers. See the README before scaling this. replicas: 1 strategy: # The volume is ReadWriteOnce, so a rolling update deadlocks: the new pod # waits for a volume the old pod has not released. type: Recreate selector: matchLabels: app: n8n template: metadata: labels: app: n8n spec: securityContext: runAsNonRoot: true runAsUser: 1000 fsGroup: 1000 initContainers: - name: wait-for-postgres image: postgres:16.10-alpine command: - sh - -c - | until pg_isready -h n8n-postgres -U n8n -d n8n; do echo "waiting for postgres..."; sleep 3 done containers: - name: n8n image: docker.n8n.io/n8nio/n8n:2.37.4 ports: - name: http containerPort: 5678 env: - name: DB_TYPE value: postgresdb - name: DB_POSTGRESDB_HOST value: n8n-postgres - name: DB_POSTGRESDB_PORT value: "5432" - name: DB_POSTGRESDB_DATABASE value: n8n - name: DB_POSTGRESDB_USER value: n8n - name: DB_POSTGRESDB_PASSWORD valueFrom: secretKeyRef: name: n8n-secrets key: DB_PASSWORD # Encrypts stored credentials. Held in Vault, never regenerated — # a new key does not re-encrypt, it orphans everything already # stored. - name: N8N_ENCRYPTION_KEY valueFrom: secretKeyRef: name: n8n-secrets key: N8N_ENCRYPTION_KEY # The lab CA, so a workflow can call keycloak.sttlab.local and the # domain gateways without disabling verification. n8n is Node, hence # this variable rather than a trust store. - name: NODE_EXTRA_CA_CERTS value: /etc/sttlab-ca/ca.crt envFrom: - configMapRef: name: n8n-config # The agents' Keycloak client secrets, reached from a workflow as # `$env.APPROVISIONNEMENT_CLIENT_SECRET`. A workflow acts as its # agent, so it is the workflow that must be able to exchange tokens. - secretRef: name: n8n-agent-secrets volumeMounts: - name: data mountPath: /home/node/.n8n - name: sttlab-ca mountPath: /etc/sttlab-ca readOnly: true readinessProbe: httpGet: path: /healthz port: http initialDelaySeconds: 15 periodSeconds: 10 livenessProbe: httpGet: path: /healthz port: http initialDelaySeconds: 60 periodSeconds: 20 resources: requests: cpu: 100m memory: 512Mi limits: cpu: "2" memory: 2Gi securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] volumes: - name: data persistentVolumeClaim: claimName: n8n-data - name: sttlab-ca configMap: name: sttlab-ca