Self-hoster n8n sur son propre serveur permet de garder un contrôle total sur ses workflows d'automatisation, ses données et ses credentials, sans dépendre des quotas ni du pricing de n8n Cloud. Que vous partiez d'un VPS chez Hetzner, OVH ou Scaleway, ou d'un homelab avec un NAS et Docker déjà en place, l'installation tient en quelques conteneurs bien configurés — à condition de ne pas sauter les étapes qui comptent vraiment : encryption key stable, base de données Postgres, HTTPS correctement propagé aux webhooks.
Ce guide couvre l'installation complète en production : Docker Compose avec PostgreSQL, exposition HTTPS via reverse proxy (ou tunnel Cloudflare pour un homelab sans IP publique fixe), passage en mode queue avec Redis pour encaisser la charge, durcissement sécurité, sauvegardes et procédure de mise à jour. L'objectif final : une instance n8n stable, reproductible, qui survit à un redémarrage serveur sans intervention manuelle et dont on peut restaurer l'intégralité en cas de crash disque.
Pourquoi self-hoster n8n plutôt qu'utiliser n8n Cloud
n8n Cloud facture à l'exécution de workflow et impose des quotas selon le plan. Pour une PME ou un développeur qui automatise beaucoup (webhooks à volume, scraping régulier, synchronisation de données toutes les 5 minutes), la facture grimpe vite et de façon peu prévisible. En self-hosting, le coût devient fixe : celui du serveur, quel que soit le nombre d'exécutions.
L'autre argument, souvent plus décisif : les credentials. n8n stocke les clés API, tokens OAuth et mots de passe de tous les services connectés (Stripe, Gmail, bases de données clients, CRM). Sur une instance self-hostée, ces secrets restent chiffrés sur une infrastructure que vous contrôlez entièrement, sans transiter par un tiers. Pour des workflows qui touchent des données clients sensibles, c'est souvent un prérequis de conformité plutôt qu'une préférence.
Dernier point : la version self-hosted n8n donne accès à l'intégralité des nodes et à l'édition du code des nodes communautaires, alors que certains plans cloud restreignent ces fonctionnalités.
Prérequis : serveur, domaine et DNS
Pour une instance de production stable, comptez a minima 2 vCPU, 4 Go de RAM et 40 Go de stockage SSD. C'est confortable pour faire tourner n8n, PostgreSQL et un reverse proxy simultanément, et ça couvre la grande majorité des usages PME/agence. En dessous de 2 Go de RAM, PostgreSQL et n8n se disputent la mémoire et les exécutions parallèles deviennent instables.
| Profil | vCPU | RAM | Stockage | Cas d'usage |
|---|---|---|---|---|
| Test / perso | 1 | 2 Go | 20 Go | Quelques workflows, faible volume |
| Production PME | 2 | 4 Go | 40 Go | Webhooks réguliers, intégrations métier |
| Scaling (queue mode) | 4+ | 8 Go+ | 80 Go+ | Gros volume, plusieurs workers |
Vous aurez aussi besoin d'un nom de domaine (ou sous-domaine, type n8n.mondomaine.fr) pointé en DNS vers l'IP du serveur, indispensable pour que les webhooks n8n fonctionnent correctement en HTTPS. Sans domaine et sans HTTPS, la plupart des intégrations tierces (Stripe, GitHub, Slack) refusent purement et simplement d'envoyer leurs webhooks.
Installer Docker et Docker Compose
Sur un serveur Ubuntu/Debian frais, l'installation de Docker se fait en une commande via le script officiel, puis on vérifie que Docker Compose (intégré depuis Docker Engine 20.10+ via docker compose, sans tiret) est disponible.
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
docker compose version
Reconnectez-vous en SSH après le usermod pour que l'appartenance au groupe docker soit prise en compte sans sudo. Créez ensuite un répertoire de travail dédié, par exemple /opt/n8n, qui contiendra le docker-compose.yml, le fichier .env et les scripts de sauvegarde.
Le docker-compose.yml de base
La configuration minimale de production comprend deux services : n8n et PostgreSQL. Le volume n8n_data est critique — il contient la clé de chiffrement et les métadonnées locales, à sauvegarder impérativement.
# docker-compose.yml
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: ${DB_POSTGRESDB_DATABASE}
POSTGRES_USER: ${DB_POSTGRESDB_USER}
POSTGRES_PASSWORD: ${DB_POSTGRESDB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_POSTGRESDB_USER}"]
interval: 10s
retries: 5
n8n:
image: docker.n8n.io/n8nio/n8n:1.71.2
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
env_file: .env
ports:
- "127.0.0.1:5678:5678"
volumes:
- n8n_data:/home/node/.n8n
volumes:
postgres_data:
n8n_data:
Notez le port publié sur 127.0.0.1:5678 uniquement : n8n n'est jamais exposé directement sur internet, seul le reverse proxy local y accède. Et l'image est fixée sur une version précise (1.71.2) plutôt que sur latest — indispensable pour éviter qu'une mise à jour automatique de breaking change ne casse la prod un dimanche soir.
Les variables d'environnement qui comptent vraiment
Le fichier .env pilote tout le comportement de n8n. Trois variables sont non négociables en production.
# .env
N8N_HOST=n8n.mondomaine.fr
N8N_PROTOCOL=https
N8N_PORT=5678
WEBHOOK_URL=https://n8n.mondomaine.fr/
GENERIC_TIMEZONE=Europe/Paris
N8N_ENCRYPTION_KEY=une-chaine-aleatoire-longue-et-stable
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=postgres
DB_POSTGRESDB_PORT=5432
DB_POSTGRESDB_DATABASE=n8n
DB_POSTGRESDB_USER=n8n
DB_POSTGRESDB_PASSWORD=mot-de-passe-fort
N8N_ENCRYPTION_KEY chiffre tous les credentials stockés en base. Générez-la une seule fois (openssl rand -hex 32 par exemple), notez-la dans un gestionnaire de secrets, et ne la changez plus jamais : la perdre rend irrécupérables tous les credentials déjà enregistrés, et il faudrait alors reconnecter manuellement chaque intégration.
WEBHOOK_URL doit pointer vers l'URL publique HTTPS exacte, avec le slash final. Une erreur classique est de laisser cette variable absente ou sur localhost : les workflows fonctionnent en test dans l'éditeur, mais aucun webhook externe (Stripe, formulaire, CRM) n'arrive jamais en production, sans message d'erreur explicite côté n8n.
Passer de SQLite à PostgreSQL
Par défaut, n8n installé sans configuration particulière utilise SQLite, un fichier local. Ça fonctionne pour tester, mais SQLite gère mal les accès concurrents : dès que plusieurs workflows s'exécutent en parallèle ou que le volume de webhooks dépasse quelques dizaines par jour, les verrous d'écriture provoquent des erreurs et des exécutions perdues.
PostgreSQL, comme configuré dans le docker-compose.yml ci-dessus, encaisse sans problème les accès concurrents et offre une récupération après crash bien plus robuste. C'est aussi un prérequis strict pour activer le mode queue plus loin dans ce guide. La bascule doit se faire dès l'installation initiale : migrer une instance SQLite existante vers Postgres après coup est possible mais fastidieux, mieux vaut partir directement sur la bonne base.
Exposer n8n en HTTPS avec un reverse proxy
n8n écoute en interne sur le port 5678 en HTTP. Tout le trafic public doit transiter par un reverse proxy qui termine le TLS et transmet vers n8n en local. Caddy est l'option la plus simple pour un self-host : il obtient et renouvelle automatiquement un certificat Let's Encrypt sans configuration manuelle.
# Caddyfile
n8n.mondomaine.fr {
reverse_proxy 127.0.0.1:5678
}
Trois lignes suffisent. Caddy gère la redirection HTTP → HTTPS, le renouvellement du certificat et les en-têtes de sécurité par défaut. Traefik reste une alternative pertinente si vous exposez déjà plusieurs services Docker sur le même serveur et voulez centraliser le routage via des labels sur chaque conteneur — plus de contrôle, mais une configuration initiale plus lourde que Caddy.
Dans les deux cas, la règle ne change pas : le port 5678 ne doit jamais être ouvert directement sur le pare-feu public, uniquement 80 et 443 vers le reverse proxy.
L'alternative homelab : Cloudflare Tunnel sans port forwarding
Sur un homelab (NAS, mini-PC, Raspberry Pi derrière une box internet), ouvrir les ports 80/443 n'est pas toujours possible ou souhaitable — CGNAT chez certains opérateurs, IP dynamique, ou simplement volonté de ne rien exposer publiquement. cloudflared, le tunnel Cloudflare, résout ce problème : un conteneur léger établit une connexion sortante vers Cloudflare, qui route ensuite le trafic HTTPS public jusqu'à votre n8n local, sans aucun port ouvert côté box.
# extrait docker-compose.yml
cloudflared:
image: cloudflare/cloudflared:latest
restart: unless-stopped
command: tunnel run
environment:
TUNNEL_TOKEN: ${CLOUDFLARE_TUNNEL_TOKEN}
Le tunnel se configure une fois dans le dashboard Cloudflare Zero Trust (associer le sous-domaine au service interne http://n8n:5678), puis tourne de façon autonome. C'est l'option la plus pratique pour qui héberge déjà n8n à côté d'un Jellyfin ou d'un stockage S3-compatible sur le même NAS, sans vouloir gérer de certificats ni de règles de pare-feu supplémentaires.
Scaler avec le mode queue et des workers Redis
Le mode par défaut ("regular") exécute chaque workflow directement dans le processus principal de n8n. Ça suffit pour un usage modéré, mais un pic de webhooks simultanés peut saturer l'instance et retarder les exécutions. Le mode queue distribue les exécutions vers des workers séparés via Redis.
# extrait docker-compose.yml pour le mode queue
redis:
image: redis:7-alpine
restart: unless-stopped
n8n-worker:
image: docker.n8n.io/n8nio/n8n:1.71.2
restart: unless-stopped
command: worker
depends_on:
- postgres
- redis
env_file: .env
volumes:
- n8n_data:/home/node/.n8n
Trois règles à respecter : EXECUTIONS_MODE=queue et les variables QUEUE_BULL_REDIS_HOST / QUEUE_BULL_REDIS_PORT doivent être ajoutées au .env, partagé identiquement entre le processus principal et chaque worker. Le N8N_ENCRYPTION_KEY doit être strictement identique sur toutes les instances — un worker avec une clé différente ne pourra pas déchiffrer les credentials et échouera silencieusement sur toute exécution qui en dépend. Vous pouvez ensuite scaler horizontalement en dupliquant le service n8n-worker (docker compose up -d --scale n8n-worker=3) pour absorber davantage de charge.
Pour la majorité des PME et des projets d'agence, le mode regular suffit largement. Ne passez en mode queue que si vous constatez des retards d'exécution mesurés, pas par anticipation.
Sécuriser l'instance en production
L'authentification basique (N8N_BASIC_AUTH_ACTIVE) est dépréciée depuis n8n v2 au profit du système de gestion d'utilisateurs intégré, configuré au premier lancement via l'interface (création du compte owner avec email + mot de passe). Ne réactivez pas l'ancien mécanisme : le système intégré gère les rôles, l'invitation d'utilisateurs et, sur l'offre Enterprise, le SSO.
Au-delà de l'authentification applicative, quatre points structurent une instance durcie :
- Réseau : seuls les ports 80/443 du reverse proxy sont exposés publiquement, jamais 5678 ni 5432.
- Rate limiting sur les routes de webhook, via Caddy (
rate_limit) ou en amont chez Cloudflare, pour absorber les tentatives d'abus sans saturer l'instance. - Secrets :
N8N_ENCRYPTION_KEYet les mots de passe Postgres dans un fichier.envavec permissions restreintes (chmod 600), jamais committé dans un dépôt Git. - Mises à jour de sécurité régulières de l'image n8n — les CVE touchant les nodes ou le runtime sont corrigées rapidement par l'équipe n8n, encore faut-il appliquer les patches.
Sur un homelab exposé via tunnel Cloudflare, ajoutez une couche Zero Trust Access (authentification email/SSO avant même d'atteindre n8n) pour un accès admin réservé, tout en laissant les endpoints de webhook publics accessibles sans friction pour les services tiers.
Sauvegarder et restaurer ses workflows
Deux éléments doivent être sauvegardés ensemble et de façon cohérente : le volume n8n_data (contient la clé de chiffrement locale et les données binaires des exécutions) et la base PostgreSQL (workflows, credentials chiffrés, historique d'exécution). Sauvegarder l'un sans l'autre rend la restauration inutilisable.
#!/bin/bash
# backup-n8n.sh — à lancer via cron quotidien
DATE=$(date +%Y%m%d)
docker exec n8n-postgres-1 pg_dump -U n8n n8n > /backups/n8n-db-$DATE.sql
docker run --rm -v n8n_n8n_data:/data -v /backups:/backup alpine \
tar czf /backup/n8n-data-$DATE.tar.gz -C /data .
find /backups -mtime +14 -delete
Automatisez ce script en cron quotidien, et surtout, copiez le résultat hors du serveur — un backup stocké sur le même disque que la donnée source ne protège d'aucune panne matérielle. Un bucket S3-compatible (MinIO auto-hébergé, ou un provider externe) via rclone ou aws s3 cp en fin de script est la solution la plus simple pour un homelab qui dispose déjà d'un stockage S3 en interne.
Mettre à jour n8n sans tout casser
Ne jamais mettre à jour en pointant directement sur latest en production. La procédure recommandée : sauvegarder d'abord, consulter le changelog de la version cible pour les breaking changes, puis mettre à jour de façon incrémentale.
./backup-n8n.sh
# éditer docker-compose.yml : remplacer le tag de version
docker compose pull
docker compose up -d
docker compose logs -f n8n
Pour un saut de plusieurs versions majeures, mettez à jour version par version plutôt que de sauter directement à la dernière : n8n applique des migrations de base de données à chaque démarrage, et les sauter en bloc augmente le risque de migration ratée sur une base volumineuse. En cas de problème après mise à jour, le retour arrière consiste à repointer le tag d'image vers la version précédente et relancer — la base de données reste compatible tant qu'aucune migration destructive n'a eu lieu entre-temps.
Superviser l'instance
docker compose logs -f n8n reste le premier réflexe pour déboguer une exécution en échec. Pour un suivi continu, un check d'uptime externe (UptimeRobot, Healthchecks.io) sur l'URL publique suffit à être alerté en cas de panne du serveur ou du reverse proxy.
Un point souvent négligé : la table des exécutions grossit vite si rien n'est configuré pour la purger. Les variables EXECUTIONS_DATA_PRUNE=true, EXECUTIONS_DATA_MAX_AGE (en heures) et EXECUTIONS_DATA_PRUNE_MAX_COUNT limitent la rétention et évitent qu'une base Postgres de quelques centaines de Mo ne devienne plusieurs dizaines de Go au bout de quelques mois d'exécutions fréquentes.
En pratique
Sur les stacks Kreio, n8n tourne systématiquement dans son propre docker-compose.yml, isolé des applications clientes, avec Traefik en frontal partagé pour router plusieurs sous-domaines (n8n., app., api.) vers leurs conteneurs respectifs sur le même serveur. Les données binaires volumineuses (fichiers générés par les workflows) sont déportées vers un bucket S3-compatible plutôt que stockées dans le volume n8n_data, via la variable N8N_DEFAULT_BINARY_DATA_MODE=filesystem combinée à un montage réseau, ce qui garde les sauvegardes légères et rapides.
Pour la procédure de mise à jour sans interruption de service sur un serveur qui héberge plusieurs conteneurs (n8n compris), la même logique que pour un déploiement applicatif s'applique : voir l'article sur le déploiement blue/green et canary pour éviter un downtime perceptible lors des montées de version.
Sources
- n8n Docs — Configuring queue mode — docs.n8n.io
- n8n Docs — Enable queue mode — docs.n8n.io
- n8n Docs — Set a custom encryption key — docs.n8n.io
- Caddy Docs — Automatic HTTPS — caddyserver.com