Esegui n8n e SearXNG Localmente con Docker: Costruisci il Tuo Playground AI No-Code

Scritto da Francesco Di Donato • 20 aprile 2025 • 14 minuti di lettura
Viviamo in un mondo in cui chiunque abbia un computer e una connessione a Internet può accedere ad agenti di intelligenza artificiale, spesso senza costi e in esecuzione su macchine personali. In questa guida, imparerai come configurare un ambiente locale no-code utilizzando due strumenti open-source:
- n8n 🔗: Un potente strumento di automazione visiva che ti consente di prototipare e sperimentare rapidamente senza una vasta conoscenza di programmazione.
- SearXNG 🔗: Un metasearch engine incentrato sulla privacy che aggrega i risultati di varie fonti. Lo integreremo con n8n, consentendo a un agente AI di recuperare dati web aggiornati.
Tutto viene eseguito localmente con Docker Compose, quindi non sono necessarie iscrizioni, pagamenti o condivisione di dati.
Questo articolo fa parte di una serie basata su approfondimenti dalle mie sperimentazioni notturne 🦉. Trovo questa configurazione potente e facile da implementare, rendendola accessibile a chiunque sia interessato a esplorare queste tecnologie.
Cosa Sarai in Grado di Fare 🌟
Alla fine di questa configurazione, avrai un playground no-code in cui potrai:
- Attivare flussi di lavoro utilizzando l’editor visivo di n8n.
- Condurre ricerche automatizzate sul web con SearXNG.
- Costruire flussi di lavoro basati sull’intelligenza artificiale senza sforzo, come un bot per il fact-checking, un riassuntore di notizie o un assistente di ricerca.
La cosa migliore è che è 100% locale e privato, ad eccezione dell’elaborazione AI effettiva, che rimane gratuita poiché utilizzeremo Gemini 2.0 Flash. Nei futuri post del blog, esploreremo come integrare un modello come DeepSeek V3
in esecuzione localmente con ollama
in n8n, completando la configurazione locale.
Prima di iniziare, assicurati di avere Docker
🔗 installato. Questa guida funzionerà indipendentemente dal tuo sistema operativo.
Se non hai un background di programmazione, la prossima sezione potrebbe sembrare scoraggiante, ma fidati, è più facile di quanto potresti aspettarti. Scoprirai presto quanto possa essere potente questa abilità!
Passaggio 1: Crea il Tuo File Docker Compose 🏗️
Prima che emergessero soluzioni come Docker 🔗, l’esecuzione di software sul tuo computer o su un server remoto richiedeva il download e la configurazione di più componenti.
Docker semplifica questo processo. Gli sviluppatori possono creare servizi (siti web, server, database, ecc.) e definire un file chiamato Dockerfile
. Sebbene non sia necessario comprendere i dettagli della sua creazione, è importante sapere che contiene istruzioni che qualsiasi computer con Docker può seguire per assemblare il software al volo. Questo pre-packaging include tutto il necessario per il funzionamento del software, migliorando la replicabilità.
Per prima cosa, crea una cartella sul tuo computer (chiamiamola workspace
) e aggiungi un file vuoto al suo interno chiamato docker-compose.yml
.
workspace
└── docker-compose.yml
I creatori di n8n hanno rilasciato un repository GitHub pubblico chiamato Self-hosted AI Starter Kit 🔗. Questo repository contiene un file docker-compose.yml
ben strutturato. Lo apriremo 🔗 per identificare i componenti di cui abbiamo bisogno. Se hai fretta, puoi usare direttamente la loro versione, che include altri servizi utili che potrei trattare nei futuri post del blog. Tuttavia, scomporre le cose può essere un valido esercizio mentale, specialmente durante l’apprendimento.
Nel loro docker-compose.yml
, ci concentreremo su tutto ciò che riguarda n8n
e la sua dipendenza, Postgres
. Filtreremo i componenti relativi a ollama
(poiché useremo invece il modello gratuito Gemini 2.0 Flash) e qdrant
(un database vettoriale che preferisco sostituire con pgvector
—restate sintonizzati per saperne di più).
n8n:
hostname: n8n
container_name: n8n
image: n8nio/n8n:latest
networks: ['workspace']
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_USER=root
- DB_POSTGRESDB_PASSWORD=password
- N8N_DIAGNOSTICS_ENABLED=false
- N8N_PERSONALIZATION_ENABLED=false
- N8N_ENCRYPTION_KEY
- N8N_USER_MANAGEMENT_JWT_SECRET
restart: unless-stopped
ports:
- 5678:5678
volumes:
- n8n_storage:/home/node/.n8n
- ./n8n/backup:/backup
- ./shared:/data/shared
depends_on:
postgres:
condition: service_healthy
n8n-import:
condition: service_completed_successfully
Dipende da Postgres
, quindi dobbiamo includere il database nella nostra configurazione. Si basa anche su n8n-import
. Sebbene opzionale, ci permette di sincronizzare i flussi di lavoro e le credenziali di n8n. Lo manterremo nella nostra configurazione.
Dopo averlo eseguito la prima volta, noterai una nuova cartella n8n.
workspace
├── docker-compose.yml
└── n8n
└── backup
├── credentials
└── workflows
Esposizione
- Istruiamo il container a consentire la comunicazione dal nostro computer. n8n espone le sue funzionalità sulla porta
5678
. Questo ci permette di accedere a n8n localmente nel nostro browser all’indirizzohttp://localhost:5678
.
ports:
- 5678:5678
# Se cambi in "3000:5678",
# sarà "http://localhost:3000"
Copia il seguente file all’interno del tuo docker-compose.yml
volumes:
n8n_storage:
postgres_storage:
networks:
workspace:
# Gli attributi elencati in x-n8n sono necessari sia per il servizio n8n che per n8n-import.
# Lo definiamo solo una volta, evitando stupidi errori di battitura e avendo meno righe.
# È unibile nei servizi con la sintassi <<: *service-n8n (vedi sotto).
x-n8n: &service-n8n
image: n8nio/n8n:latest
networks:
- workspace
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_USER=${POSTGRES_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
- N8N_DIAGNOSTICS_ENABLED=false
- N8N_PERSONALIZATION_ENABLED=false
- N8N_ENCRYPTION_KEY
- N8N_USER_MANAGEMENT_JWT_SECRET
services:
postgres:
image: postgres:16-alpine
hostname: postgres
networks:
- workspace
restart: unless-stopped
environment:
- POSTGRES_USER
- POSTGRES_PASSWORD
- POSTGRES_DB
volumes:
- postgres_storage:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}']
interval: 5s
timeout: 5s
retries: 10
# n8n-import ha l'unico scopo e ciclo di vita di leggere le credenziali e i flussi di lavoro memorizzati
# sul nostro computer nella cartella n8n/backup e iniettarli in n8n.
n8n-import:
<<: *service-n8n
hostname: n8n-import
container_name: n8n-import
entrypoint: /bin/sh
command:
- '-c'
- 'n8n import:credentials --separate --input=/backup/credentials && n8n import:workflow --separate --input=/backup/workflows'
volumes:
- ./n8n/backup:/backup
depends_on:
postgres:
condition: service_healthy
n8n:
<<: *service-n8n
hostname: n8n
container_name: n8n
restart: unless-stopped
ports:
- 5678:5678
volumes:
- n8n_storage:/home/node/.n8n
- ./n8n/backup:/backup
- ./shared:/data/shared
depends_on:
postgres:
condition: service_healthy
n8n-import:
condition: service_completed_successfully
È ora di istruire Docker a scaricare ed eseguire questi servizi. La prima volta che lo fai, potrebbe richiedere alcuni minuti poiché deve scaricare i componenti necessari. Tuttavia, Docker memorizza queste immagini per impostazione predefinita, quindi i riavvii successivi saranno molto più veloci.
Apri il tuo terminale ed esegui il seguente comando:
docker compose up
Questo comando funge da accensione per il tuo motore. Quando vuoi spegnere i servizi, esegui semplicemente docker compose down
. Se questa è la prima volta che esegui una configurazione del genere e funziona correttamente, prenditi un momento per fare una pausa e festeggiare!
Passaggio 2: Crea un Agente AI n8n con Gemini 2.0 Flash 🧠
Il comando precedente ha riempito il tuo terminale di numerosi log. Alla fine di questi log, dovresti vedere il container n8n
che indica che è in ascolto su http://localhost:5678
. Inserisci questo URL nel tuo browser e dovresti vedere la schermata di autenticazione di n8n. Per la prima volta, dovrai creare un account. Stai tranquillo, l’email e la password che fornisci non verranno inviate a internet; rimarranno sulla tua macchina, memorizzate nel database Postgres che abbiamo configurato in precedenza.
Prima di creare il nostro agente AI, dobbiamo trovare un “cervello” per esso. Visita aistudio.google.com 🔗 e crea una chiave API gratuita per Gemini 2.0 Flash (è gratuita al momento della scrittura).
Assicurati di copiare la chiave API, poiché non verrà visualizzata di nuovo. Dovrai fornirla durante la creazione delle credenziali di n8n.
Successivamente, aggiungeremo i seguenti nodi:
- Trigger chat
- Agente AI
- Modello: Gemini 2.0 Flash
- (opzionale) Memoria Semplice
Ora, testiamo la chat chiedendogli qualcosa.
Ottimo! A questo punto, hai essenzialmente ricreato un’interfaccia per quel modello.
Ma sai cosa c’è di ancora più bello di un cervello altamente intelligente intrappolato in una scatola? Un cervello altamente intelligente che può recuperare informazioni fresche dal web.
Passaggio 3: Configura localmente SearXNG
Esiste un utile repository GitHub pubblico chiamato searxng/searxng-docker 🔗 che contiene un file docker-compose.yml
. Similmente a quanto fatto per n8n, estrarremo ciò di cui abbiamo bisogno da questo repository. In questo caso, useremo tutto, che dovrà essere correttamente unito con il docker-compose.yml
che abbiamo assemblato in precedenza.
Crea un Caddyfile
nella cartella.
workspace
├── docker-compose.yml
├── Caddyfile
└── n8n
└── backup
├── credentials
└── workflows
E aggiungi quanto segue all’interno.
{
admin off
log {
output stderr
format filter {
# Conserva i primi 8 bit da IPv4 e 32 bit da IPv6
request>remote_ip ip_mask 8 32
request>client_ip ip_mask 8 32
# Rimuovi informazioni identificabili
request>remote_port delete
request>headers delete
request>uri query {
delete url
delete h
delete q
}
}
}
}
{$SEARXNG_HOSTNAME}
tls {$SEARXNG_TLS}
encode zstd gzip
@api {
path /config
path /healthz
path /stats/errors
path /stats/checker
}
@search {
path /search
}
@imageproxy {
path /image_proxy
}
@static {
path /static/*
}
header {
# CSP (https://content-security-policy.com)
Content-Security-Policy "upgrade-insecure-requests; default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; form-action 'self' https://github.com/searxng/searxng/issues/new; font-src 'self'; frame-ancestors 'self'; base-uri 'self'; connect-src 'self' https://overpass-api.de; img-src * data:; frame-src https://www.youtube-nocookie.com https://player.vimeo.com https://www.dailymotion.com https://www.deezer.com https://www.mixcloud.com https://w.soundcloud.com https://embed.spotify.com;"
# Disabilita alcune funzionalità del browser
Permissions-Policy "accelerometer=(),camera=(),geolocation=(),gyroscope=(),magnetometer=(),microphone=(),payment=(),usb=()"
# Imposta la politica del referrer
Referrer-Policy "no-referrer"
# Forza i client a usare HTTPS
Strict-Transport-Security "max-age=31536000"
# Impedisce lo sniffing del tipo MIME dal Content-Type dichiarato
X-Content-Type-Options "nosniff"
# X-Robots-Tag (commenta per consentire l'indicizzazione del sito)
X-Robots-Tag "noindex, noarchive, nofollow"
# Rimuovi l'header "Server"
-Server
}
header @api {
Access-Control-Allow-Methods "GET, OPTIONS"
Access-Control-Allow-Origin "*"
}
route {
# Politica di cache
header Cache-Control "max-age=0, no-store"
header @search Cache-Control "max-age=5, private"
header @imageproxy Cache-Control "max-age=604800, public"
header @static Cache-Control "max-age=31536000, public, immutable"
}
# SearXNG (uWSGI)
reverse_proxy localhost:8080 {
header_up X-Forwarded-Port {http.request.port}
header_up X-Real-IP {http.request.remote.host}
# https://github.com/searx/searx-docker/issues/24
header_up Connection "close"
}
Per brevità, ecco il docker-compose.yml
finale. Aggiunge caddy
e redis
per supportare searxng
.
volumes:
n8n_storage:
postgres_storage:
caddy-data:
caddy-config:
valkey-data2:
networks:
workspace:
# Gli attributi elencati in x-n8n sono necessari sia per il servizio n8n che per n8n-import.
# Lo definiamo solo una volta, evitando stupidi errori di battitura e avendo meno righe.
# È unibile nei servizi con la sintassi <<: *service-n8n (vedi sotto).
x-n8n: &service-n8n
image: n8nio/n8n:latest
networks:
- workspace
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_USER=${POSTGRES_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
- N8N_DIAGNOSTICS_ENABLED=false
- N8N_PERSONALIZATION_ENABLED=false
- N8N_ENCRYPTION_KEY
- N8N_USER_MANAGEMENT_JWT_SECRET
services:
postgres:
image: postgres:16-alpine
hostname: postgres
networks:
- workspace
restart: unless-stopped
environment:
- POSTGRES_USER
- POSTGRES_PASSWORD
- POSTGRES_DB
volumes:
- postgres_storage:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}']
interval: 5s
timeout: 5s
retries: 10
# n8n-import ha l'unico scopo e ciclo di vita di leggere le credenziali e i flussi di lavoro memorizzati
# sul nostro computer nella cartella n8n/backup e iniettarli in n8n.
n8n-import:
<<: *service-n8n
hostname: n8n-import
container_name: n8n-import
entrypoint: /bin/sh
command:
- '-c'
- 'n8n import:credentials --separate --input=/backup/credentials && n8n import:workflow --separate --input=/backup/workflows'
volumes:
- ./n8n/backup:/backup
depends_on:
postgres:
condition: service_healthy
n8n:
<<: *service-n8n
hostname: n8n
container_name: n8n
restart: unless-stopped
ports:
- 5678:5678
volumes:
- n8n_storage:/home/node/.n8n
- ./n8n/backup:/backup
- ./shared:/data/shared
depends_on:
postgres:
condition: service_healthy
n8n-import:
condition: service_completed_successfully
caddy:
container_name: caddy
image: docker.io/library/caddy:2-alpine
networks:
- workspace
restart: unless-stopped
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data:rw
- caddy-config:/config:rw
environment:
- SEARXNG_HOSTNAME=${SEARXNG_HOSTNAME:-http://localhost:8080}
- SEARXNG_TLS=${LETSENCRYPT_EMAIL:-internal}
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
logging:
driver: 'json-file'
options:
max-size: '1m'
max-file: '1'
redis:
container_name: redis
image: docker.io/valkey/valkey:8-alpine
command: valkey-server --save 30 1 --loglevel warning
restart: unless-stopped
networks:
- workspace
volumes:
- valkey-data2:/data
cap_drop:
- ALL
cap_add:
- SETGID
- SETUID
- DAC_OVERRIDE
logging:
driver: 'json-file'
options:
max-size: '1m'
max-file: '1'
searxng:
container_name: searxng
image: docker.io/searxng/searxng:latest
restart: unless-stopped
networks:
- workspace
ports:
- '8080:8080'
volumes:
- ./searxng:/etc/searxng:rw
environment:
- SEARXNG_BASE_URL=https://${SEARXNG_HOSTNAME:-localhost:8080}/
- UWSGI_WORKERS=${SEARXNG_UWSGI_WORKERS:-4}
- UWSGI_THREADS=${SEARXNG_UWSGI_THREADS:-4}
cap_drop:
- ALL
cap_add:
- CHOWN
- SETGID
- SETUID
logging:
driver: 'json-file'
options:
max-size: '1m'
max-file: '1'
Per prima cosa, dovresti spegnere la macchina:
docker compose down
Ora, aggiorna i contenuti del tuo docker-compose.yml
. Infine, riavvia tutto.
docker compose up
Questo scaricherà i componenti necessari e renderà il servizio disponibile su http://localhost:8080
. Quando accedi a questo URL nel tuo browser, troverai un’interfaccia semplice per utilizzare il servizio.
Dopo aver eseguito una query iniziale, vedrai che espone un endpoint GET /search
. Questo endpoint richiede un parametro di query chiamato q
, che dovrebbe contenere la query di ricerca. Dai pannelli laterali, puoi anche aggiungere parametri di query opzionali per controllare aspetti come time_range
(con opzioni come day
, month
o year
). Possiamo anche specificare la language
e safesearch
. Per maggiori informazioni sulla personalizzazione, consulta la documentazione ufficiale di SearXNG 🔗.
Tuttavia, c’è una cosa che impedisce all’Agente AI di n8n di utilizzare questo servizio: SearXNG deve comunicare in formato JSON. Attualmente, restituisce solo HTML (che è adatto per la visualizzazione in un browser).
Configurarlo per restituire risposte in formato JSON è semplice. Potresti aver notato che, dopo aver eseguito searxng
, è apparsa una nuova cartella chiamata searxng
.
workspace
├── docker-compose.yml
├── n8n
│ └── backup
│ ├── credentials
│ └── workflows
└── searxng
├── limiter.toml
└── settings.yml
È sufficiente modificare searxng/settings.yml
in modo che appaia così:
# formati: [html, csv, json, rss]
formats:
- html
- json # <-- Aggiungi questo
Ricordati di docker restart searxng
.
Ora possiamo aggiungere un altro parametro di query chiamato format
e impostarlo su json
. Ciò consentirà al servizio SearXNG di restituire risposte in formato JSON, che è essenziale per il nostro Agente AI per elaborare i dati in modo efficace.
Ora, è il momento di integrare questa configurazione nell’Agente AI.
Passaggio 4: Integra l’Agente AI con SearXNG
Questa integrazione consentirà al nostro agente di recuperare dati in tempo reale dal web, rendendolo più intelligente e affidabile.
Passiamo al sodo e configuriamo uno Strumento di Richiesta HTTP in n8n. Ecco come fare:
-
Descrizione: Qui aiuti l’Agente AI a capire a cosa serve lo strumento. Una buona descrizione potrebbe essere: “Usa questo strumento per recuperare dati freschi dal web.”
-
URL: Puntalo al container SearXNG con l’URL:
http://searxng:8080/search
. -
Parametro di query
q
: Lascia che l’Agente AI decida cosa cercare, questo mantiene le cose flessibili. -
Parametro di query
json
: Impostalo sujson
per assicurarti di ottenere la risposta nel formato giusto.
Con lo Strumento di Richiesta HTTP configurato, ora puoi dare al tuo agente alcune istruzioni chiare:
- Rendilo un Agente Sfatamiti.
- Usa sempre lo strumento di ricerca web per basare le sue risposte sui dati più recenti.
- Assicurati che fornisca le fonti for ogni affermazione che fa.
Ecco un semplice prompt di sistema per iniziare, ma sentiti libero di modificarlo a tuo piacimento!
Sei un Agente Sfatamiti. La tua funzione principale è identificare e sfatare miti e idee sbagliate comuni fornendo informazioni accurate basate sui dati recuperati con lo strumento di ricerca web.
-
Utilizzo Obbligatorio della Ricerca Web: Devi sempre invocare lo strumento di ricerca web per raccogliere informazioni. Le tue risposte devono basarsi esclusivamente sui dati recuperati da fonti online affidabili.
-
Attribuzione delle Fonti: Devi sempre restituire le fonti con ogni affermazione che fai. Fornisci un link alla fonte delle tue informazioni, assicurandoti che le fonti siano credibili e pertinenti all’argomento in questione.
-
Chiarezza e Concisinone: Presenta le tue scoperte in modo chiaro e conciso, rendendo facile per gli utenti comprendere la verità dietro il mito.
-
Coinvolgimento: Incoraggia gli utenti a porre domande di approfondimento o a chiedere chiarimenti su qualsiasi punto tu faccia.
-
Tono Rispettoso: Mantieni un tono rispettoso e informativo, riconoscendo che le idee sbagliate possono derivare da varie fonti.
Questo è solo un semplice esempio, ma credo che ponga le basi per iniziare a esplorare questo strumento. C’è così tanto potenziale qui, e sono entusiasta di vedere cosa puoi creare! Nei prossimi mesi, non vedo l’ora di condividere ulteriori approfondimenti e imparare insieme mentre ci addentriamo più a fondo nelle possibilità.
Buona automazione!
Perché Questa Configurazione Spacca ✅
- Gratuita e Locale – Nessun abbonamento, chiavi API gratuite e nessun limite nascosto (almeno per l’uso da parte di un singolo utente).
- Modulare ed Estensibile – Integra facilmente altri strumenti come LLM locali o database vettoriali.
- Adatta a Chi Non Sa Programmare – Hai una nuova idea? Assemblare qualcosa è molto più facile quando non devi programmare (specialmente dopo aver programmato tutto il giorno per qualcun altro). Invece, colleghi semplicemente dei blocchi. Sebbene ci siano delle complessità, lo trovo utile per la prototipazione e la sperimentazione. Inoltre, ti permette di creare chat, moduli a più passaggi o persino esporre flussi di lavoro come endpoint, consentendoti di creare la tua interfaccia utente.
- (Non) Privata per Progettazione – Stiamo usando Gemini 2.0 Flash, il che significa che i nostri dati vengono elaborati sui loro server per definizione. Questo potrebbe non essere necessariamente un problema, a seconda del tuo caso d’uso. Se hai bisogno di mantenere i tuoi dati al 100% privati, puoi tornare alle istruzioni di Ollama che abbiamo filtrato durante il Passaggio 1 e usare quello invece.
Perché non usare i nodi della community di n8n?
Se decidi di sostituire n8n con un’implementazione ottimizzata personalizzata in futuro, non dovrai fare alcun lavoro aggiuntivo per gli altri componenti come SearXNG o Postgres. Puoi semplicemente sostituire il servizio n8n con il tuo server Python (o assumere qualcun altro per farlo per te), e tutto il resto continuerà a funzionare come prima.
Questo approccio rende molto più facile la transizione al cloud se crei un flusso di lavoro di valore che desideri rendere disponibile online. Ad esempio, puoi copiare e incollare quel docker-compose.yml
in Coolify 🔗 per replicare ciò che abbiamo fatto localmente su un server remoto.
Hai Domande? 💬
Questa configurazione è solo il punto di partenza, c’è molto che puoi costruire da qui. Se incontri problemi o vuoi aiuto per esplorare idee, sentiti libero di lasciare un commento o di connetterti con me su Twitter/LinkedIn.