I 7 errori più comuni con n8n in produzione (e come evitarli)
Errori reali che abbiamo visto in decine di installazioni n8n in produzione: nessun error handling, credenziali esposte, memory leak, zero monitoring e come risolverli.

Impara dagli errori degli altri
In anni di consulenza su n8n per aziende italiane, abbiamo visto gli stessi errori ripetersi con una frequenza impressionante. La buona notizia è che sono tutti evitabili. La cattiva notizia è che la maggior parte delle persone li scopre solo quando qualcosa si rompe in produzione, spesso nel momento peggiore.
Ecco i 7 errori più comuni e, per ciascuno, la soluzione pratica.
Errore 1: nessun error handling nei workflow
Il problema
Questo è in assoluto l'errore più diffuso. Il workflow funziona perfettamente durante i test, ma in produzione i dati non sono mai perfetti. Un campo mancante, un'API che non risponde, un formato inatteso e il workflow si blocca silenziosamente.
Esempio reale
Un e-commerce aveva automatizzato la sincronizzazione ordini con il gestionale. Tutto funzionava da 3 mesi. Poi un cliente ha inserito un carattere speciale nel campo indirizzo. Il workflow si è bloccato e 127 ordini non sono stati sincronizzati prima che qualcuno se ne accorgesse - dopo 4 giorni.
La soluzione
1. Aggiungi un Error Trigger a ogni workflow critico:
Crea un workflow separato con un nodo Error Trigger. Questo workflow si attiva automaticamente quando un qualsiasi altro workflow fallisce.
// Workflow "Error Handler Globale"
// Nodo Error Trigger -> Nodo Code -> Notifica
const error = $json;
const message = `
ERRORE WORKFLOW
Workflow: ${error.workflow.name}
Nodo: ${error.execution.lastNodeExecuted}
Errore: ${error.execution.error.message}
Timestamp: ${new Date().toISOString()}
Esecuzione: ${error.execution.id}
URL: https://n8n.tuodominio.it/execution/${error.execution.id}
`;
return [{ json: { message, severity: 'critical' } }];
2. Usa nodi IF per validare i dati in ingresso:
// Prima di processare un ordine, verifica i campi critici
const order = $json;
const errors = [];
if (!order.email) errors.push('Email mancante');
if (!order.billing?.first_name) errors.push('Nome mancante');
if (!order.line_items?.length) errors.push('Nessun prodotto');
if (errors.length > 0) {
// Instrada verso il percorso di errore
return [{ json: { order, errors, valid: false } }];
}
return [{ json: { ...order, valid: true } }];
3. Usa il nodo Error Trigger per ogni workflow individuale che gestisca il fallimento in modo specifico (retry, notifica, log).
Regola d'oro
Se un workflow non ha error handling, non è pronto per la produzione. Punto.
Errore 2: nessun backup (o backup non testati)
Il problema
n8n salva tutto nel suo database interno: workflow, credenziali, esecuzioni. Se il database si corrompe o il server muore, perdi tutto.
Esempio reale
Un'agenzia aveva 47 workflow attivi su n8n. Il disco del server si è riempito (vedi Errore 5) e il database SQLite si è corrotto. Non avevano backup. Hanno dovuto ricostruire tutto da zero: 3 settimane di lavoro perse.
La soluzione
Backup automatico giornaliero:
#!/bin/bash
# /opt/scripts/backup-n8n.sh
DATE=$(date +%Y%m%d)
BACKUP_DIR="/opt/backups/n8n"
mkdir -p "$BACKUP_DIR"
# Esporta workflow
docker exec n8n n8n export:workflow --all \
--output="/tmp/workflows.json" 2>/dev/null
# Esporta credenziali
docker exec n8n n8n export:credentials --all \
--output="/tmp/credentials.json" 2>/dev/null
# Copia dal container
docker cp n8n:/tmp/workflows.json "$BACKUP_DIR/workflows_${DATE}.json"
docker cp n8n:/tmp/credentials.json "$BACKUP_DIR/credentials_${DATE}.json"
# Backup database
cp /var/lib/docker/volumes/n8n_data/_data/database.sqlite \
"$BACKUP_DIR/database_${DATE}.sqlite"
# Comprimi e cifra
tar -czf "$BACKUP_DIR/n8n_${DATE}.tar.gz" \
"$BACKUP_DIR/workflows_${DATE}.json" \
"$BACKUP_DIR/credentials_${DATE}.json" \
"$BACKUP_DIR/database_${DATE}.sqlite"
# Copia su storage remoto
rclone copy "$BACKUP_DIR/n8n_${DATE}.tar.gz" remote:backups/n8n/
# Pulisci (mantieni 30 giorni locale, 90 remoto)
find "$BACKUP_DIR" -name "n8n_*.tar.gz" -mtime +30 -delete
echo "[$(date)] Backup completato" >> /var/log/n8n-backup.log
Testa il restore almeno una volta al mese. Un backup non testato è come un paracadute non verificato.
# Test restore su istanza temporanea
docker run -d --name n8n-test -p 5679:5678 \
-v /tmp/n8n-test:/home/node/.n8n \
n8nio/n8n
# Importa workflow
docker exec n8n-test n8n import:workflow \
--input="/tmp/workflows.json"
# Verifica
docker exec n8n-test n8n list:workflow
# Pulisci
docker rm -f n8n-test
Errore 3: credenziali in chiaro o non protette
Il problema
Le credenziali salvate in n8n (API key, token OAuth, password) sono criptate nel database con la chiave N8N_ENCRYPTION_KEY. Ma abbiamo visto troppe istanze dove:
- La chiave di cifratura è quella di default (non impostata)
- Le credenziali sono nel file
.envcon permessi 644 (leggibile da tutti) - Le API key sono hardcoded nei nodi Code invece di usare le credenziali
- Il file
docker-compose.ymlè in un repository Git pubblico con le variabili in chiaro
Esempio reale
Uno sviluppatore ha pubblicato il suo docker-compose.yml su GitHub per chiedere aiuto in un forum. Nel file c'erano in chiaro: la chiave di cifratura n8n, l'API key di OpenAI, il token Slack e le credenziali del database. Prima che se ne accorgesse, qualcuno aveva usato la sua API key OpenAI per generare contenuti, accumulando 340 euro di costi.
La soluzione
1. Imposta sempre la chiave di cifratura:
# Genera chiave sicura
openssl rand -hex 32
# Aggiungi al .env
N8N_ENCRYPTION_KEY=la_tua_chiave_generata
2. Proteggi il file .env:
chmod 600 .env
chown root:root .env
3. Mai credenziali nel codice. Usa le credenziali di n8n:
// SBAGLIATO
const apiKey = 'sk-abc123...';
// GIUSTO
// Usa il nodo HTTP Request con credenziali configurate in n8n
// oppure variabili d'ambiente:
const apiKey = $env.OPENAI_API_KEY;
4. Se usi Git, aggiungi al .gitignore:
.env
docker-compose.override.yml
*.pem
*.key
Errore 4: zero monitoring
Il problema
n8n gira felicemente sul server finché non smette di farlo. Senza monitoring, scopri che n8n è giù quando un cliente si lamenta che non ha ricevuto la conferma ordine, oppure quando ti accorgi che le email non partono da 3 giorni.
La soluzione
Setup minimo (15 minuti, costo zero):
- Uptime Kuma per il monitoring HTTP:
docker run -d --name uptime-kuma \
-p 3001:3001 \
-v uptime-kuma:/app/data \
louislam/uptime-kuma
# Configura un monitor per:
# - https://n8n.tuodominio.it (HTTP 200)
# - Intervallo: 60 secondi
# - Notifica: Telegram/Email/Slack
- Healthcheck nel docker-compose.yml:
services:
n8n:
image: n8nio/n8n:latest
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:5678/healthz"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
restart: unless-stopped
- Monitor per i workflow critici con Healthchecks.io:
// Alla fine di ogni workflow critico, aggiungi un nodo HTTP Request
// che pinga Healthchecks.io
{
"method": "GET",
"url": "https://hc-ping.com/il-tuo-uuid"
}
// Se il ping non arriva entro l'intervallo previsto,
// Healthchecks.io ti notifica
- Alert disco pieno (vedi Errore 5):
# Cron ogni ora
0 * * * * df -h / | awk 'NR==2{gsub(/%/,""); if($5>85) system("curl -s https://hc-ping.com/uuid/fail")}'
Errore 5: memory leak e disco pieno
Il problema
n8n salva i dati di ogni esecuzione nel database. Con workflow che processano molti dati (immagini, documenti PDF, payload grandi), il database cresce rapidamente. Abbiamo visto database SQLite da 40 GB su server con 50 GB di disco.
Il secondo problema è la memoria: workflow con loop grandi o che processano file pesanti possono consumare tutta la RAM del server, causando il crash di n8n.
La soluzione
1. Configura la pulizia delle esecuzioni:
# Mantieni solo le ultime 1000 esecuzioni
EXECUTIONS_DATA_MAX_AGE=168 # 7 giorni in ore
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_PRUNE_MAX_COUNT=1000
# Salva solo gli errori (meno spazio)
EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
EXECUTIONS_DATA_SAVE_ON_ERROR=all
EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=true
2. Limita la memoria del container Docker:
services:
n8n:
image: n8nio/n8n:latest
deploy:
resources:
limits:
memory: 2G
reservations:
memory: 512M
environment:
- NODE_OPTIONS=--max-old-space-size=1536
3. Sposta su PostgreSQL per database grandi:
SQLite funziona bene per installazioni piccole, ma con molti workflow e esecuzioni è meglio PostgreSQL:
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=localhost
DB_POSTGRESDB_PORT=5432
DB_POSTGRESDB_DATABASE=n8n
DB_POSTGRESDB_USER=n8n
DB_POSTGRESDB_PASSWORD=password_sicura
4. Monitora lo spazio disco:
# Cron: alert se disco oltre 85%
0 */6 * * * USAGE=$(df / | awk 'NR==2{gsub(/%/,""); print $5}'); \
[ "$USAGE" -gt 85 ] && \
curl -X POST -d "Disco al ${USAGE}%!" \
"https://api.telegram.org/bot$TOKEN/sendMessage?chat_id=$CHAT_ID&text=ALERT:+disco+${USAGE}%25"
Errore 6: nessun rate limiting sulle API esterne
Il problema
Quando un workflow chiama un'API esterna (Google Sheets, Slack, CRM) in un loop, può facilmente superare i limiti di rate dell'API. Il risultato: errori 429 (Too Many Requests), dati persi e, nel peggiore dei casi, account bloccato.
Esempio reale
Un workflow doveva aggiornare 5.000 righe in Google Sheets. Inviava una richiesta API per ogni riga, senza pausa. Dopo 100 richieste, Google ha bloccato le chiamate per 60 secondi. Il workflow continuava a tentare, accumulando errori. Alla fine, solo 100 righe su 5.000 erano state aggiornate, ma il workflow risultava "completato con successo" perché non c'era error handling (vedi Errore 1).
La soluzione
1. Usa il nodo Wait per rispettare i limiti:
Per ogni batch di 50 elementi:
-> Processa batch
-> Wait 2 secondi
-> Batch successivo
2. Usa batch processing nativo di n8n:
Molti nodi n8n supportano le operazioni batch. Invece di aggiornare una riga alla volta, aggiorna 50 righe in una singola chiamata API.
3. Implementa retry con backoff esponenziale:
// Nodo Code: retry con backoff
const maxRetries = 3;
let delay = 1000; // 1 secondo
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await $http.request({
method: 'POST',
url: apiUrl,
body: data
});
return [{ json: response }];
} catch (error) {
if (error.statusCode === 429 && attempt < maxRetries) {
// Aspetta con backoff esponenziale
await new Promise(resolve => setTimeout(resolve, delay));
delay *= 2; // 1s, 2s, 4s
continue;
}
throw error;
}
}
4. Conosci i limiti delle API che usi:
| API | Limite | Strategia |
|---|---|---|
| Google Sheets | 100 richieste/100 secondi | Batch update, wait 1s |
| Slack | 1 messaggio/secondo | Wait 1.5s tra messaggi |
| HubSpot | 100 richieste/10 secondi | Batch, rate limiter |
| OpenAI | Varia per piano | Queue con priorità |
| WooCommerce | 25 richieste/secondo | Batch, wait se necessario |
Errore 7: nessun versioning dei workflow
Il problema
I workflow n8n evolvono nel tempo. Modifichi una cosa, poi un'altra, poi qualcosa si rompe e non ricordi com'era prima. Senza versioning, non puoi tornare indietro.
Esempio reale
Un consulente ha modificato un workflow critico "al volo" in produzione per aggiungere una funzionalità. La modifica ha introdotto un bug che duplicava le email ai clienti. Il workflow precedente non era stato salvato. Ci sono volute 4 ore per ricostruire la logica originale, durante le quali i clienti hanno continuato a ricevere email doppie.
La soluzione
1. Esporta i workflow in Git:
#!/bin/bash
# /opt/scripts/version-n8n.sh
cd /opt/n8n-workflows
# Esporta tutti i workflow
docker exec n8n n8n export:workflow --all --output="/tmp/all-workflows.json"
docker cp n8n:/tmp/all-workflows.json ./workflows.json
# Separa in file individuali per leggibilità
node -e "
const wfs = require('./workflows.json');
const fs = require('fs');
fs.mkdirSync('./workflows', { recursive: true });
wfs.forEach(wf => {
const name = wf.name.toLowerCase()
.replace(/[^a-z0-9]/g, '-')
.replace(/-+/g, '-');
fs.writeFileSync(
'./workflows/' + name + '.json',
JSON.stringify(wf, null, 2)
);
});
"
# Commit se ci sono cambiamenti
git add -A
git diff --cached --quiet || git commit -m "Aggiornamento workflow $(date +%Y-%m-%d)"
2. Aggiungi al crontab:
# Versioning ogni 6 ore
0 */6 * * * /opt/scripts/version-n8n.sh >> /var/log/n8n-version.log 2>&1
3. Tag prima di modifiche importanti:
git tag -a "v$(date +%Y%m%d)-pre-modifica-ordini" -m "Prima della modifica al workflow ordini"
4. Usa ambienti separati:
- Sviluppo: istanza n8n locale per testare le modifiche
- Staging: copia della produzione per test finali
- Produzione: solo workflow testati e approvati
Checklist pre-produzione
Prima di dichiarare un'istanza n8n "in produzione", verifica:
- Ogni workflow critico ha error handling
- Backup automatici configurati e testati
- Chiave di cifratura impostata e salvata separatamente
- File .env con permessi 600
- Monitoring attivo con notifiche
- Pulizia esecuzioni configurata
- Limiti di memoria Docker impostati
- Rate limiting rispettato per le API esterne
- Workflow esportati in Git
- Procedura di restore documentata e testata
- Procedura di aggiornamento documentata
Se manca anche solo un punto, non sei pronto per la produzione. Sistemalo prima.
Vuoi un audit della tua istanza n8n in produzione? Contattaci per una revisione completa.
Team n8n.it
Specialisti in Automazione
Siamo un team di esperti n8n focalizzati sull'automazione dei processi aziendali e la sicurezza delle implementazioni self-hosted.
Articoli correlati

Audit di sicurezza n8n: checklist per proteggere la tua istanza
Checklist pratica e completa per mettere in sicurezza la tua istanza n8n: reverse proxy, SSL, autent...
Leggi di più
Il costo reale di n8n self-hosted: server, manutenzione e tempo
n8n è gratuito, ma il self-hosting ha costi nascosti. Analizziamo il TCO reale: server, SSL, backup,...
Leggi di più
n8n e AI Agent: come automatizzare senza perdere il controllo
Guida pratica per usare gli AI Agent in n8n con guardrail, human-in-the-loop, validazione output, mo...
Leggi di più