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 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 :

Prérequis

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

  1. Oh-My-Zsh (facultatif) : autocomplétion Docker, AWS CLI et Git.
  2. Mise à jour système : sudo yum update -y. Les mises à jour d’Amazon Linux apportent correctifs de sécurité et corrections de dépendances.
  3. Nginx : yum install, systemctl start et enable. À ce stade, http://your-ec2-ip doit afficher la page d’accueil par défaut de Nginx.
  4. 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).
  5. Cron : cronie pour le renouvellement SSL. AL2023 recommande les timers systemd, mais cronie fonctionne toujours (paquets dépréciés d’AL2023).
  6. Docker : yum install docker, démarrage au boot, puis usermod -aG docker $USER. Ce dernier ne prend effet qu’après déconnexion/reconnexion : d’ici là, utilisez sudo docker.
  7. 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 compose avec un espace, voir installer le plugin Compose).
  8. Client PostgreSQL : yum install -y postgresql15, pour tester RDS, créer la base et déboguer.
  9. Python, Git : requis par Certbot et utiles ensuite.
  10. Node.js et PM2 : pas indispensables dans une architecture Docker, car n8n tourne dans des conteneurs. Le restart: always de 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 :

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

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

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é

  1. Sauvegardez la clé de chiffrement tout de suite (gestionnaire de mots de passe, support chiffré).
  2. Restreignez le SSH à votre IP dans le security group d’EC2.
  3. 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.
  4. Planifiez des sauvegardes régulières : archive du dossier n8n-docker et snapshots RDS (aws rds create-db-snapshot).
  5. 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

Pour aller plus loin, lisez la version complète de l’article (en anglais) : Installing N8N on AWS EC2: The Complete Production-Ready Guide.

Vous doutez de votre tracking ?

Réserver un appel Ou commencez par l’audit analytics gratuit →