n8n Installation sécurisée avec Traefik & PostgreSQL sur Docker
Ce tutoriel vous guidera pour déployer une instance n8n robuste, sécurisée et prête pour la production sur un serveur Ubuntu 24.x LTS. Nous utiliserons la pile technologique suivante :
Vous n’avez pas encore votre instance ? Créez un compte n8n puis déployez-la sur un VPS Contabo (excellent rapport qualité/prix pour l’auto-hébergement).
- n8n : La plateforme d’automatisation des workflows.
- PostgreSQL : Une base de données puissante pour vos données.
- Traefik : Un reverse proxy moderne pour la gestion HTTPS.
- Docker & Docker Compose : Pour conteneuriser nos services.
Table des Matières
1. Prérequis
Avant de commencer, assurez-vous de disposer des éléments suivants :
✅ Un serveur Linux (ce guide est spécifiquement adapté pour Ubuntu 24.x LTS).
✅ Un nom de domaine pointant vers l’adresse IP de votre serveur via un enregistrement DNS de type A.
✅ Les ports 80 (HTTP) et 443 (HTTPS) ouverts sur le pare-feu de votre serveur.
2. Configuration Initiale du Serveur (Ubuntu 24)
Ces étapes préparent votre serveur pour un environnement sécurisé et fonctionnel.
a. Création d’un utilisateur non-root (Sécurité)
Il est fortement déconseillé d’opérer en tant que root.
# Connectez-vous en root à votre serveur
# Remplacez 'votre_utilisateur' par le nom d'utilisateur de votre choix
adduser votre_utilisateur
# Donnez à cet utilisateur les privilèges 'sudo'
usermod -aG sudo votre_utilisateur
# Déconnectez-vous et reconnectez-vous avec votre nouvel utilisateur
su - votre_utilisateur
Toutes les commandes suivantes seront exécutées avec cet utilisateur.
b. Changement du nom d’hôte
# Remplacez 'votre_serveur' par le nom d'hôte de votre choix
# 1. Définir le nom d'hôte (immédiat et dans /etc/hostname)
sudo hostnamectl set-hostname votre_serveur
# 2. Mettre à jour la résolution locale
sudo nano /etc/hosts
# Modifiez/Assurez-vous que la ligne 127.0.1.1 pointe vers le nouveau nom :
# 127.0.0.1 localhost
# 127.0.1.1 votre_serveur
# 3. Assurer la persistance sur Contabo (cloud-init)
sudo nano /etc/cloud/cloud.cfg
hostname: votre_serveur
fqdn: votre_serveur
preserve_hostname: true (verif, seule cette ligne semble nécessaire)
manage_etc_hosts: false # Dire à cloud-init de ne JAMAIS toucher au fichier /etc/hosts.
# 4. Redémarrer pour valider la persistance
sudo reboot
Pour que le changement soit visible dans votre terminal, déconnectez-vous et reconnectez-vous.
c. Mises à jour du système et sécurité
# Mettez à jour les paquets manuellement une première fois
sudo apt update && sudo apt upgrade -y
# Installez le paquet pour les mises à jour automatiques
sudo apt install unattended-upgrades -y
# Activez et configurez les mises à jour automatiques (choisissez "Oui")
sudo dpkg-reconfigure --priority=low unattended-upgrades
d. Configuration du pare-feu (UFW)
sudo ufw allow 22/tcp # Port SSH
sudo ufw allow 80/tcp # Port HTTP pour Traefik
sudo ufw allow 443/tcp # Port HTTPS pour Traefik
sudo ufw enable # Activez le pare-feu
sleep 2
sudo ufw status # Vérifiez la configuration
e. Protection contre les attaques Brute Force (Fail2Ban)
sudo apt install fail2ban -y
sudo systemctl restart fail2ban
sleep 2
sudo fail2ban-client status
DNS Configuration
Par défaut, les serveurs VPS utilisent les DNS de Contabo, ce qui peut provoquer des erreurs getaddrinfo EAI_AGAIN. Pour corriger cela, passez aux DNS de Google :
sudo nano /etc/systemd/resolved.conf
Modifiez la section [Resolve] :
[Resolve]
DNS=8.8.8.8 8.8.4.4
FallbackDNS=1.1.1.1 9.9.9.9Forcez /etc/resolv.conf à pointer vers les DNS de Google et redémarrez le service :
sudo mv /etc/resolv.conf /etc/resolv.conf.old
echo -e "nameserver 8.8.8.8\nnameserver 8.8.4.4" | sudo tee /etc/resolv.conf
sudo chattr +i /etc/resolv.conf
systemctl restart systemd-resolvedVérifiez la configuration DNS :
resolvectl status
Installation de Docker et Docker Compose
# Créez un répertoire temporaire pour le script d'installation
mkdir ~/tmp
cd ~/tmp
# setup-docker-repository.sh
# Configures the official Docker APT repository on an Ubuntu system.
# Update the apt package index and install prerequisite packages.
sudo apt-get update
sudo apt-get install -y ca-certificates curl
# Add Docker's official GPG key.
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Set up the repository.
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Update the apt package index again with the new repository.
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Ajoutez votre utilisateur au groupe Docker pour lancer des commandes sans 'sudo'
sudo usermod -aG docker $USER
# --- IMPORTANT ---
# Vous devez vous déconnecter puis vous reconnecter pour que ce changement soit pris en compte.
# Après reconnexion, vérifiez que l'installation a réussi :
docker run hello-world
3. Création du Réseau Partagé
Pour que Traefik puisse communiquer avec n8n, ils doivent être sur le même réseau Docker.
docker network create traefik_proxy
4. Déploiement de Traefik 🚦
a. Préparation de l’environnement Traefik
# Créez et accédez au répertoire pour Traefik
mkdir ~/traefik
cd ~/traefik
touch docker-compose.yml
# Créez le fichier qui stockera les certificats SSL
touch acme.json
chmod 600 acme.json
# Créez le fichier .env pour Traefik
nano .env
Dans le fichier .env, collez la ligne suivante en remplaçant par votre email :
# Votre email pour la génération des certificats SSL Let's Encrypt
[email protected]
b. Création du fichier docker-compose.yml pour Traefik
Créez le fichier ~/traefik/docker-compose.yml et collez-y le contenu suivant.
nano docker-compose.ymlservices:
traefik:
image: traefik:v3
container_name: traefik
restart: always
ports:
- "80:80"
- "443:443"
command:
- "--api=false"
- "--api.insecure=false"
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --providers.docker.network=traefik_proxy
- --certificatesresolvers.letsencrypt.acme.httpchallenge=true
- --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
- --certificatesresolvers.letsencrypt.acme.email=${SSL_EMAIL}
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ./acme.json:/letsencrypt/acme.json
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- traefik_proxy
environment:
- TZ=Europe/Paris
networks:
traefik_proxy:
external: true
c. Lancement de Traefik
# Depuis le répertoire ~/traefik
docker compose up -d
5. Déploiement de n8n avec PostgreSQL 🚀
a. Préparation de l’environnement n8n
# Créez le répertoire de configuration pour n8n
mkdir ~/n8n
cd ~/n8n
# --- PRÉPARATION DU RÉPERTOIRE PARTAGÉ ET DES PERMISSIONS ---
# 1. Créer un groupe commun qui servira de pont entre n8n et l'utilisateur SFTP
sudo groupadd donnees_partagees
# 2. Créer le répertoire qui sera partagé
sudo mkdir -p /local-files/ftp
# 3. Appliquer les permissions au répertoire partagé
# Le propriétaire est l'utilisateur du conteneur (1000).
# Le groupe est notre groupe partagé.
sudo chown 1000:donnees_partagees /local-files/ftp
# Le mode "2775" est crucial :
# - '2' (setgid): force les nouveaux fichiers/dossiers créés à l'intérieur
# à hériter du groupe 'donnees_partagees'.
# - '775': donne les pleins droits au propriétaire (n8n) et au groupe (utilisateur SFTP),
# et un accès en lecture seule aux autres.
sudo chmod 2775 /local-files/ftp
sudo find /local-files/ftp -type d -exec chmod 2775 {} \;
sudo find /local-files/ftp -type f -exec chmod 664 {} \;
# Créez le fichier .env pour n8n
nano .env
Dans ce fichier .env, collez le contenu suivant et adaptez-le. Pour générer votre clé de chiffrement, exécutez openssl rand -base64 32.
# --- CONFIGURATION GÉNÉRALE ---
GENERIC_TIMEZONE=Europe/Paris
# --- DOMAINE N8N ---
N8N_HOSTNAME=n8n.exemple.com
# --- CLÉ DE CHIFFREMENT (TRÈS IMPORTANT) --- openssl rand -base64 32
N8N_ENCRYPTION_KEY=VOTRE_CLE_DE_CHIFFREMENT_A_CHANGER
# --- TASK RUNNERS ---
N8N_RUNNERS_ENABLED=true
# --- IDENTIFIANTS POSTGRESQL ---
POSTGRES_DB=n8n
POSTGRES_USER=n8n_user
POSTGRES_PASSWORD=VOTRE_MOT_DE_PASSE_TRES_SOLIDE_A_CHANGER
# --- SÉCURITÉ ET PERMISSIONS ---
N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
UID=1000
GID=1001
Trouvez l’ID de notre nouveau groupe avec la commande :
getent group donnees_partagees | cut -d: -f3Pour trouver votre UID et GID, exécutez id -u et id -g.
b. Création du fichier docker-compose.yml pour n8n
Créez le fichier ~/n8n/docker-compose.yml et collez-y cette configuration.
nano ~/n8n/docker-compose.ymlservices:
db:
image: postgres:15-alpine
container_name: n8n_postgres
restart: always
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- traefik_proxy
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 10
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: always
user: "${UID}:${GID}"
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=db
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
- DB_POSTGRESDB_USER=${POSTGRES_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
- DB_POSTGRESDB_SSL=false
- N8N_HOST=${N8N_HOSTNAME}
- N8N_PORT=5678
- N8N_PROTOCOL=https
- NODE_ENV=production
- EXECUTIONS_DATA_PRUNE=true
- EXECUTIONS_DATA_MAX_AGE=720
- WEBHOOK_URL=https://${N8N_HOSTNAME}/
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- N8N_RUNNERS_ENABLED=${N8N_RUNNERS_ENABLED}
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=${N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS}
- NODE_FUNCTION_ALLOW_EXTERNAL=*
- NODE_FUNCTION_ALLOW_BUILTIN=*
- NODE_PATH=/home/node/.n8n/node_modules
- N8N_PROXY_HOPS=1
volumes:
- n8n_data:/home/node/.n8n
- /local-files:/files
networks:
- traefik_proxy
depends_on:
db:
condition: service_healthy
labels:
- traefik.enable=true
- traefik.http.routers.n8n.rule=Host(`${N8N_HOSTNAME}`)
- traefik.http.routers.n8n.entrypoints=websecure
- traefik.http.routers.n8n.tls.certresolver=letsencrypt
- traefik.http.services.n8n.loadbalancer.server.port=5678
- traefik.http.routers.n8n.middlewares=n8n-secure-headers@docker
- traefik.http.middlewares.n8n-secure-headers.headers.forceSTSHeader=true
- traefik.http.middlewares.n8n-secure-headers.headers.stsSeconds=31536000
- traefik.http.middlewares.n8n-secure-headers.headers.stsIncludeSubdomains=true
- traefik.http.middlewares.n8n-secure-headers.headers.stsPreload=true
- traefik.http.middlewares.n8n-secure-headers.headers.contentTypeNosniff=true
- traefik.http.middlewares.n8n-secure-headers.headers.browserXssFilter=true
networks:
traefik_proxy:
external: true
volumes:
n8n_data:
postgres_data:
c. Lancement de n8n et PostgreSQL
# Depuis le répertoire ~/n8n
docker compose up -d
Options de confort
Couleurs ls / répertoires + Backup nano
dircolors -p | sed -e 's/^DIR 01;34/DIR 01;36/' -e 's/^OTHER_WRITABLE 34;42/OTHER_WRITABLE 01;36/' > ~/.dircolors && eval "$(dircolors -b ~/.dircolors)"
mkdir -p ~/.nano_backups; touch ~/.nanorc; grep -qxF 'set backup' ~/.nanorc || echo 'set backup' >> ~/.nanorc; grep -qxF 'set backupdir ~/.nano_backups' ~/.nanorc || echo 'set backupdir ~/.nano_backups' >> ~/.nanorc6. Configuration d’un accès SFTP sécurisé (Optionnel)
Cette section explique comment créer un accès SFTP sécurisé pour un utilisateur. Pour une sécurité maximale, l’utilisateur sera « emprisonné » (chroot) dans son propre répertoire personnel (/home/utilisateur_sftp). Un « pont » technique (mount --bind) sera ensuite créé pour lui donner accès au répertoire partagé /local-files/ftp depuis sa prison.
a. Création de l’utilisateur et ajout au groupe partagé
# 1. Créez un utilisateur standard (son /home sera sa prison)
sudo adduser utilisateur_sftp
# 2. Ajoutez cet utilisateur au groupe partagé pour lui donner les droits d'écriture
sudo usermod -aG donnees_partagees utilisateur_sftpb. Préparation de la prison « Chroot »
La prison de l’utilisateur sera son répertoire /home. Pour des raisons de sécurité, il doit appartenir à root.
# Le répertoire /home/utilisateur_sftp devient la prison et doit appartenir à root
sudo chown root:root /home/utilisateur_sftp
sudo chmod 755 /home/utilisateur_sftpc. Configuration du service SSH
Modifiez le fichier de configuration SSH pour appliquer la prison.
- Ouvrez le fichier :
sudo nano /etc/ssh/sshd_config - Ajoutez ce bloc à la toute fin du fichier :
# --- Configuration de la prison SFTP pour l'utilisateur ---
Match User utilisateur_sftp
ForceCommand internal-sftp
PasswordAuthentication yes
ChrootDirectory /home/utilisateur_sftp
PermitTunnel no
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding noTestez et appliquez la configuration :
sudo sshd -t
sudo systemctl restart sshd. Création du « pont » vers le répertoire partagé
Cette étape projette le répertoire partagé à l’intérieur de la prison de l’utilisateur.
- Créez un point de montage dans la prison :
sudo mkdir /home/utilisateur_sftp/partage_ftpRendez le montage permanent via /etc/fstab (pour qu’il survive à un redémarrage).
Attention : une erreur dans ce fichier peut empêcher le serveur de démarrer.
# 1. Sauvegardez le fichier fstab par sécurité
sudo cp /etc/fstab /etc/fstab.bak
# 2. Ouvrez fstab pour le modifier
sudo nano /etc/fstab
# 3. Ajoutez cette ligne à la toute fin du fichier
/local-files/ftp /home/utilisateur_sftp/partage_ftp none bind 0 0Appliquez le montage sans redémarrer :
Si la commande ne retourne aucune erreur, la configuration est réussie.
sudo mount -ae. Connexion avec un client SFTP (ex: FileZilla)
- Hôte : L’adresse IP de votre serveur
- Protocole :
SFTP - SSH File Transfer Protocol - Port : 22 (ou votre port SSH si vous l’avez changé)
- Identifiant :
utilisateur_sftp - Mot de passe : Celui que vous avez défini.
En cas d’ajout de nouveaux répertoires dans /local-files/ftp
sudo mkdir /local-files/ftp/nouveau_repertoire && sudo chmod 2775 /local-files/ftp/nouveau_repertoire
7. Sauvegarde et Maintenance
Pour une installation de production, une stratégie de sauvegarde et de maintenance est cruciale.
a. Stratégie de Sauvegarde
Pensez à sauvegarder régulièrement les éléments suivants :
- Base de données PostgreSQL : Contient tous vos workflows et exécutions.
- Configuration et données n8n : Inclut les identifiants chiffrés.
- Certificats Traefik : Pour éviter d’atteindre les limites de Let’s Encrypt lors d’une restauration.
⚠️ Si /files/ftp/backup/backup.lock (/local-files/ftp/backup/backup.lock depuis le container) est présent la sauvegarde est interrompue.
Exemple de script de sauvegarde :
mkdir ~/backup
cd ~/backup
nano backup_n8n.sh
chmod 700 backup_n8n.sh#!/bin/bash
# ==============================================================================
# --- INITIALISATION ---
# ==============================================================================
# Trouve le répertoire où se trouve le script lui-même
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
ENV_FILE="${SCRIPT_DIR}/.env"
# Vérifie la présence du fichier .env à côté du script
if [ ! -f "$ENV_FILE" ]; then
echo "ERREUR: Fichier .env introuvable dans ${SCRIPT_DIR}" >&2
exit 1
fi
# Charge toutes les variables depuis le fichier .env
set -a
source "$ENV_FILE"
set +a
# Vérifie que la variable la plus importante a bien été chargée
if [ -z "$N8N_PROJECT_PATH" ]; then
echo "ERREUR: La variable N8N_PROJECT_PATH n'est pas définie dans le fichier .env." >&2
exit 1
fi
# ==============================================================================
# --- FONCTIONS UTILES ---
# ==============================================================================
# Fonction pour envoyer des notifications à Discord
send_discord_notification() {
local status="$1"
local title="$2"
local message="$3"
if [ -z "${DISCORD_WEBHOOK_URL}" ]; then return; fi
if [ "$status" == "success" ]; then
local color=3066993 # Vert
local ping_content=""
else # "failure" ou autre
local color=15158332 # Rouge
local ping_content="@everyone"
fi
local json_payload
json_payload=$(cat <<EOF
{
"content": "$ping_content",
"username": "Sauvegarde ${SERVER_HOSTNAME}",
"avatar_url": "https://i.imgur.com/4M34hi2.png",
"embeds": [{
"title": "$title",
"description": "$message",
"color": $color
}],
"allowed_mentions": {
"parse": ["everyone"]
}
}
EOF
)
curl -s -H "Content-Type: application/json" -X POST -d "$json_payload" "$DISCORD_WEBHOOK_URL" > /dev/null
}
# ==============================================================================
# --- DÉBUT DU SCRIPT ---
# ==============================================================================
# Se placer dans le répertoire du projet n8n. C'est l'étape la plus importante.
cd "${N8N_PROJECT_PATH}" || { echo "ERREUR: Le dossier du projet n8n '${N8N_PROJECT_PATH}' est introuvable." >&2; exit 1; }
# Définir le nom du projet à partir du nom du dossier
PROJECT_NAME=$(basename "$PWD")
# Récupérer le nom de l'hôte pour l'utiliser dans les notifications
SERVER_HOSTNAME=$(hostname)
# Créer les dossiers nécessaires s'ils n'existent pas
mkdir -p "$BACKUP_STORAGE_PATH"
mkdir -p "${LOCAL_FILES_PATH}/ftp/backup"
# 1. VÉRIFIER L'ABSENCE DE TRAITEMENT EN COURS (VIA FICHIER LOCK)
LOCK_FILE_FULL_PATH="${LOCAL_FILES_PATH}/ftp/backup/${LOCK_FILE_NAME}"
echo "Vérification de l'absence de traitement en cours..."
if [ -f "$LOCK_FILE_FULL_PATH" ]; then
MSG="Sauvegarde impossible, un traitement est en cours (fichier de verrouillage détecté)."
echo "ERREUR: $MSG"
send_discord_notification "failure" "ℹ️ Sauvegarde Annulée" "$MSG"
exit 1
else
echo "Aucun traitement en cours détecté."
fi
# VÉRIFIER LA PRÉSENCE DES FICHIERS DE CONFIGURATION DANS LE DOSSIER PROJET
if [ ! -f "docker-compose.yml" ] || [ ! -f ".env" ]; then
MSG="Sauvegarde impossible : 'docker-compose.yml' ou '.env' est manquant dans ${N8N_PROJECT_PATH}."
echo "ERREUR: $MSG"
send_discord_notification "failure" "❌ Échec Critique de la Sauvegarde" "$MSG"
exit 1
fi
echo "Fichiers de configuration trouvés."
# 2. Arrêter les services (docker compose est exécuté depuis le bon dossier)
echo "Arrêt des conteneurs..."
docker compose stop
if [ $? -ne 0 ]; then
send_discord_notification "failure" "❌ Échec Critique de la Sauvegarde" "Impossible d'arrêter les conteneurs."
exit 1
fi
# 3. Créer l'archive
SNAPSHOT_NAME="${PROJECT_NAME}_$(date +%Y-%m-%dT%H%M).tar.gz"
echo "Création de la sauvegarde : ${SNAPSHOT_NAME}"
FULL_N8N_VOLUME="${PROJECT_NAME}_${N8N_VOLUME_NAME}"
FULL_POSTGRES_VOLUME="${PROJECT_NAME}_${POSTGRES_VOLUME_NAME}"
if ! docker volume inspect "$FULL_N8N_VOLUME" > /dev/null 2>&1 || ! docker volume inspect "$FULL_POSTGRES_VOLUME" > /dev/null 2>&1; then
send_discord_notification "failure" "❌ Échec Critique de la Sauvegarde" "Un ou plusieurs volumes Docker sont introuvables."
docker compose up -d
exit 1
fi
docker run --rm \
-v "${FULL_N8N_VOLUME}:/backup-source/n8n_data:ro" \
-v "${FULL_POSTGRES_VOLUME}:/backup-source/postgres_data:ro" \
-v "${LOCAL_FILES_PATH}:/backup-source/local-files:ro" \
-v "$(pwd)/docker-compose.yml":/backup-source/docker-compose.yml:ro \
-v "$(pwd)/.env":/backup-source/.env:ro \
-v "${BACKUP_STORAGE_PATH}":/backup-dest \
busybox tar -czf "/backup-dest/${SNAPSHOT_NAME}" --exclude='tmp' --exclude='*.gz' -C /backup-source .
if [ $? -ne 0 ]; then
send_discord_notification "failure" "❌ Échec Critique de la Sauvegarde" "La création de l'archive a échoué."
docker compose up -d
exit 1
fi
echo "Sauvegarde créée avec succès."
# 4. Redémarrer les services
echo "Correction des permissions sur /local-files/ftp..."
if [ -d "${LOCAL_FILES_PATH}/ftp" ]; then
find "${LOCAL_FILES_PATH}/ftp" -type d -exec chmod 2775 {} \;
find "${LOCAL_FILES_PATH}/ftp" -type f -exec chmod 664 {} \;
fi
echo "Redémarrage des conteneurs..."
docker compose up -d
# 5. Gérer la rétention
echo "Nettoyage des anciennes sauvegardes..."
find "${BACKUP_STORAGE_PATH}" -name "${PROJECT_NAME}_*.tar.gz" -type f -mtime +"$((RETENTION_DAYS - 1))" -delete
# 6. Envoyer la notification de succès
send_discord_notification "success" "✅ Sauvegarde Réussie" "La sauvegarde a été effectuée avec succès et inclut les fichiers de configuration.\\n**Fichier :** \`${SNAPSHOT_NAME}\`"
echo "Processus de sauvegarde terminé avec succès."Paramètres .env (pour les notifs)
# --- CHEMINS IMPORTANTS ---
N8N_PROJECT_PATH="/home/phil/n8n"
# Indiquez le chemin absolu où les archives de sauvegarde seront stockées
BACKUP_STORAGE_PATH="/opt/n8n_backups"
# --- Configuration du Fichier de Verrouillage ---
LOCK_FILE_NAME="backup.lock"
LOCAL_FILES_PATH="/local-files"
# --- Configuration générale de la sauvegarde ---
N8N_VOLUME_NAME="n8n_data"
POSTGRES_VOLUME_NAME="postgres_data"
RETENTION_DAYS=30
# --- Notifications Discord ---
DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/xxx"Vous pouvez automatiser ce script avec une tâche cron.
(crontab -l 2>/dev/null; echo "0 3 * * * /home/phil/backup/backup_n8n.sh") | crontab -b. Restauration
Cette procédure explique comment restaurer une sauvegarde sur un nouveau serveur, en écrasant une éventuelle installation existante.
Phase 1 : Préparation
- Transférez votre archive de sauvegarde (
.tar.gz) sur le serveur de destination, par exemple dans/opt/n8n_backups/en utilisantscp.
Phase 2 : Destruction de l’ancienne instance (Optionnel) Cette phase n’est nécessaire que si vous remplacez une instance existante.
- Placez-vous dans le dossier du projet n8n à effacer :
cd /chemin/vers/ancien/n8n - Arrêtez et supprimez complètement l’environnement (conteneurs ET volumes de données).
⚠️ Attention : Cette action est irréversible.
docker compose down -v
Phase 3 : Restauration
- Créez un dossier propre pour votre nouvelle instance et placez-vous à l’intérieur.
mkdir -p /root/n8n cd /root/n8n - Extrayez l’intégralité de l’archive dans ce nouveau dossier.Bash
# Adaptez le chemin et le nom de l'archive tar zxf /opt/n8n_backups/votre_archive.tar.gz - Maintenant que
docker-compose.ymlest présent, créez les volumes Docker vides.docker compose up -d docker compose stop - Restaurez les données de l’archive dans les volumes Docker fraîchement créés.
# Adaptez les noms des volumes et de l'archive
docker run --rm \ -v n8n_n8n_data:/restore-dest/n8n_data \ -v n8n_postgres_data:/restore-dest/postgres_data \ -v "$(pwd)/local-files":/restore-dest/local-files \ -v /opt/n8n_backups:/backup-source:ro \ busybox tar xzf "/backup-source/votre_archive.tar.gz" -C /restore-dest
Phase 4 : Finalisation
- Corrigez les permissions des fichiers restaurés. C’est une étape cruciale pour éviter les erreurs.
docker run --rm -v n8n_n8n_data:/data busybox chown -R 1000:1000 /data - Démarrez votre instance n8n restaurée !Bash
docker compose up -d - Vérifiez les logs (
docker compose logs -f n8n) et accédez à l’interface via votre navigateur pour confirmer que tout est en ordre.
c. test de restauration
Cette procédure utilise un script automatisé pour tester une sauvegarde n8n de manière rapide, fiable et totalement isolée de votre environnement de production.
Le script gère la création d’un environnement de test (dans un dossier séparé, avec un port et des volumes distincts), sa vérification, et son nettoyage complet et interactif. Il ne modifiera jamais votre instance de production.
Étape 1 : Créer le script de test
Créez le fichier suivant, par exemple dans votre dossier ~/backup/ :
nano test_restore.sh
chmod +x test_restore.sh
Collez-y le contenu suivant :
#!/bin/bash
#
# test_restore.sh
# Purpose: Reliable and simple test restore for n8n in an isolated environment.
#
# Usage: ./test_restore.sh /path/to/your/backup.tar.gz
#
# This script performs a restore in an isolated environment to never
# impact the production instance. It automatically cleans up
# previous tests and provides a fully automated verify/cleanup cycle.
#
set -e # Stop the script immediately if a command fails
# ==============================================================================
# --- CONFIGURATION ---
# ==============================================================================
# Directory where the test restore will be performed (resolves '~')
TEST_DIR="~/n8n_restored_instance"
# Docker project name to isolate test containers and volumes
TEST_PROJECT_NAME="n8nrestored"
# Original n8n port in the docker-compose.yml
PROD_PORT="5678"
# New port for the test instance (ensure it is free)
TEST_PORT="5679"
# Names of the volumes as defined in the compose file (relative, not full name)
N8N_VOLUME_NAME="n8n_data"
POSTGRES_VOLUME_NAME="postgres_data"
# Verification settings
VERIFY_TIMEOUT_SECONDS=120 # 2 minutes
VERIFY_POLL_INTERVAL=5 # 5 seconds
# ==============================================================================
# --- ARGUMENT VALIDATION ---
if [ -z "$1" ]; then
echo "ERROR: You must provide the path to the backup file as an argument." >&2
echo "Usage: $0 /path/to/your/backup.tar.gz" >&2
exit 1
fi
BACKUP_FILE_PATH=$(realpath "$1") # Get absolute path
if [ ! -f "$BACKUP_FILE_PATH" ]; then
echo "ERROR: Backup file '${BACKUP_FILE_PATH}' not found." >&2
exit 1
fi
# --- SCRIPT START ---
echo "### STARTING N8N TEST RESTORE ###"
# Resolve the test directory path (handles '~')
EVAL_TEST_DIR=$(eval echo "$TEST_DIR")
BACKUP_FILENAME=$(basename "$BACKUP_FILE_PATH")
SERVER_IP=$(hostname -I | awk '{print $1}')
TEST_URL="http://${SERVER_IP}:${TEST_PORT}"
# 0. Preventive cleanup of any previous test environment
echo "[0/6] Cleaning up any previous test environment..."
if [ -f "${EVAL_TEST_DIR}/docker-compose.yml" ]; then
cd "$EVAL_TEST_DIR"
echo " Shutting down old test containers..."
docker compose -p "$TEST_PROJECT_NAME" down -v --remove-orphans > /dev/null 2>&1 || true
cd ~ # Go back home for deletion
fi
echo " Deleting old test directory..."
rm -rf "$EVAL_TEST_DIR"
echo " Test environment is clean."
# 1. Prepare new test environment
echo "[1/6] Preparing new environment in ${EVAL_TEST_DIR}..."
mkdir -p "$EVAL_TEST_DIR"
cd "$EVAL_TEST_DIR"
echo " Extracting archive (this may take a moment)..."
tar -xzf "$BACKUP_FILE_PATH"
# 2. Adapt configuration files
echo "[2/6] Adapting configuration for isolation..."
echo " Neutralizing fixed container names in docker-compose.yml..."
# Comment out any 'container_name' directive to prevent conflicts
sed -i -E 's/^\s*(container_name:.*)/#\1/g' docker-compose.yml
echo " Generating port and environment override file..."
# This override file only changes the port and adds the secure cookie setting.
cat << EOF > docker-compose.test-override.yml
# This file is auto-generated by test_restore.sh
# It isolates the test environment, overriding ports and env vars.
services:
n8n: # Assuming 'n8n' is the service name
ports:
- "${TEST_PORT}:${PROD_PORT}" # Map to new test port
environment:
- N8N_SECURE_COOKIE=false # Disable secure cookie for easy HTTP testing
EOF
echo " Configuration docker-compose.test-override.yml created."
# 3. Create and populate new test volumes
echo "[3/6] Creating and populating test Docker volumes..."
FULL_N8N_VOLUME="${TEST_PROJECT_NAME}_${N8N_VOLUME_NAME}"
FULL_POSTGRES_VOLUME="${TEST_PROJECT_NAME}_${POSTGRES_VOLUME_NAME}"
docker volume create "$FULL_N8N_VOLUME" > /dev/null
docker volume create "$FULL_POSTGRES_VOLUME" > /dev/null
# Copy data from extracted folders into the new named volumes
docker run --rm -v "$(pwd)/${N8N_VOLUME_NAME}:/src:ro" -v "$FULL_N8N_VOLUME:/dest" busybox cp -a /src/. /dest/
docker run --rm -v "$(pwd)/${POSTGRES_VOLUME_NAME}:/src:ro" -v "$FULL_POSTGRES_VOLUME:/dest" busybox cp -a /src/. /dest/
echo " Test volumes are ready."
# 4. Start the restored instance
echo "[4/6] Starting test containers (Project: ${TEST_PROJECT_NAME})..."
# Use both files: the modified original and the override
docker compose -p "$TEST_PROJECT_NAME" -f docker-compose.yml -f docker-compose.test-override.yml up -d
# 5. Automated verification
echo "[5/6] Verifying test instance at ${TEST_URL}..."
echo " Waiting for n8n service to become available (Timeout: ${VERIFY_TIMEOUT_SECONDS}s)."
SECONDS=0
while true; do
# We curl 'localhost' as it's faster and avoids firewall issues if script is run locally
if curl -s --fail "http://localhost:${TEST_PORT}" > /dev/null; then
echo ""
echo "############################################################"
echo "### SUCCESS: Test instance is LIVE! ###"
echo "############################################################"
echo " => Instance is accessible at: ${TEST_URL}"
echo " => (Verified via http://localhost:${TEST_PORT})"
echo ""
VERIFIED=true
break
fi
if [ $SECONDS -ge $VERIFY_TIMEOUT_SECONDS ]; then
echo ""
echo "############################################################"
echo "### FAILURE: Test instance FAILED to start. ###"
echo "############################################################"
echo " => Could not connect to http://localhost:${TEST_PORT} after ${VERIFY_TIMEOUT_SECONDS}s."
echo " => Check logs with: cd ${EVAL_TEST_DIR} && docker compose -p ${TEST_PROJECT_NAME} logs -f n8n"
echo ""
VERIFIED=false
break
fi
sleep $VERIFY_POLL_INTERVAL
SECONDS=$((SECONDS + VERIFY_POLL_INTERVAL))
echo -n "."
done
# 6. Interactive Cleanup
echo "[6/6] Cleanup"
if [ "$VERIFIED" = true ]; then
echo "You can now manually verify the instance in your browser at ${TEST_URL}"
echo "NOTE: If ${SERVER_IP} is not accessible, you may need to open the firewall:"
echo "sudo ufw allow ${TEST_PORT}/tcp"
fi
echo ""
read -p "Press [Enter] to SHUT DOWN and DELETE the test environment..."
echo " Shutting down and deleting test containers, volumes, and network..."
# We are already in the correct directory (EVAL_TEST_DIR)
docker compose -p "$TEST_PROJECT_NAME" -f docker-compose.yml -f docker-compose.test-override.yml down -v --remove-orphans
cd ~ # Go back home to delete the directory
rm -rf "$EVAL_TEST_DIR"
echo "### TEST RESTORE PROCESS COMPLETE. SYSTEM IS CLEAN. ###"
Étape 2 : Lancer le test
Exécutez le script en lui donnant le chemin complet vers votre fichier .tar.gz :
./test_restore.sh /opt/n8n_backups/votre_archive.tar.gz
Étape 3 : Vérification et Nettoyage
Le script va maintenant s’exécuter et automatiser tout le processus :
- Nettoyage : Il supprime tout ancien environnement de test (
n8nrestored). - Extraction : Il crée un dossier (
~/n8n_restored_instance) et y extrait l’archive. - Isolation : Il commente les
container_nameet crée un fichierdocker-compose.test-override.ymlpour changer le port et désactiverN8N_SECURE_COOKIE. - Restauration : Il crée de nouveaux volumes Docker (préfixés
n8nrestored_) et y copie les données. - Démarrage : Il lance les conteneurs de test.
- Vérification : Il tente de contacter l’instance de test sur le nouveau port (ex:
5679).- En cas d’échec : Il affiche un message d’erreur et les commandes pour consulter les logs.
- En cas de succès : Il confirme que l’instance est en ligne.
- Nettoyage interactif : Le script se met en pause et attend votre confirmation (touche [Entrée]).
- À ce stade, vous pouvez vérifier manuellement l’URL
http://<VOTRE_IP>:<PORT_TEST>dans votre navigateur (pensez à ouvrir le port sur votre pare-feu si nécessaire :sudo ufw allow 5679/tcp). - Une fois votre vérification manuelle terminée, appuyez sur [Entrée] dans le terminal. Le script supprimera alors automatiquement et proprement toutes les ressources de test (conteneurs, volumes, réseaux et le dossier d’extraction).
- À ce stade, vous pouvez vérifier manuellement l’URL
8. Gestion des Logs Docker
Par défaut, les logs Docker peuvent saturer votre disque. Il est recommandé de configurer la rotation des logs.
Créez ou modifiez le fichier /etc/docker/daemon.json :
sudo nano /etc/docker/daemon.json
Ajoutez le contenu suivant :
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
Cela limite les logs de chaque conteneur à 3 fichiers de 10 Mo chacun. Redémarrez Docker pour appliquer les changements :
sudo systemctl restart docker
9. Accès et Finalisation
🎉 C’est terminé !
Ouvrez votre navigateur et rendez-vous sur votre URL (ex: https://n8n.exemple.com). Vous devriez voir l’interface n8n sécurisée par HTTPS. Créez votre compte « Owner » pour commencer à automatiser.
Vous avez déployé avec succès une pile d’automatisation complète, sécurisée et alignée sur les meilleures pratiques.



