feat: deploy n8n on the cluster

The runtime for the estate's autonomous agents. Postgres as a StatefulSet with
its own claim, n8n behind a cert-manager certificate and the nginx ingress, and
both secrets in Vault behind an AppRole scoped to n8n/* alone.

The certificate is issued into the cluster rather than copied into the repo, so
the private key never reaches git. The CA certificate is committed and mounted
as NODE_EXTRA_CA_CERTS, so a workflow can reach keycloak and the domain
gateways without anyone disabling verification to make it work.

One replica and strategy: Recreate, both deliberate — n8n needs Redis and queue
mode before a second scheduler is safe, and a ReadWriteOnce volume deadlocks a
rolling update.

N8N_ENCRYPTION_KEY is generated once and kept in Vault. Regenerating it does not
re-encrypt the stored credentials, it orphans them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Stéphane Tailland
2026-08-29 12:34:40 +02:00
commit dc13738e86
17 changed files with 552 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
# cert-manager issues this into the `n8n-tls` Secret, from the same
# ClusterIssuer as the rest of the estate.
#
# Deliberately not a copy of a certificate committed to git: the private key
# stays in the cluster, is rotated by cert-manager, and never reaches a repo.
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: n8n-tls
namespace: n8n
spec:
secretName: n8n-tls
dnsNames:
- n8n.sttlab.local
issuerRef:
name: sttlab-local-ca
kind: ClusterIssuer
+31
View File
@@ -0,0 +1,31 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: n8n
namespace: n8n
annotations:
# The editor pushes execution progress over a long-lived connection, and
# imports can be large. Buffering it defeats the first; the default body
# size defeats the second.
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-buffering: "off"
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
spec:
ingressClassName: nginx
tls:
- hosts:
- n8n.sttlab.local
secretName: n8n-tls
rules:
- host: n8n.sttlab.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: n8n
port:
number: 80