Installare n8n con Docker Compose su un VPS
Configura n8n 2.x con Docker Compose e PostgreSQL su un VPS. Pronto per la produzione fin da subito con versioni fissate, backup della chiave di cifratura, health check e limiti di risorse.
Questa guida installa n8n 2.12.3 con Docker Compose e PostgreSQL su un VPS. Avrai un'istanza n8n funzionante in circa 15 minuti. La configurazione usa impostazioni di produzione fin da subito: versioni delle immagini fissate, credenziali cifrate, porta vincolata solo a localhost, health check su entrambi i container e limiti di risorse Docker. Niente reverse proxy o SSL qui. Questo viene trattato in .
Di cosa ho bisogno prima di installare n8n?
Serve un VPS con Debian 12 o Ubuntu 24.04 e almeno 4 GB di RAM. Docker e Docker Compose (il plugin docker compose, non il vecchio binario docker-compose) devono essere installati. Se Docker non è ancora configurato, segui prima Docker Compose per deployment multi-servizio su VPS.
Verifica che Docker Compose sia disponibile:
docker compose version
Output atteso:
Docker Compose version v2.x.x
Se questo comando fallisce con docker: 'compose' is not a docker command, hai il vecchio binario standalone. Installa il plugin Docker Compose dal repository ufficiale Docker.
Serve anche un utente non-root con accesso sudo. Tutti i comandi di questa guida vengono eseguiti con quell'utente, non come root.
Perché usare PostgreSQL invece di SQLite per n8n?
PostgreSQL gestisce le connessioni concorrenti, supporta il WAL per il ripristino dopo crash e funziona con pg_dump per backup a caldo mentre n8n è in esecuzione. SQLite blocca l'intero file del database a ogni scrittura. Con esecuzioni concorrenti di webhook, questo causa timeout e corruzione dei dati. Non è possibile effettuare un backup sicuro di un database SQLite mentre n8n è in esecuzione senza rischiare una copia corrotta. Per qualsiasi uso oltre i test locali, PostgreSQL è la scelta giusta.
| Caratteristica | SQLite | PostgreSQL |
|---|---|---|
| Scritture concorrenti | Lock a singolo scrittore | MVCC completo |
| Backup a caldo | Non sicuro in esecuzione | pg_dump in qualsiasi momento |
| Ripristino dopo crash | Replay manuale del journal | Replay automatico del WAL |
| Scalabilità | Singolo processo | Connection pooling |
| Variabile d'ambiente n8n | DB_TYPE=sqlite |
DB_TYPE=postgresdb |
Come installo n8n con Docker Compose su un VPS?
Crea una directory di progetto, scrivi un file .env con i tuoi segreti, scrivi il docker-compose.yml, avvia lo stack e verifica che tutto sia sano. Ogni passaggio include una verifica.
Creare la directory del progetto
mkdir -p ~/n8n && cd ~/n8n
Creare il file .env
Tutti i segreti vanno in un file .env. Non inserire mai password o chiavi direttamente in docker-compose.yml.
Genera una password del database forte e la chiave di cifratura n8n:
echo "POSTGRES_PASSWORD=$(openssl rand -base64 32)" >> .env
echo "N8N_ENCRYPTION_KEY=$(openssl rand -hex 32)" >> .env
Aggiungi le variabili rimanenti:
cat >> .env << 'EOF'
# PostgreSQL
POSTGRES_USER=n8n
POSTGRES_DB=n8n
# n8n
N8N_VERSION=2.12.3
N8N_HOST=localhost
N8N_PORT=5678
N8N_PROTOCOL=http
N8N_DIAGNOSTICS_ENABLED=false
GENERIC_TIMEZONE=UTC
EOF
Limita i permessi del file. Solo il tuo utente deve poterlo leggere:
chmod 600 .env
Verifica:
ls -la .env
Dovresti vedere -rw-------. Nessun altro utente sul server può leggere la tua password del database o la tua chiave di cifratura.
Come genero e salvo la chiave di cifratura n8n?
Il N8N_ENCRYPTION_KEY è stato generato sopra con openssl rand -hex 32. Questo produce una chiave casuale di 32 byte (64 caratteri esadecimali). n8n usa questa chiave per cifrare tutte le credenziali che memorizzi: chiavi API, token OAuth, password di database nei workflow. Se perdi questa chiave, tutte le credenziali memorizzate diventano permanentemente illeggibili. Non esiste alcun meccanismo di recupero.
Salva la chiave di cifratura adesso. Copiala in un gestore di password o in un vault offline:
grep N8N_ENCRYPTION_KEY .env
Conserva l'output in un posto sicuro al di fuori di questo server. Fallo prima di aggiungere la tua prima credenziale in n8n.
Riferimento delle variabili d'ambiente
| Variabile | Scopo | Valore di esempio |
|---|---|---|
POSTGRES_USER |
Nome del superutente PostgreSQL | n8n |
POSTGRES_PASSWORD |
Password del superutente PostgreSQL | (generata, 32+ caratteri) |
POSTGRES_DB |
Nome del database | n8n |
N8N_VERSION |
Tag dell'immagine n8n fissato | 2.12.3 |
N8N_ENCRYPTION_KEY |
Cifra le credenziali memorizzate | (generata, 64 caratteri hex) |
N8N_HOST |
Host per l'interfaccia n8n | localhost |
N8N_PORT |
Porta per l'interfaccia n8n | 5678 |
N8N_PROTOCOL |
HTTP o HTTPS | http |
N8N_DIAGNOSTICS_ENABLED |
Inviare telemetria a n8n | false |
GENERIC_TIMEZONE |
Fuso orario per i trigger cron | UTC |
Scrivere il docker-compose.yml
cat > docker-compose.yml << 'COMPOSE'
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
deploy:
resources:
limits:
memory: 512M
cpus: "1.0"
security_opt:
- no-new-privileges:true
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
n8n:
image: docker.n8n.io/n8nio/n8n:${N8N_VERSION}
restart: unless-stopped
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: 5432
DB_POSTGRESDB_DATABASE: ${POSTGRES_DB}
DB_POSTGRESDB_USER: ${POSTGRES_USER}
DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
N8N_HOST: ${N8N_HOST}
N8N_PORT: ${N8N_PORT}
N8N_PROTOCOL: ${N8N_PROTOCOL}
N8N_DIAGNOSTICS_ENABLED: ${N8N_DIAGNOSTICS_ENABLED}
GENERIC_TIMEZONE: ${GENERIC_TIMEZONE}
ports:
- "127.0.0.1:5678:5678"
volumes:
- n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:5678/healthz || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
deploy:
resources:
limits:
memory: 2G
cpus: "2.0"
security_opt:
- no-new-privileges:true
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
volumes:
postgres_data:
n8n_data:
COMPOSE
Alcuni punti da notare in questo file:
Nessuna chiave version:. Docker Compose V2 la ignora. Tutti i tutorial della concorrenza includono ancora version: '3.7' o version: '3.8'. La specifica Compose ha deprecato questo campo. Includerlo produce un warning nelle versioni attuali di Docker.
Porta vincolata a 127.0.0.1. La riga "127.0.0.1:5678:5678" restringe n8n a localhost. È un requisito di sicurezza, non una preferenza. I mapping delle porte Docker aggirano completamente iptables e le regole UFW. Se scrivi 5678:5678 senza il prefisso 127.0.0.1, n8n è accessibile da internet anche se il tuo firewall blocca la porta 5678. Un reverse proxy sulla stessa macchina inoltrerà il traffico a localhost:5678 una volta configurato.
security_opt: no-new-privileges:true impedisce ai processi all'interno del container di acquisire privilegi aggiuntivi tramite binari setuid o setgid. È una misura di difesa in profondità contro attacchi di escape dal container.
Rotazione dei log. Il blocco logging limita il log JSON di ogni container a 3 file da 10 MB ciascuno (30 MB massimo per servizio). Senza questa configurazione, i log Docker crescono fino a riempire il disco. Su un VPS con spazio limitato, questo conta.
Health check su entrambi i servizi. PostgreSQL usa pg_isready. n8n usa il suo endpoint /healthz. La condizione depends_on assicura che n8n parta solo dopo che PostgreSQL ha superato il suo health check.
Limiti di risorse. PostgreSQL ottiene 512 MB di RAM e 1 CPU. n8n ottiene 2 GB di RAM e 2 CPU. Questi valori funzionano bene su un VPS da 4-8 GB. Regolali in base alla dimensione del server e alla complessità dei workflow.
Volumi con nome. Sia postgres_data che n8n_data sono volumi con nome gestiti da Docker. Docker gestisce automaticamente proprietà e permessi all'interno del volume. Non serve creare directory sull'host né correggere i permessi manualmente.
restart: unless-stopped invece di restart: always. Entrambi riavviano dopo un crash, ma unless-stopped rispetta i comandi manuali docker compose stop. Con restart: always, un container fermato manualmente si riavvia se il daemon Docker si riavvia (ad esempio dopo un aggiornamento di sistema).
Avviare lo stack
cd ~/n8n
docker compose up -d
Osserva i log durante il primo avvio:
docker compose logs -f
Attendi che n8n mostri una riga contenente n8n ready on. Premi Ctrl+C per uscire dal visualizzatore di log.
Verificare l'installazione
Controlla che entrambi i container siano in esecuzione e sani:
docker compose ps
Output atteso:
NAME IMAGE ... STATUS PORTS
n8n-n8n-1 docker.n8n.io/n8nio/n8n:2.12.3 ... Up X minutes (healthy) 127.0.0.1:5678->5678/tcp
n8n-postgres-1 postgres:16-alpine ... Up X minutes (healthy)
Fai attenzione: entrambi i container mostrano (healthy) nella colonna STATUS. Questo conferma che gli health check stanno passando. Se vedi (health: starting), attendi 30 secondi e controlla di nuovo.
Testa l'API n8n dal server:
curl -s http://localhost:5678/healthz
Output atteso:
{"status":"ok"}
Verifica che la porta sia in ascolto solo su localhost, non su tutte le interfacce:
ss -tlnp | grep 5678
Dovresti vedere 127.0.0.1:5678 nell'output. Se vedi 0.0.0.0:5678, il binding della porta è sbagliato. Ferma lo stack, correggi la riga ports in docker-compose.yml e riavvia.
Come creo l'account proprietario di n8n?
Apri un tunnel SSH dalla tua macchina locale per accedere a n8n dal browser:
ssh -L 5678:127.0.0.1:5678 your-user@your-server-ip
Apri http://localhost:5678 nel browser. n8n mostra una schermata di configurazione al primo accesso. Crea il tuo account proprietario con una password forte. Questo account ha accesso amministratore completo a n8n.
Dopo aver creato l'account, chiudi il tunnel SSH. Non lasciare la porta 5678 in tunnel più del necessario. Configura un reverse proxy con SSL per l'accesso regolare. Vedi .
Come verifico che n8n funzioni correttamente?
Dopo aver creato il tuo account e chiuso il tunnel SSH, esegui un'ultima serie di controlli dal server.
Controlla la salute dei container:
docker compose ps --format "table {{.Name}}\t{{.Status}}"
Entrambi i servizi dovrebbero mostrare (healthy).
Controlla i log di n8n per errori:
docker compose logs n8n --tail 20
Cerca righe ERROR. Un avvio pulito mostra messaggi di migrazione del database seguiti da n8n ready on.
Controlla i log di PostgreSQL:
docker compose logs postgres --tail 10
Dovresti vedere database system is ready to accept connections.
Controlla l'uso disco dei tuoi volumi Docker:
docker system df -v | grep -E "n8n|postgres"
Questo ti dice quanto spazio stanno usando n8n e PostgreSQL. Controllalo periodicamente su istanze VPS con spazio limitato.
Qualcosa è andato storto?
Il container si chiude immediatamente. Controlla i log con docker compose logs n8n. Cause comuni: file .env mancante, password PostgreSQL sbagliata, o la chiave di cifratura contiene caratteri speciali che rompono l'espansione della shell. Rigenera il tuo .env se necessario.
Errori di permessi. Il container n8n gira come UID 1000. Se passi da volumi con nome a bind mount, assicurati che la directory host appartenga all'UID 1000: sudo chown -R 1000:1000 ./n8n_data.
Health check fallito. n8n ha bisogno di 20-30 secondi per avviarsi al primo boot mentre esegue le migrazioni del database. Se gli health check falliscono, controlla docker compose logs n8n per errori di migrazione. L'impostazione start_period: 30s dà tempo a n8n prima che gli health check inizino.
Non riesco a connettermi a n8n. La porta è vincolata a localhost. Non puoi accedervi da un'altra macchina senza un tunnel SSH o un reverse proxy. Questo è intenzionale.
Chiave di cifratura persa. Se hai perso N8N_ENCRYPTION_KEY e n8n ha credenziali memorizzate, quelle credenziali sono perse. Non c'è possibilità di recupero. Ecco perché esiste il passaggio di backup.
Cosa devo fare dopo aver installato n8n?
Questa installazione ti dà un'istanza n8n funzionante accessibile solo dal server stesso. Per l'uso in produzione, servono altre tre cose:
-
Reverse proxy con SSL. Configura Nginx o Caddy davanti a n8n con un certificato TLS. Questo ti dà accesso HTTPS con un nome di dominio. Vedi .
-
Backup. Pianifica backup automatizzati del database PostgreSQL e della chiave di cifratura n8n. Vedi .
-
Aggiornamenti. Per aggiornare n8n, cambia
N8N_VERSIONin.envcon la nuova versione, poi eseguidocker compose up -d. Docker scarica la nuova immagine e ricrea il container. Leggi sempre le note di rilascio di n8n prima di aggiornare.
Per la guida principale sulle opzioni di automazione dei workflow su VPS, vedi .
Per le basi di Docker Compose e la gestione di più servizi, vedi Docker Compose per deployment multi-servizio su VPS.
Copyright 2026 Virtua.Cloud. Tutti i diritti riservati. Questo contenuto è un'opera originale del team Virtua.Cloud. La riproduzione, ripubblicazione o redistribuzione senza autorizzazione scritta è vietata.
Pronto a provare?
Distribuisci il tuo server in pochi secondi. Linux, Windows o FreeBSD.
Vedi piani VPS