# 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.