n8n (orchestration agentique)
Decision du 11/10/2026 : retrait B2. Les sauvegardes/PITR B2 ci-dessous sont historiques et retirees du rendu actif.
| Élément | Valeur |
|---|---|
| URL | https://n8n.lab.djayzone.com |
| Namespace | n8n |
| Image | n8nio/n8n:2.43.1 (Deployment GitOps et runtime observés le 09/10/2026) |
| Base active | CloudNativePG n8n-incident-recovery, 1 instance, PostgreSQL 17.11 ; n8n-postgresql conservé séparément |
| Réplicas | 1 n8n + 1 OAuth2 Proxy |
| Proxies en amont | 2 — Traefik puis OAuth2 Proxy, d'où N8N_PROXY_HOPS=2 |
| Placement | Épinglé sur la région os-uk2, donc sur le worker vps-f0621bb1 |
| Accès | Portail GitHub via OAuth2 Proxy, distinct des comptes n8n ; /webhook* exclu du portail |
| Disponibilité | Aucune : instance unique, base unique, nœud unique |
Exploitation sans IA
Les procédures de diagnostic, reprise et exploitation manuelle sont regroupées dans le manuel opérateur. Elles restent utilisables si le Gateway MCP ou les agents sont indisponibles.
Rôle
n8n est le moteur d'orchestration du MVP agentique (#337, Lot 1) : événements, workflows, retries, callbacks et enchaînement des étapes. L'état durable des tâches, leurs checkpoints et leurs leases sont désormais portés par Task Runtime dans le namespace agent-runtime. Il n'héberge pas l'intelligence — celle-ci reste dans les runtimes conversationnels — ni une seconde passerelle MCP.
Depuis le Lot 4 de #434, n8n est lui-même exposé derrière le MCP Gateway unifié, dans le namespace d'outils n8n.* : les agents lisent, administrent et déclenchent ses workflows par le connecteur unique, sans configuration dédiée. Un workflow n'est déclenchable que s'il porte le tag mcp, possède un nœud Webhook actif et est activé. Le Gateway peut ajouter ou retirer explicitement ce tag via n8n.set_workflow_exposure, sans exposition implicite lors de la création ou de l'activation — voir la fiche Cluster Operator MCP.
Relais navigateur ChatGPT
Le Service chatgpt-browser-relay.n8n.svc.cluster.local:8080 est servi par un
petit contrôleur permanent (32Mi demandés, limite 128Mi). Les deux navigateurs,
headless et graphique, sont arrêtés au repos, avec leurs sidecars. Le premier
appel autorisé réveille le headless ; après 15 minutes sans appel terminé, il
retourne en veille. Les sondes et /v1/models ne réveillent pas le navigateur.
La RAM réellement économisée et le délai de réveil doivent être mesurés après
déploiement ; les requests ne sont pas une mesure de consommation.
L'API existante est conservée : POST /v1/chat avec
{"message":"Texte à envoyer dans ChatGPT"} et un Bearer fourni par le Secret
chatgpt-browser-relay-auth (clé token). La route compatible OpenAI
/v1/chat/completions conserve son contrat interne et ses protections.
L'authentification, la taille et le payload sont vérifiés avant tout réveil.
Les appels sont sérialisés ; le mode config bloque les nouveaux appels et ne
peut pas interrompre un appel en cours. Le délai client doit couvrir le réveil
(180 secondes maximum) et l'opération amont bornée (jusqu'à 300 secondes pour
une complétion avec réparation du protocole).
Le PVC chatgpt-browser-relay-profile et les Secrets existants sont conservés.
Les deux navigateurs sont places sur BB1, noeud du volume local-path existant.
Le contrôleur ne lit ni ne copie les cookies et ne journalise ni prompts,
réponses, identifiants ou contenu du bureau. Une session ChatGPT absente ou
expirée reste une erreur explicite, sans connexion automatisée, contournement
de CAPTCHA/MFA ou achat d'API. L'image Playwright et le client Python sont
alignés sur 1.63.0, y compris pour le bootstrap.
Reconnexion depuis le poste opérateur
La seule commande nécessaire est :
kubectl -n n8n port-forward svc/chatgpt-browser-relay-bootstrap 3000:3000
Ouvrir http://localhost:3000, utiliser operator et le mot de passe existant
du Secret chatgpt-browser-bootstrap-auth (clé password). L'ouverture de la
page authentifiée déclenche le mode config côté cluster ; établir seulement
le tunnel TCP ne déclenche rien. Attendre le navigateur, se connecter à ChatGPT
manuellement, puis cliquer sur Terminer. Le mode normal revient en veille,
prêt à se réveiller au prochain appel. Aucun script, scale ou accès aux Secrets
depuis le poste n'est nécessaire.
Le bureau est proxyfié en HTTP/WebSocket par le portail. Le service reste ClusterIP, sans Ingress ni LoadBalancer, et les NetworkPolicies n'autorisent aucune entrée réseau sur le port 3000 du portail. L'accès passe par le port-forward Kubernetes autorisé et le mot de passe opérateur. Les actions config exigent aussi un jeton CSRF et une origine locale cohérente.
La session config expire 20 minutes après son activation, même si la page reste ouverte ou si le contrôleur redémarre. Un rafraîchissement ne prolonge pas ce délai. Fermer seulement le port-forward n'est pas un signal fiable de fin : utiliser Terminer, ou laisser le délai expirer.
Coordination et reprise
Le contrôleur n'a le droit de modifier que les sous-ressources scale des deux Deployments du relay. Il attend la disparition complète des pods de l'autre mode, y compris ceux en terminaison, avant tout démarrage. Un initContainer authentifié bloque également chaque navigateur tant que le contrôleur ne lui a pas donné la permission d'utiliser le profil partagé.
Les manifests omettent spec.replicas pour confier ce champ au contrôleur ;
Flux continue à gérer images, sondes, limites et NetworkPolicies. Le Deployment
du contrôleur utilise Recreate. Son état minimal (mode, dernier appel terminé,
expiration config) est conservé sur le PVC distinct
chatgpt-browser-relay-control-state de 128Mi, sans cookie ou donnée métier.
Après livraison, vérifier SHA/Flux, contrôleur Ready, veille à 0/0, bascule graphique, accès WebSocket, retour après Terminer/expiration, vraie réponse ChatGPT après réveil et appel OpenClaw. Un pod Ready ne prouve pas la session authentifiée. Le rollback passe par une PR de revert ; conserver les PVC et attendre la fin complète du graphique avant d'autoriser le headless. Ne pas supprimer de profil ou de verrou pour forcer une reprise.
Placement et résidence
n8n, son OAuth2 Proxy et sa base sont épinglés sur la région os-uk2, conformément à l'objectif du quatrième nœud : concentrer le compute sur le worker et délester les control-plane français. L'épinglage est exprimé par topology.kubernetes.io/region, pas par kubernetes.io/hostname, afin de rester valide si le worker est réinstallé sous un autre nom.
Conséquence à connaître : n8n manipulera des jetons GitHub et Plane ainsi que le contenu des tickets, et ces données résident hors de France. Ce périmètre est distinct de celui de NotreCouple, qui reste explicitement exclu de os-uk2 et dont aucune donnée personnelle ne transite par n8n.
Disponibilité — limite assumée
L'application, son OAuth2 Proxy et sa base PostgreSQL vivent sur le même nœud unique, avec un PVC local-path épinglé à ce nœud.
La perte de vps-f0621bb1 arrête donc l'orchestration et rend son état inaccessible jusqu'au retour du nœud. Ce n'est pas un oubli : os-uk2 ne contient qu'un nœud, donc l'anti-affinité required du Cluster ne pourrait de toute façon pas placer une deuxième instance, et #337 demande explicitement de ne pas durcir avant d'avoir fait tourner la boucle. Un passage à instances: 2 deviendra volontairement non ordonnançable plutôt que de disperser la base hors du worker.
Les sauvegardes PostgreSQL sont configurées via CloudNativePG et le plugin Barman Cloud vers le stockage B2 backups-lab, avec archivage WAL continu et rétention déclarée à 7 jours. Historique (14/09/2026) : le cluster alors utilisé n8n-postgresql rencontrait des erreurs de lecture du catalogue B2 (RetentionPolicyFailed) liées aux limites de téléchargement/transactions. Le contexte était suivi sous PLAT-242. Depuis, la base effectivement consommée est n8n-incident-recovery et les sauvegardes des 02–03/10 ci-dessous ont terminé avec succès ; l'incident de septembre ne doit pas être présenté comme une panne actuelle. Ces preuves datées ne remplacent pas un drill de restauration isolée ni la lecture des sauvegardes récentes. n8n reste exclu des dépendances runtime critiques par conception.
Accès et authentification
Deux couches distinctes, et il faut les garder distinctes.
L'OAuth2 Proxy GitHub est un portail d'accès : il décide qui peut atteindre le service. Il n'ouvre pas de session n8n. Une fois le portail franchi, l'utilisateur est un visiteur anonyme pour n8n, qui applique ensuite sa propre gestion de comptes. Conséquences pratiques en exploitation :
- un compte GitHub autorisé au portail n'a aucun droit dans n8n tant qu'il n'y a pas de compte n8n ;
- retirer un accès dans n8n ne ferme pas l'accès au service, et inversement ;
- le compte propriétaire n8n doit être créé et protégé indépendamment, à la première ouverture de l'éditeur.
L'Ingress est unique et se termine sur le proxy ; elle ne pointe jamais sur n8n. La NetworkPolicy applique la même règle depuis le réseau : Traefik ne peut atteindre que le proxy. Une Ingress ajoutée par erreur plus tard ne suffirait donc pas à exposer l'éditeur anonymement.
Le proxy réutilise le connecteur homelab existant — même OAuth App GitHub, même cookie _homelab_sso, même domaine .lab.djayzone.com. Le callback pointe sur auth.lab.djayzone.com, comme monitoring et stirling-pdf : aucune URL de callback supplémentaire n'est requise dans l'OAuth App.
Plane a suivi le chemin inverse : son proxy externe a été retiré par #416 au profit de son authentification native. n8n garde un portail parce qu'il n'a pas d'équivalent — son éditeur expose des credentials et un moteur d'exécution.
Les chemins /webhook, /webhook-test et /webhook-waiting contournent le portail via --skip-auth-route. Ce sont des points d'entrée machine : un SSO navigateur leur renverrait une redirection de connexion qu'un appelant automatique ne sait pas suivre.
Point faible connu : l'authentification de ces chemins est faite par n8n lui-même, via une credential « Header Auth » portée par chaque workflow publié. Un workflow publié sans cette credential est ouvert à qui connaît son chemin. C'est une discipline de workflow, pas une garantie de plateforme, et c'est la faiblesse à traiter en priorité dans le hardening agentique (#338).
Secrets — hors Git
Trois Secrets doivent exister dans le namespace n8n avant que Flux ne rende les pods prêts. Aucun n'est versionné et aucun ne doit être généré depuis une conversation.
| Secret | Clés | Rôle |
|---|---|---|
oauth2-proxy-credentials |
client-id, client-secret, cookie-secret |
SSO GitHub. Le cookie-secret doit être identique à celui des autres namespaces, sinon la session partagée du homelab ne fonctionne pas |
n8n-postgresql-app |
username, password — type kubernetes.io/basic-auth |
Propriétaire de la base. Utilisé au bootstrap puis par spec.managed.roles pour maintenir le rôle PostgreSQL n8n aligné avec le Secret consommé par l'application |
n8n-secrets |
encryption-key |
Chiffre les credentials stockées par n8n |
Le Secret n8n-postgresql-app est géré hors Git, mais doit porter le label cnpg.io/reload=true afin qu'une future rotation de son mot de passe soit propagée immédiatement par CloudNativePG. Le manifeste Git ne contient que la référence au Secret, jamais sa valeur.
N8N_ENCRYPTION_KEY est irremplaçable : la régénérer sur une instance déjà peuplée rend toutes les credentials existantes illisibles.
Exploitation
kubectl -n n8n get deploy,pod,svc,ingress,networkpolicy,cluster -o wide
kubectl -n n8n logs deploy/n8n --tail=200
kubectl -n n8n logs deploy/oauth2-proxy --tail=100
kubectl -n n8n get cluster -o wide
kubectl -n n8n get deployment n8n -o yaml # repérer DB_POSTGRESDB_HOST
curl.exe -I https://n8n.lab.djayzone.com
Le conteneur n8n conserve readOnlyRootFilesystem: true. Les chemins /home/node/.n8n, /home/node/.cache et /tmp sont donc montés explicitement en emptyDir pour les écritures transitoires nécessaires au démarrage ; l'état durable reste PostgreSQL.
La route publique conserve un rate-limit Traefik global à average: 300 et burst: 600. Le seuil initial 60/120 était trop bas pour le chargement à froid de l'éditeur n8n, qui demande de nombreux chunks JS/CSS en parallèle : Traefik rejetait alors une partie des assets en 429 avant même qu'ils n'atteignent OAuth2 Proxy, ce qui pouvait laisser l'interface vide. Le rate-limit reste volontairement présent pour protéger la route partagée avec les webhooks.
Sans session de portail, / doit répondre 302 vers GitHub. Un chemin /webhook/... inexistant doit répondre 404 émis par n8n, et non une redirection de connexion : c'est ce qui prouve que le contournement --skip-auth-route fonctionne et que le portail ne s'applique pas aux appels machine.
Mise à jour
Modifier explicitement le tag et le digest de l'image dans apps/ovh-primary/n8n/deployment.yaml, consulter les notes de version, valider Kustomize, puis laisser Flux effectuer le remplacement. La stratégie de déploiement est Recreate : deux instances qui se recouvrent déclencheraient deux fois le même workflow planifié.
Historique : qualification de la mise à jour 2.42.2 (02–03/10/2026)
Les PR #906 à #923 qualifient la mise à jour sur la restauration séparée n8n-upgrade-qualification. Les workflows copiés sont inactifs, leurs exécutions copiées purgées, et le Job ne peut joindre que DNS et cette base. Il compare les graphes, teste le démarrage et l'import inactif sur 2.42.2, puis la lecture et le démarrage 2.39.4 sur la base migrée. Les fichiers d'export restent sur emptyDir Memory et sont effacés en fin de qualification, sans contenu métier journalisé. La copie comporte cinq workflows et aucun credential enregistré : aucun déchiffrement réel n'est revendiqué.
La base a été restaurée depuis le backup vérifié 20261002T020002. Une sauvegarde fraîche de la base active n8n-pre2422-20261002 a ensuite terminé le 02/10/2026 à 21:12:11 UTC (20261002T211131). Cette preuve est datée ; elle ne garantit pas les sauvegardes futures. Le diagnostic de septembre sous PLAT-242 ci-dessus est historique et ne décrit pas l'état de cette sauvegarde réussie.
Depuis, la version 2.43.1 est déclarée et observée en production au 09/10 ; les preuves détaillées ci-dessous concernent seulement la qualification antérieure de la 2.42.2. Le rollout conserve Recreate pour éviter deux superviseurs simultanés, ainsi que la clé de chiffrement existante. Le Job n8n-rollback-runtime-v6 a validé le retour à l'image 2.39.4 sur la base migrée, avec HTTP 200 sur /healthz/readiness. Le cache de l'ancien serveur occupe 79 826 944 octets (~76 Mio) : la limite de 32 Mio des précédents Jobs était insuffisante ; #923 la porte à 256 Mio sans modifier la production. La sauvegarde quotidienne de la base active 20261003T020002 a terminé le 03/10/2026 à 02:00:44 UTC. Une restauration de la sauvegarde reste un recours distinct qui perdrait les écritures postérieures et ne se déclenche pas automatiquement.