Installare n8n su VPS con Docker, Postgres e HTTPS
Guida completa per configurare n8n su VPS usando Docker Compose. Risolvi gli errori WEBHOOK_URL e encryption-key per integrare Postgres e il proxy HTTPS correttamente.
Cosa stai costruendo
n8n è uno strumento per l'automazione dei workflow: un editor visuale in cui un trigger, un webhook, una pianificazione o l'invio di un modulo avvia una catena di nodi che chiamano API, trasformano i dati e li scrivono in altri sistemi. È diventato lo strumento di integrazione predefinito per i workflow con agenti AI, perché comunica con ogni provider di modelli e database senza richiedere la scrittura di un servizio dedicato. Un docker run permette di avere un editor funzionante in due minuti. Questa guida tratta il restante novanta per cento: renderlo persistente usando Postgres invece del file SQLite predefinito, renderlo raggiungibile tramite HTTPS e, aspetto che quasi tutti configurano in modo errato, fare in modo che i webhook forniscano un URL effettivamente raggiungibile dall'esterno.
Lo stack completo è costituito da due container sulla stessa rete Docker: n8n e un database Postgres che contiene i workflow e le credenziali. Un reverse proxy sull'host termina TLS e inoltra le richieste a n8n su localhost, quindi nessun servizio è esposto direttamente su Internet al di fuori di quel proxy. Si aggiunge agli altri servizi presenti nella shortlist del self-hosting 2026.
Prerequisiti e limiti reali
Serve un VPS con almeno 1 GB di RAM. Prevedi 2 GB quando i workflow iniziano a svolgere attività reali, perché le esecuzioni e il runtime Node.js consumano memoria. Se l’out-of-memory killer arresta il container durante un’esecuzione, te ne accorgi nel modo peggiore. Per iniziare è sufficiente 1 vCPU. Se il server eseguirà anche un servizio più pesante, dimensiona prima le risorse per quel servizio. Il caso più comune è una libreria fotografica: i requisiti minimi effettivi di RAM di PhotoPrism e Immich sono molto superiori a quelli richiesti da n8n. Lo stesso vale per un server multimediale. Un server Jellyfin e un’interfaccia web per consultarlo, come Halcyon, che ricostruisce la libreria come un videonoleggio degli anni 90, utilizzeranno RAM e capacità di transcodifica molto prima che n8n ne rilevi la necessità.
Serve un dominio o sottodominio, ad esempio n8n.example.com, con un record A che punti all'indirizzo IP pubblico del VPS e che sia già risolto prima di richiedere un certificato. Le porte 80 e 443 devono essere aperte verso il proxy. La porta 5678 utilizzata da n8n non deve essere esposta a Internet. Servono Docker Engine e il plugin Compose. Se docker compose version restituisce un errore con docker: 'compose' is not a docker command, significa che è installato il vecchio binario standalone e che il plugin è sudo apt install docker-compose-plugin.
SQLite va bene per i test, Postgres per tutto ciò da cui dipendi
Il database predefinito di n8n è un file SQLite in /home/node/.n8n/database.sqlite. Per fare prove va bene: non montare alcun volume e perderai il database al primo ricreare del container. Anche questa è una lezione utile. Il motivo per passare a Postgres non è la velocità pura. SQLite consente un solo lock di scrittura, quindi un'istanza che esegue più workflow contemporaneamente, oppure la modalità queue che prima o poi vorrai usare, genera SQLITE_BUSY: database is locked in condizioni di concorrenza. Postgres non ha questo limite, consente backup affidabili con pg_dump ed è il database previsto dalla documentazione di n8n per i server da cui dipendi. In seguito dovrai migrare manualmente i dati. Se questo server è importante, inizia con Postgres.
DNS e firewall
Punta prima il record e apri le porte, così il passaggio successivo relativo al certificato non fallisce perché il nome non viene risolto.
dig +short n8n.example.com
curl -s ifconfig.me
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow OpenSSH
sudo ufw enableNon aprire 5678. Il file Compose associa n8n a 127.0.0.1:5678, quindi solo il reverse proxy dell'host può raggiungerlo; un ufw allow 5678 annullerebbe questo isolamento.
Il file Compose
Crea una directory di lavoro e un file docker-compose.yml. Questo definisce l'intero stack: due servizi, una rete privata e due volumi denominati.
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: n8n
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: n8n
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- n8n_net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n -d n8n"]
interval: 10s
timeout: 5s
retries: 5
n8n:
image: docker.n8n.io/n8nio/n8n:2.29.10
restart: unless-stopped
ports:
- "127.0.0.1:5678:5678"
environment:
- N8N_HOST=n8n.example.com
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://n8n.example.com/
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_PROXY_HOPS=1
- GENERIC_TIMEZONE=Europe/London
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
volumes:
- n8n_data:/home/node/.n8n
networks:
- n8n_net
depends_on:
postgres:
condition: service_healthy
volumes:
postgres_data:
n8n_data:
networks:
n8n_net:È importante chiarire alcune scelte. DB_POSTGRESDB_HOST=postgres è il nome del servizio che Docker risolve sulla rete condivisa, non localhost, che all'interno del container n8n indica n8n stesso. depends_on con condition: service_healthy impedisce a n8n di avviarsi prima di Postgres; senza questa impostazione, n8n si avvia, non trova il database e termina. Il volume denominato n8n_data montato in /home/node/.n8n contiene la chiave di crittografia e, con SQLite, il database: è l'unica directory che non devi perdere. Fissa l'immagine a una versione esatta, mai latest; i motivi sono descritti nella sezione sugli aggiornamenti qui sotto.
Il file dei secret
Non inserire mai le password nel file Compose. Inseriscile in un file .env accanto a questo file, che Compose legge automaticamente, e generane i valori in modo che siano realmente casuali.
printf 'POSTGRES_PASSWORD=%s\n' "$(openssl rand -hex 24)" > .env
printf 'N8N_ENCRYPTION_KEY=%s\n' "$(openssl rand -hex 32)" >> .env
chmod 600 .envN8N_ENCRYPTION_KEY è la stringa più importante di questa configurazione: è la chiave con cui vengono cifrate tutte le credenziali memorizzate. Impostala esplicitamente invece di lasciare che la generi n8n, perché un valore generato da te può essere annotato e ripristinato. Dopo che n8n ha cifrato la prima credenziale con questa chiave, modificarla rende impossibile decifrare tutte le credenziali. Impostala quindi ora, una sola volta, e non modificare mai più quella riga.
Le variabili di ambiente che determinano il funzionamento dei webhook
Quattro variabili controllano il modo in cui n8n si presenta all'esterno. Configurarle in modo errato è la causa più frequente delle richieste di supporto su n8n.
N8N_HOSTè il nome host pubblico,n8n.example.com. Se si lascia il valore predefinitolocalhostdietro un proxy, l'editor prova a caricare la propria API dalocalhostnel browser dell'utente. La richiesta non va a buon fine.N8N_PROTOCOL=httpsindica a n8n che il servizio è pubblicato tramite TLS. In questo modo n8n contrassegna il cookie di sessione comeSecuree genera URLhttps://.N8N_PORT=5678è la porta su cui n8n è in ascolto all'interno del container. Non è la porta pubblica: il proxy gestisce la porta 443.WEBHOOK_URL=https://n8n.example.com/è la variabile più critica. n8n genera gli indirizzi dei webhook da incollare in Stripe, GitHub o in qualsiasi altro client esterno a partire da questi valori. Se la variabile non è impostata o contiene un valore errato, n8n utilizzaN8N_HOST:N8N_PORTe forniscehttps://n8n.example.com:5678/webhook/...o, peggio,http://localhost:5678/webhook/.... L'indirizzo viene visualizzato senza errori, sembra plausibile, ma non è raggiungibile da Internet. Di conseguenza, le richieste del client non arrivano mai e il problema può passare inosservato. Impostare l'esatto URL di base pubblico, con la barra finale, quindi verificare che il nodo webhook mostri un URL senza porta.
N8N_PROXY_HOPS=1 indica al server Express di n8n di considerare attendibile un proxy davanti al servizio. In questo modo il rate limiting e tutte le funzionalità che leggono l'indirizzo IP del client vedono l'indirizzo reale, non quello del proxy. In questa configurazione, una variabile che non va impostata intenzionalmente è N8N_RUNNERS_ENABLED. I task runner, che eseguono la logica dei nodi Code di n8n in un processo separato da quello principale, erano una funzionalità da attivare esplicitamente per tutta la serie 1.x. In quella serie, la variabile aveva come valore predefinito false e doveva essere impostata su true. n8n 2.0 li ha attivati per impostazione predefinita. Per questo, nella serie 2.x, questa guida specifica che la variabile è deprecata e la documentazione di n8n indica che non è più necessario impostarla.
Primo avvio
docker compose up -d
docker compose ps
docker compose logs -f n8nUn primo avvio riuscito termina con una riga Editor is now accessible via:, preceduta da una riga n8n ready on ..., port 5678. docker compose ps dovrebbe mostrare entrambi i container Up, con postgres contrassegnato come (healthy). Se n8n entra in un ciclo Restarting, consulta i log: nella maggior parte dei casi la causa è la connessione al database oppure i permessi sul volume descritti di seguito.
TLS con un reverse proxy
n8n comunica tramite HTTP in chiaro sulla porta 5678; un componente a monte termina la connessione HTTPS. Esistono due opzioni semplici.
Se esegui già diversi container, pubblica n8n tramite un reverse proxy Traefik che emette automaticamente i certificati TLS. Sono sufficienti alcune label: Traefik richiede e rinnova il certificato automaticamente.
Se questo è l'unico servizio sul server, un virtual host nginx con un certificato Let's Encrypt è più semplice. Usa la configurazione TLS di Certbot e nginx per Ubuntu 24.04 per ottenere il certificato, quindi utilizza questo server block:
server {
listen 443 ssl;
server_name n8n.example.com;
ssl_certificate /etc/letsencrypt/live/n8n.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/n8n.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:5678;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600;
client_max_body_size 16m;
}
}Gli header Upgrade e Connection "upgrade" sono obbligatori. n8n invia gli aggiornamenti in tempo reale delle esecuzioni all'editor tramite WebSocket. Senza queste due righe, la pagina di accesso viene caricata ma poi si blocca mostrando un avviso di connessione persa. proxy_read_timeout 3600 impedisce che le esecuzioni di lunga durata vengano interrotte dopo i 60 secondi predefiniti di nginx. L'header X-Forwarded-Proto $scheme completa N8N_PROXY_HOPS=1: informa n8n che la richiesta originale era HTTPS, anche se il proxy lo raggiunge tramite HTTP in chiaro. In questo modo n8n non considera la connessione non sicura e non rifiuta il proprio cookie.
Il primo workflow, per renderlo operativo
Apri https://n8n.example.com/, crea l'account proprietario (sezione successiva) e costruisci il workflow più semplice che dimostri il corretto funzionamento del percorso: webhook in ingresso, chiamata HTTP e risposta in uscita.
- Aggiungi un nodo Webhook. Imposta il metodo su
POSTe un percorso comehello. Il nodo mostra due URL, un Test URL e un Production URL. Questa è la causa di molte segnalazioni del tipo "il mio webhook non funziona". Il Test URL risponde a una sola chiamata e soltanto dopo che hai fatto clic su Listen for test event; in seguito scade. Il Production URL viene registrato quando pubblichi il workflow e da quel momento risponde alle chiamate. - Aggiungi un nodo HTTP Request dopo il webhook e indirizzalo a una qualsiasi API JSON pubblica. Una richiesta GET a
https://api.github.com/zenrestituisce una stringa su una sola riga, che è sufficiente. - Aggiungi un nodo Respond to Webhook e imposta l'opzione Respond del nodo Webhook su "Using Respond to Webhook node", in modo che il chiamante riceva in risposta l'output del nodo HTTP.
- Fai clic su Publish (in alto a destra) ed esegui la chiamata:
curl -X POST https://n8n.example.com/webhook/hello. Dovresti ricevere la riga zen: POST in ingresso, chiamata API e risposta in uscita. È la struttura della maggior parte delle automazioni reali. Le guide meno recenti indicano di attivare qui l'interruttore Active; n8n 2.0 ha sostituito questo interruttore con Publish e Unpublish.
Una variante pianificata sostituisce il nodo Webhook con un Schedule Trigger e chiama invece un endpoint di un modello. Un'istanza self-hosted di Ollama in esecuzione sullo stesso VPS è un modo ordinato per creare un riepilogatore notturno.
Gestione degli utenti, non autenticazione di base
Le guide meno recenti di n8n indicano di impostare N8N_BASIC_AUTH_ACTIVE=true. Queste variabili sono state rimosse in n8n 1.0 e ora non hanno alcun effetto. Oggi l’autenticazione usa l'account proprietario: al primo accesso all'editor, n8n richiede la creazione di un account proprietario con email e password. Questo controllo è obbligatorio e non esiste una modalità anonima. Crealo subito dopo il primo avvio, prima di condividere l'URL: tra docker compose up e l'invio del primo modulo, l'istanza può essere acquisita dal primo utente che riesce a raggiungerla. Un livello di basic auth sul reverse proxy è una misura aggiuntiva ragionevole, ma costituisce un secondo fattore e non l'autenticazione effettiva. L'account proprietario e tutte le altre funzionalità descritte in questa guida sono disponibili nella Community Edition gratuita. Se in seguito vuoi aggiungere altri utenti con ruoli granulari o il supporto SSO, vale la pena leggere quali funzionalità di n8n richiedono una licenza a pagamento prima di pianificare l'installazione in base a queste esigenze.
Backup: prima la chiave di crittografia, poi il database
È necessario eseguire il backup di due elementi, ma non sono sostituibili allo stesso modo.
La N8N_ENCRYPTION_KEY. Ogni credenziale memorizzata in n8n, inclusi token API, password del database e secret OAuth, viene crittografata a riposo con questa chiave. I workflow presenti in Postgres sono inutilizzabili senza di essa: se ripristini il database su un nuovo server con una chiave diversa, n8n non può decrittografare neppure una credenziale. Non è possibile recuperarle né reimpostarle. Il file .env contiene la chiave. Copialo fuori dal server, idealmente in una voce del password manager, il giorno stesso in cui crei la chiave. Questo è il backup realmente importante.
Il database Postgres, che contiene i workflow, la cronologia delle esecuzioni e le credenziali crittografate:
docker compose exec -T postgres pg_dump -U n8n -d n8n \
| gzip > n8n-db-$(date +%F).sql.gzEsegui questo comando secondo una pianificazione e copia il dump fuori dal server. Per ripristinarlo su un VPS nuovo: avvia lo stack una volta, in modo che il database venga creato, arresta n8n, ricarica il dump con psql, inserisci lo stesso N8N_ENCRYPTION_KEY in .env e avvia n8n. La stessa chiave più il dump producono un'istanza funzionante; una chiave nuova rende inutilizzabili i workflow per qualsiasi credenziale.
Aggiornamenti: bloccare il tag
Il file Compose blocca n8nio/n8n:2.29.10 invece di latest intenzionalmente. n8n rilascia una nuova versione minore quasi ogni settimana e, occasionalmente, modifica lo schema del database o il comportamento dei nodi tra una versione e l’altra. Per questo, latest significa che un pull non presidiato può installare una build che migra il database appena viene avviata. Blocca una versione, leggi le note di rilascio prima di aggiornarla — n8n segnala lì le modifiche incompatibili — e procedi con un aggiornamento pianificato:
docker compose exec -T postgres pg_dump -U n8n -d n8n | gzip > pre-upgrade.sql.gz
# edit the image tag in docker-compose.yml, then:
docker compose pull n8n
docker compose up -d n8n
docker compose logs -f n8nI passaggi tra versioni principali sono quelli in cui questo controllo è più importante. La linea 2.0, ad esempio, ha impostato N8N_BLOCK_ENV_ACCESS_IN_NODE su true per impostazione predefinita. Di conseguenza, qualsiasi nodo Code che leggeva process.env perde silenziosamente l’accesso finché non lo reimposti su false. La stessa release ha iniziato ad applicare autorizzazioni più restrittive al file delle impostazioni, ha abilitato per impostazione predefinita i task runner e ha sostituito l’interruttore Activate del workflow con Publish. Leggi la pagina sulle modifiche incompatibili della versione 2.0 prima di passare a una nuova versione principale. All’avvio, n8n esegue automaticamente le migrazioni del database necessarie. Proprio per questo, il pg_dump prima dell’aggiornamento è obbligatorio. Le credenziali sono memorizzate in forma crittografata usando una chiave in .env, mentre i dati risiedono in Postgres. I container sono quindi ricreabili: per aggiornare, sostituiscili; per eseguire il rollback, blocca il tag della versione precedente e ripristina il dump.
Modalità di errore e relative stringhe di log
The requested webhook "POST hello" is not registered. Un errore 404 deriva dalla chiamata a un webhook il cui workflow non è pubblicato, oppure dalla chiamata al percorso di test quando non è attivo l'ascolto. I percorsi di test (/webhook-test/...) rispondono solo quando è attiva la funzione "Listen for test event"; i percorsi di produzione (/webhook/...) rispondono solo dopo la pubblicazione del workflow. Il codice This webhook is not registered for GET requests. Did you mean to make a POST request? indica che il metodo utilizzato è errato: il nodo attende una richiesta POST mentre è stata inviata una GET.
L'URL del webhook mostra una :5678 o localhost. Il nodo visualizza https://n8n.example.com:5678/webhook/... o http://localhost:5678/.... La variabile WEBHOOK_URL non è impostata o è errata, quindi n8n ha costruito l'indirizzo a partire da N8N_HOST:N8N_PORT invece che dal tuo indirizzo pubblico di base. Imposta WEBHOOK_URL=https://n8n.example.com/, ricrea il container con docker compose up -d e la porta non corretta sparirà.
There was a problem loading init data nel browser. L'editor è stato caricato ma non riesce a raggiungere la propria API di backend. Dietro un proxy, questo è quasi sempre dovuto a una configurazione errata di N8N_HOST o WEBHOOK_URL, a un proxy che non inoltra gli header WebSocket Upgrade, o a N8N_PROTOCOL che non corrisponde alla modalità di connessione utilizzata. Verifica le quattro variabili pubbliche e assicurati che il proxy inoltri Upgrade e Connection.
password authentication failed for user "n8n" nei log, con il container che continua a riavviarsi. La password inviata da n8n non corrisponde a quella con cui è stato inizializzato il database. L'insidia: Postgres legge POSTGRES_PASSWORD solo quando inizializza una directory dati vuota. Se avvii lo stack una volta e poi modifichi POSTGRES_PASSWORD in .env, il volume postgres_data esistente manterrà la vecchia password. Ripristina il valore originale oppure, se non hai dati da conservare, docker compose down e docker volume rm il volume postgres, quindi riavvia da zero.
EACCES: permission denied, open '/home/node/.n8n/config' all'avvio. n8n viene eseguito come utente node (UID 1000) e non riesce a scrivere nella propria directory di configurazione. Questo problema si verifica quando si esegue un bind-mount di una cartella host (./n8n_data:/home/node/.n8n) di proprietà di root. Utilizza il volume nominato mostrato sopra oppure, se preferisci un bind mount, esegui prima sudo chown -R 1000:1000 ./n8n_data.
Permissions 0644 for n8n settings file /home/node/.n8n/config are too wide. Changing permissions to 0600.. Dalla versione 2.x, n8n impone per impostazione predefinita 0600 su quel file di impostazioni e lo corregge automaticamente all'avvio. Questa riga di log indica che il sistema ha già corretto i permessi, solitamente dopo un bind mount o dopo che un ripristino ha copiato il file con permessi troppo permissivi. Non è necessaria alcuna azione; imposta N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=false solo se il tuo file system non è in grado di gestire i permessi.
Mismatching encryption keys, la riga completa indica che la chiave di cifratura nel file di impostazioni /home/node/.n8n/config non corrisponde alla N8N_ENCRYPTION_KEY presente nel tuo ambiente. La chiave nel tuo ambiente differisce da quella che n8n ha scritto nel volume dati durante un'esecuzione precedente; ciò accade il più delle volte perché n8n ha generato una chiave casuale in un avvio precedente, quando la variabile non era impostata, e successivamente ne è stata impostata una diversa. Ripristina la chiave originale in .env oppure, solo se non hai credenziali salvate che vale la pena conservare, elimina il file config all'interno del volume n8n_data e lascia che n8n lo rigeneri, accettando il fatto che le credenziali esistenti diventeranno illeggibili.
Un banner di accesso relativo ai cookie sicuri: Your n8n server is configured to use a secure cookie, however you are either visiting this via an insecure URL, or using Safari. Hai impostato N8N_PROTOCOL=https ma hai raggiunto n8n tramite HTTP semplice, solitamente accedendo direttamente all'IP e alla porta invece che tramite il proxy HTTPS. Accedi tramite https://n8n.example.com/. Imposta N8N_SECURE_COOKIE=false solo se non puoi assolutamente utilizzare HTTPS, e non farlo mai su una macchina esposta a Internet.
Per inserire un modello linguistico all'interno di tali workflow, consulta creazione di workflow AI con Claude e n8n.
FAQ
Devo usare SQLite o Postgres per n8n?
SQLite (il valore predefinito) è sufficiente per provare n8n e per un'istanza personale che esegue un workflow alla volta. Usa Postgres per qualsiasi ambiente da cui dipendi: il lock di scrittura singolo di SQLite genera database is locked in condizioni di concorrenza, mentre Postgres consente backup affidabili con pg_dump. La migrazione successiva è manuale; se il server è importante, inizia con Postgres.
Perché i webhook di n8n non vengono mai attivati?
Quasi sempre WEBHOOK_URL. Se non è impostata o è errata, n8n stampa indirizzi webhook costruiti usando N8N_HOST:N8N_PORT, spesso contenenti un :5678 o localhost, che sembrano validi ma non sono raggiungibili da Internet. Le richieste del chiamante non arrivano quindi a destinazione. Imposta WEBHOOK_URL=https://n8n.example.com/ e verifica che il nodo mostri un URL senza porta. La seconda causa è chiamare l'URL di produzione di un workflow che non hai pubblicato: viene restituito The requested webhook ... is not registered.
Che cosa devo includere nei backup di n8n?
Due elementi. Il N8N_ENCRYPTION_KEY del file .env, perché ogni credenziale memorizzata viene crittografata con questo valore e, se lo perdi, non può più essere decrittografata. Copialo fuori dal server il giorno stesso in cui lo crei. Serve inoltre un pg_dump del database Postgres contenente workflow, cronologia e credenziali. Per un ripristino sono necessari entrambi: la stessa chiave e il dump.
Come posso eseguire n8n dietro HTTPS?
n8n espone HTTP non crittografato sulla porta 5678; un reverse proxy davanti a n8n termina TLS. Associa n8n a 127.0.0.1:5678, in modo che solo il proxy possa raggiungerlo, quindi usa Traefik con certificati automatici oppure nginx con un certificato Let's Encrypt. Imposta N8N_PROTOCOL=https e WEBHOOK_URL=https://your-host/ e verifica che il proxy inoltri gli header Upgrade di WebSocket; in caso contrario, l'editor si blocca.
Come posso aggiornare n8n in sicurezza?
Blocca un tag immagine specifico invece di latest, crea prima un pg_dump perché n8n esegue automaticamente le migrazioni all'avvio, leggi le note di rilascio per individuare eventuali modifiche incompatibili, quindi aggiorna il tag ed esegui docker compose pull n8n && docker compose up -d n8n. Il container è ricreabile, quindi puoi eseguire il rollback bloccando il tag precedente e ripristinando il dump creato prima dell'aggiornamento.