n8n est un outil puissant d’automatisation de workflows : pensez Zapier, mais en fair-code et auto-hébergeable. n8n.cloud propose une offre managée, mais héberger soi-même sur AWS donne :
- le contrôle total de vos données et de vos workflows ;
- aucun quota d’exécutions (les offres n8n Cloud incluent un volume mensuel d’exécutions, voir les tarifs) ;
- des intégrations sur mesure sans restriction ;
- des coûts prévisibles (EC2 et RDS fixes plutôt qu’une facturation à l’usage).
Le piège : installer n8n correctement demande bien plus qu’un npm install n8n. Il faut un reverse proxy (Nginx) pour terminer le SSL, PostgreSQL pour la persistance (SQLite ne tient pas en production), Redis pour le mode queue avec des workers séparés, des certificats Let’s Encrypt et une orchestration Docker Compose. Ce guide suit chaque étape, du lancement de l’instance EC2 à l’accès sécurisé sur https://yourdomain.com. Comptez 1 à 2 heures selon votre familiarité avec AWS.
Architecture
Internet (HTTPS:443)
↓
Nginx (SSL termination + WebSocket support)
↓
N8N Main Container (UI + API on localhost:5678)
↓
N8N Worker Container (Background job execution)
↓
Redis (Queue management)
↓
RDS PostgreSQL (Data persistence)
Pourquoi ces choix :
- Docker et Docker Compose isolent n8n de l’hôte : pas de pollution de l’instance EC2, retour arrière instantané, ajout de workers facile. L’alternative, un
npm installdirect, est brouillonne et difficile à maintenir. - Nginx sert de passerelle : terminaison SSL, WebSocket, contrôle du buffering. Exposer n8n directement sur le port 443 exigerait de le lancer en root.
- Let’s Encrypt (Certbot) : certificats gratuits, renouvellement automatique par cron, expiration à 90 jours qui impose de bonnes pratiques.
- RDS PostgreSQL : base de production hors d’EC2, avec sauvegardes automatiques, restauration à un instant donné, mise à l’échelle indépendante, et la base survit à la suppression de l’instance.
- Redis : file de messages pour l’exécution distribuée. n8n Main délègue les workflows lourds aux workers. L’alternative est le mode régulier, où tout s’exécute dans le process principal (voir le mode queue).
Prérequis
- EC2 : t3.medium ou plus (2 vCPU, 4 Go de RAM minimum), Amazon Linux 2023, 20 Go GP3, IP publique.
- RDS PostgreSQL : PostgreSQL 15.x, db.t3.micro (évolutif), 20 Go GP3, accessibilité publique désactivée (accès via le security group d’EC2).
- Security groups : sur EC2, port 22 (SSH) limité à votre IP ou via SSM Session Manager, ports 80 et 443 ouverts à 0.0.0.0/0. Sur RDS, port 5432 ouvert uniquement depuis le security group d’EC2.
- Un domaine ou sous-domaine pointant vers l’IP publique d’EC2, propagation DNS terminée (vérifiez avec
dig yourdomain.com). - Les identifiants RDS : endpoint, utilisateur maître (souvent
postgres), mot de passe. La basen8nest créée pendant l’installation.
Avant de commencer, vérifiez la connexion SSH à EC2, la connexion à RDS depuis EC2 (avec le client PostgreSQL) et la résolution DNS (dig yourdomain.com +short doit renvoyer l’IP d’EC2).
Installation en 22 blocs
Blocs 0 à 9 : le système et les outils
- Oh-My-Zsh (facultatif) : autocomplétion Docker, AWS CLI et Git.
- Mise à jour système :
sudo yum update -y. Les mises à jour d’Amazon Linux apportent correctifs de sécurité et corrections de dépendances. - Nginx :
yum install,systemctl startetenable. À ce stade,http://your-ec2-ipdoit afficher la page d’accueil par défaut de Nginx. - Certbot : client officiel Let’s Encrypt. Sur Amazon Linux 2023, n’activez pas EPEL (chemin d’AL2, plus maintenu, voir AL2023 et EPEL) : installez certbot depuis le dépôt AL2023 si disponible, ou via pip/snap selon les instructions de certbot. Aucun certificat n’est demandé à ce stade, car il faut d’abord que n8n tourne (bloc 21).
- Cron :
croniepour le renouvellement SSL. AL2023 recommande les timers systemd, mais cronie fonctionne toujours (paquets dépréciés d’AL2023). - Docker :
yum install docker, démarrage au boot, puisusermod -aG docker $USER. Ce dernier ne prend effet qu’après déconnexion/reconnexion : d’ici là, utilisezsudo docker. - Docker Compose : binaire autonome téléchargé depuis les releases GitHub dans
/usr/local/bin/docker-compose. Compose v2+ existe aussi comme plugin du CLI Docker (docker composeavec un espace, voir installer le plugin Compose). - Client PostgreSQL :
yum install -y postgresql15, pour tester RDS, créer la base et déboguer. - Python, Git : requis par Certbot et utiles ensuite.
- Node.js et PM2 : pas indispensables dans une architecture Docker, car n8n tourne dans des conteneurs. Le
restart: alwaysde Docker remplace l’auto-restart de PM2.
Blocs 10 à 12 : dossier, certificat RDS, clé de chiffrement
Créez un dossier de travail dédié (~/n8n-docker), simple à sauvegarder. Téléchargez le bundle de certificats RDS pour que n8n vérifie le serveur (valable pour toute région AWS commerciale, voir la doc AWS) :
wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -O rds-ca.pem
Générez ensuite la clé de chiffrement, qui protège clés d’API, jetons OAuth, secrets de webhooks et mots de passe stockés. n8n chiffre toujours les identifiants, mais en mode queue le main et les workers doivent partager la même clé : définissez N8N_ENCRYPTION_KEY explicitement (clé de chiffrement n8n).
N8N_ENCRYPTION_KEY=$(openssl rand -base64 32)
echo $N8N_ENCRYPTION_KEY
Avertissement critique : si vous perdez cette clé, vous ne pouvez plus déchiffrer les identifiants stockés. n8n démarrera, mais tous les champs chiffrés seront illisibles. Copiez-la dans un gestionnaire de mots de passe.
Bloc 13 : le fichier .env
cat > .env << EOF
# AWS RDS Config
RDS_HOST=your-rds-endpoint.amazonaws.com
POSTGRES_USER=postgres
POSTGRES_PASSWORD=your-rds-password
POSTGRES_DB=n8n
# Domain & SSL Config
DOMAIN_NAME=yourdomain.com
N8N_PATH=/
# Redis Config (internal Docker network)
REDIS_HOST=redis
# N8N Encryption Key (CRITICAL - BACKUP THIS!)
N8N_ENCRYPTION_KEY=$N8N_ENCRYPTION_KEY
# N8N Optimizations
OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true
N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
# WebSocket and Performance
N8N_DISABLE_UI=false
N8N_METRICS=false
# Community Packages (AI Agent tools, custom nodes)
N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true
# Email for SSL Certificate
SSL_EMAIL=your-email@example.com
EOF
Quelques points :
- Aucune variable d’authentification basic auth n’est à définir : n8n a son propre système d’utilisateurs, et vous créez le compte admin à la première connexion.
OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=trueenvoie aussi les exécutions manuelles (éditeur) aux workers ; les exécutions de production y passent déjà en mode queue (variables du mode queue).- Ne définissez pas
N8N_RUNNERS_ENABLED: déprécié depuis n8n 2.0, les task runners sont actifs par défaut. n8n recommande de toujours les utiliser en production, car ils sont la seule couche d’isolation entre le code utilisateur et n8n ; en production, lancez-les en mode externe (imagen8nio/runners). Voir Task runners et les changements majeurs de n8n 2.0. N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=trueautorise les nœuds AI Agent à utiliser les paquets communautaires comme outils (LangChain).
Blocs 14 et 15 : initialiser la base
Un script init-data.sh vérifie que la base n8n existe (SELECT 1 FROM pg_database ...), la crée sinon (CREATE DATABASE), accorde les droits (GRANT ALL PRIVILEGES, GRANT CREATE ON SCHEMA public) pour que n8n puisse créer ses tables, et efface le mot de passe de l’environnement. On le lance après un test de connexion à RDS avec réessais toutes les 5 secondes :
until PGPASSWORD='your-rds-password' psql \
--host="your-rds-endpoint.amazonaws.com" \
--username="postgres" \
--dbname="postgres" \
-c '\q' 2>/dev/null; do
echo "Waiting for PostgreSQL..."
sleep 5
done
./init-data.sh "your-rds-endpoint.amazonaws.com" "postgres" 'your-rds-password' "n8n"
Si Zsh renvoie event not found: Example123!, c’est l’expansion d’historique sur le ! : entourez le mot de passe de guillemets simples (pas doubles), dans bash comme dans zsh. Nous avons rencontré ce piège pendant l’installation : le script marchait dans Bash et cassait dans Zsh.
Bloc 16 : docker-compose.yml
Trois services : Redis, n8n Main et n8n Worker. Avec condition: service_healthy, n8n et le worker attendent que Redis soit sain.
volumes:
n8n_storage:
redis_storage:
services:
redis:
image: redis:7-alpine
restart: always
volumes:
- redis_storage:/data
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 5s
timeout: 5s
retries: 10
n8n:
image: docker.n8n.io/n8nio/n8n:2.x.y # pin the exact release you tested
restart: always
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=${RDS_HOST}
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
- DB_POSTGRESDB_USER=${POSTGRES_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_HEALTH_CHECK_ACTIVE=true
- DB_POSTGRESDB_SSL_CA=/rds-ca.pem
- DB_POSTGRESDB_SSL_REJECT_UNAUTHORIZED=true
- N8N_HOST=${DOMAIN_NAME}
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://${DOMAIN_NAME}/
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
- N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true
ports:
- "127.0.0.1:5678:5678"
volumes:
- n8n_storage:/home/node/.n8n
- ./rds-ca.pem:/rds-ca.pem
depends_on:
redis:
condition: service_healthy
n8n-worker:
image: docker.n8n.io/n8nio/n8n:2.x.y # same release as main
restart: always
command: worker
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=${RDS_HOST}
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
- DB_POSTGRESDB_USER=${POSTGRES_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
- DB_POSTGRESDB_SSL_CA=/rds-ca.pem
- DB_POSTGRESDB_SSL_REJECT_UNAUTHORIZED=true
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_HEALTH_CHECK_ACTIVE=true
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
volumes:
- ./rds-ca.pem:/rds-ca.pem
depends_on:
redis:
condition: service_healthy
n8n:
condition: service_started
- Pas de ligne
version:en tête : elle est obsolète et ne déclenche qu’un avertissement (référence du fichier Compose). Un simpledepends_onen liste ne fait que l’ordre de démarrage (contrôler l’ordre de démarrage). - Épinglez l’image n8n sur une release 2.x exacte et lisez les notes de version avant de changer le tag : un pull non épinglé peut sauter une version majeure.
- Le port 5678 n’est publié que sur
127.0.0.1: n8n n’est joignable que via Nginx, jamais directement depuis Internet (Docker publie par défaut sur toutes les interfaces). - Les RDS sont signés par des CA AWS, pas auto-signés : avec le bundle monté, n8n peut vérifier le certificat, donc gardez
SSL_REJECT_UNAUTHORIZED=true. Avecfalse, la connexion est chiffrée mais le serveur n’est pas vérifié (variables de base de données n8n). - Le worker tourne en mode
worker: pas d’interface, pas de port exposé, il exécute seulement les workflows.
Blocs 17 à 19 : démarrer
sudo docker-compose up -d télécharge les images (2 à 5 minutes la première fois), crée le réseau et les volumes, puis lance les trois conteneurs. sudo docker-compose ps doit les montrer Up (Redis healthy). Un avertissement « variable is not set. Defaulting to a blank string » signifie que Compose n’a pas trouvé .env : lancez-le depuis le dossier qui le contient (ou passez --env-file) et contrôlez avec docker compose config (interpolation des variables).
n8n a besoin de 1 à 2 minutes pour se connecter à RDS, exécuter les migrations, initialiser Redis et démarrer le serveur HTTP. Une boucle de 20 essais espacés de 30 secondes interroge http://localhost:5678 et affiche les logs après 5 essais. Chez nous, n8n a mis 90 secondes : les logs montraient encore les migrations, et il est devenu accessible à la 7e tentative. Les migrations peuvent prendre 2 à 3 minutes sur un RDS lent.
Enfin, silencieux mais utile : l’avertissement Redis overcommit_memory is set to 0 disparaît avec :
sudo sysctl vm.overcommit_memory=1
echo 'vm.overcommit_memory = 1' | sudo tee -a /etc/sysctl.conf > /dev/null
Bloc 20 : Nginx en HTTP (temporaire)
Let’s Encrypt doit joindre votre serveur en HTTP sur le port 80 pour vérifier le domaine : on commence donc en HTTP, puis on passera en HTTPS. Le bloc server écoute sur le port 80, transmet tout à 127.0.0.1:5678 avec les en-têtes Host, X-Real-IP, X-Forwarded-For et X-Forwarded-Proto, active le WebSocket (proxy_http_version 1.1, Upgrade, Connection "upgrade", sans lesquels l’interface temps réel de n8n ne fonctionne pas), fixe des timeouts de 300 secondes (les longs workflows peuvent durer des minutes) et désactive le buffering. La configuration finale ci-dessous reprend exactement ce bloc location.
Piège rencontré : nginx: [emerg] invalid number of arguments in "proxy_pass" vient de barres obliques inverses parasites avant les points-virgules, causées par un heredoc avec EOF sans guillemets. Utilisez <<'EOF' (avec guillemets) pour empêcher l’interprétation par le shell.
Bloc 21 : obtenir le certificat SSL
sudo certbot certonly --nginx \
-d yourdomain.com \
--non-interactive \
--agree-tos \
--email your-email@example.com
Certbot pose un fichier temporaire sous /.well-known/acme-challenge/ que Let’s Encrypt va chercher. Le certificat et la clé atterrissent dans /etc/letsencrypt/live/yourdomain.com/ (utilisez fullchain.pem dans Nginx). Échecs fréquents : domaine qui ne pointe pas vers EC2 (Connection refused, vérifiez avec dig), port 80 fermé dans le security group (Connection timed out), Nginx arrêté (sudo systemctl start nginx).
Bloc 22 : Nginx en HTTPS (final)
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:5678;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_buffering off;
proxy_request_buffering off;
}
}
server {
listen 80;
server_name yourdomain.com;
return 301 https://$host$request_uri;
}
Testez avec sudo nginx -t, puis curl -I https://yourdomain.com (attendu : 200 OK) et curl -I http://yourdomain.com (attendu : 301 vers HTTPS). Pour le renouvellement automatique, ajoutez un cron quotidien à 3 h (Certbot ne renouvelle que si l’expiration est à moins de 30 jours), puis testez avec certbot renew --dry-run :
(crontab -l 2>/dev/null; echo "0 3 * * * certbot renew --quiet --post-hook 'systemctl reload nginx'") | crontab -
Dépannage : les vrais problèmes rencontrés
- Erreur
libcrypt.so.1avec Docker Compose. Elle vient du binaire Compose v1 historique (basé sur Python). Compose v2+, écrit en Go, ne la rencontre pas. Correctif :sudo yum install -y libxcrypt-compat. - Mot de passe avec caractères spéciaux dans Zsh. Guillemets simples, jamais doubles :
PGPASSWORD='myPassword!123'. - Nginx « invalid number of arguments ». Retirez les barres obliques inverses avant les points-virgules et utilisez un heredoc entre guillemets simples.
- n8n ne démarre pas après 10 minutes. Lisez
sudo docker-compose logs n8n. Causes :ECONNREFUSEDvers RDS (security group sur le port 5432),permission denied for schema public(relancer le bloc 15), ouStored data could not be decrypted(cléN8N_ENCRYPTION_KEYchangée : restaurez l’originale, ou réinitialisez avecdocker-compose down -vau prix de toutes les données chiffrées). - Échec de la demande de certificat. Vérifiez dans l’ordre : le DNS pointe vers EC2, le port 80 est ouvert, Nginx répond en HTTP, le
server_namecorrespond exactement au domaine. - Déconnexions WebSocket dans l’interface (« Connection lost », workflows figés). En-têtes WebSocket absents ou timeout trop court : revérifiez
proxy_http_version 1.1,Upgrade,Connection "upgrade"etproxy_read_timeout 300s.
Gestion après installation
Commandes courantes, depuis ~/n8n-docker : docker-compose ps, logs -f n8n (ou n8n-worker), restart, down (les données restent dans les volumes) et up -d. Pour mettre à jour, lisez les notes de version, changez le tag 2.x.y épinglé dans docker-compose.yml, puis docker-compose pull et docker-compose up -d. Surveillez l’espace avec docker system df et df -h ; docker system prune -a --volumes supprime toutes les données Docker inutilisées, à manier avec précaution.
À la première visite de https://yourdomain.com, n8n demande de créer le compte admin (e-mail, prénom, nom, mot de passe).
Bonnes pratiques de sécurité
- Sauvegardez la clé de chiffrement tout de suite (gestionnaire de mots de passe, support chiffré).
- Restreignez le SSH à votre IP dans le security group d’EC2.
- Activez le chiffrement au repos de RDS dès la création de l’instance (chiffrement RDS). Pour une instance existante non chiffrée : copie chiffrée d’un snapshot, puis restauration.
- Planifiez des sauvegardes régulières : archive du dossier
n8n-dockeret snapshots RDS (aws rds create-db-snapshot). - Surveillez les logs d’erreurs de n8n.
Conclusion
Vous avez une installation n8n de production sur AWS EC2 : SSL avec renouvellement automatique, données persistantes sur RDS PostgreSQL, exécution distribuée par Redis et workers, reverse proxy Nginx avec WebSocket. L’isolation Docker, l’ajout de workers par simple édition du docker-compose.yml, les sauvegardes RDS et les redémarrages automatiques rendent l’ensemble évolutif, fiable et simple à maintenir. Prochaines étapes : créer un premier workflow, connecter des intégrations, automatiser les sauvegardes, explorer les nœuds AI Agent de n8n.
Sources
- Documentation n8n
- Référence Docker Compose
- Let’s Encrypt
- Guide de configuration Nginx
- n8n : mode queue
- n8n : changements majeurs de la 2.0
- AWS : SSL avec RDS
Pour aller plus loin, lisez la version complète de l’article (en anglais) : Installing N8N on AWS EC2: The Complete Production-Ready Guide.