Files
infra-n8n/README.md
T
Stéphane Tailland dc13738e86 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>
2026-08-29 12:34:40 +02:00

80 lines
3.3 KiB
Markdown

# infra-n8n
n8n on the lab cluster: the runtime for the estate's autonomous agents,
`Approvisionnement` and `Recouvrement`, whose workflows live in
`classic-models-agent-flows`.
Nothing business-specific is here. This repo deploys the engine; the workflows
are imported into it.
## Layout
| | |
|---|---|
| `k8s/postgres/` | the database, a StatefulSet with its own volume claim |
| `k8s/n8n/` | the workload, its config and its data volume |
| `k8s/ingress/` | a cert-manager `Certificate` and the nginx `Ingress` |
| `k8s/secrets/` | the `ClusterSecretStore` for this estate and the `ExternalSecret` |
| `dev/` | the Flux plumbing that applies `k8s/`, and manages itself |
Reachable at **https://n8n.sttlab.local** once `192.168.139.101` (ingress-nginx)
resolves for that name.
## Two secrets, both of which must outlive any pod
They live in Vault at `secrets/n8n/instance`, read through the `vault-n8n`
store — an AppRole whose policy can read `n8n/*` and nothing else, so a
compromise here is not a way into the keycloak estate's secrets.
- `DB_PASSWORD`
- `N8N_ENCRYPTION_KEY` — encrypts the credentials n8n stores in its database.
**Never regenerate it.** A new key does not re-encrypt what is stored; it
orphans it. The workflows survive, every credential in them does not.
## Bootstrap
Three manual applies, once. The Vault side is set up first — the commands are in
`k8s/secrets/clustersecretstore.yaml`.
```
kubectl apply -f k8s/namespace.yaml
kubectl apply -f dev/gitrepository.yaml
kubectl apply -f dev/bootstrap-kustomization.yaml
```
After that everything reconciles from git, `dev/` included. Never `kubectl
apply` the objects under `k8s/` afterwards — Flux reverts them within its
interval and the cluster silently stops matching what you edited.
## Choices worth knowing
**One replica, and `strategy: Recreate`.** n8n splits editor from workers only
in queue mode, which needs Redis; without it a second replica is two schedulers
racing over the same cron triggers. And the data volume is ReadWriteOnce, so a
rolling update would deadlock — the new pod waiting for a volume the old one has
not released.
**The certificate is issued, not committed.** cert-manager writes `n8n-tls` from
the `sttlab-local-ca` ClusterIssuer. The private key stays in the cluster.
**The CA certificate *is* committed**, in `k8s/ca-configmap.yaml`, and mounted
as `NODE_EXTRA_CA_CERTS`. It is a certificate, never a key, and checking it in
means a workflow can call `keycloak.sttlab.local` and the domain gateways
without anyone disabling verification to make it work. Regenerate on rotation —
it expires 2026-11-24.
**`N8N_HOST` and `WEBHOOK_URL` are the ingress host, not the Service.** They go
into the webhook URLs n8n hands out; advertised as the in-cluster name, they
would be URLs nothing outside can call.
**Executions are pruned after two weeks.** The default keeps them for ever,
which turns the database into a log.
## What is deliberately not here
Authentication. n8n's own user management is left at its defaults, and the
instance is reachable to anyone who can resolve the host on the lab network.
Before it holds anything that matters, it needs either n8n's SSO against the
`classic-models` realm or an `openid-connect` filter at the ingress — the same
Keycloak the rest of the estate already uses.