Files
infra-n8n/k8s/n8n/deployment.yaml
T
Stéphane Tailland 2ea469af1e Default the optional env-secret slots
optional: true covers a Secret that does not exist; it does not cover the
variable being unset. Flux substitutes in strict mode, so the unused second
slot failed the whole build and every change to this repo since has silently
stopped reaching the cluster.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 07:57:08 +02:00

135 lines
4.6 KiB
YAML

apiVersion: apps/v1
kind: Deployment
metadata:
name: n8n
namespace: ${NAMESPACE}
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_IMAGE}
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: ${N8N_IMAGE}
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
# A private CA to trust, so a workflow can call an internal
# endpoint over TLS without anyone disabling verification to make it
# work. n8n is Node, hence this variable rather than a trust store.
# Provided by the deployment; an empty ConfigMap disables it in
# practice, since the file will not exist and Node falls back to its
# own bundle.
- name: NODE_EXTRA_CA_CERTS
value: /etc/extra-ca/ca.crt
envFrom:
- configMapRef:
name: n8n-config
# Whatever else this deployment's workflows need in their
# environment. Provided by the deployment, not by this repo: an
# empty Secret of that name is a valid answer.
#
# Two slots rather than one, because a deployment's environment
# usually comes from more than one owner — credentials the platform
# holds, and credentials a workflow's own estate holds.
#
# `optional: true` covers a Secret that does not exist; the `:=`
# defaults cover the *variable* not being set. Both are needed, and
# only the first was here: Flux substitutes in strict mode, so an
# unused slot failed the whole build rather than being skipped, and
# every later change to this repo silently stopped reaching the
# cluster.
- secretRef:
name: ${EXTRA_ENV_SECRET:=n8n-extra-env}
optional: true
- secretRef:
name: ${EXTRA_ENV_SECRET_2:=n8n-extra-env-2}
optional: true
volumeMounts:
- name: data
mountPath: /home/node/.n8n
- name: extra-ca
mountPath: /etc/extra-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: extra-ca
configMap:
name: ${EXTRA_CA_CONFIGMAP}
optional: true