Alternative self-hosted a Firecrawl per un VPS
Confronto tra Draco, Hound e Firecrawl self-hosted: RAM, browser headless, compatibilita API, installazione a versione fissata e collegamento MCP.
Cosa deve fare un'alternativa self-hosted a Firecrawl
Un'alternativa self-hosted a Firecrawl ha un solo compito: ricevere un URL e restituire il contenuto della pagina in formato Markdown pulito, leggibile da un agent. Le API in hosting applicano un costo per pagina, quindi la spesa aumenta in base alla quantità di contenuti che l'agent richiede. Un VPS già disponibile può eseguire lo stesso lavoro. I progetti si distinguono per una domanda: sul server deve essere avviato un browser headless, cioè un motore browser reale eseguito senza interfaccia grafica?
La risposta determina l'occupazione di memoria, il costo per ogni pagina e le pagine che vengono restituite vuote. Questa guida confronta Draco, Hound e la release self-hosted di Firecrawl, installa la soluzione più leggera a una versione fissata e la collega a un agent tramite MCP (model context protocol).
I quattro progetti e la loro funzione effettiva
Draco è un singolo binario scritto in Rust, distribuito con licenza MIT o Apache-2.0. La release v0.20.5 è stata pubblicata il 16 luglio 2026. draco scrape <url> stampa markdown su stdout. draco serve esegue un daemon che risponde su 127.0.0.1:3002, la porta utilizzata da Firecrawl. Non include alcuna container image e non avvia alcun browser.
Firecrawl self-hosted è il motore alla base del prodotto hosted, distribuito con licenza AGPL-3.0. Il relativo docker-compose.yaml definisce sette servizi: playwright-service, api, redis, rabbitmq, nuq-postgres, foundationdb e foundationdb-init. In questo modo si ottiene la coda di crawling effettiva, al costo di gestire un piccolo sistema distribuito.
Hound si trova nel repository master-fetch ed è pubblicato su PyPI come hound-mcp, con licenza MIT e versione 13.0.1 al 3 agosto 2026. Richiede Python 3.11 o versioni successive. È prima di tutto un server MCP e solo in seconda battuta un fetcher: prova a usare HTTP semplice e avvia un browser Patchright soltanto quando la richiesta HTTP semplice viene bloccata.
Trawl è incluso perché durante la ricerca degli altri progetti ci si imbatte anche in questo strumento, che svolge una funzione diversa. Risolve le challenge JavaScript e i CAPTCHA usando Firefox con fingerprint modificato, come alternativa a FlareSolverr in uno stack multimediale *arr. Non è un estrattore di markdown. La sezione seguente sull'utilizzo corretto spiega perché questa distinzione determina se il progetto sia adatto o meno al proprio stack di agent.
Perché il pool di browser è il punto in cui muoiono i piccoli VPS
Ogni scheda del browser aperta è un processo renderer separato, con il proprio DOM (document object model) e il proprio heap JavaScript. La memoria cresce quindi in base al numero di pagine aperte nello stesso momento, non al numero di pagine recuperate nell’arco della giornata. Due di questi progetti dichiarano questo costo nei rispettivi file compose.
The data behind this chart
[
{
"label": "Firecrawl api",
"memory_limit_gb": 8
},
{
"label": "Firecrawl playwright",
"memory_limit_gb": 4
},
{
"label": "Hound (browser included)",
"memory_limit_gb": 3
}
]Il file compose di Firecrawl imposta un limite di 8 GB per il container api e di 4 GB per il container Playwright, con limiti di swap corrispondenti. Il file compose di Hound imposta 3 GB per un unico container che include Chromium. Si tratta dei limiti scelti e pubblicati dai progetti, non di misurazioni effettuate su un sistema inattivo. Inoltre, Redis, RabbitMQ, PostgreSQL e FoundationDB richiedono comunque la propria quota oltre ai valori indicati per Firecrawl.
Un limite superiore alla RAM disponibile non risolve il problema. Quando il server esaurisce la memoria, il kernel termina un processo tramite il meccanismo out-of-memory killer. Di conseguenza, un container scompare da docker compose ps senza che venga scritto alcun errore nel log dell’applicazione. Leggi dmesg -T | tail dopo ogni riavvio di cui non riesci a spiegare la causa. Prevedi 8 GB per l’intero stack Firecrawl e considera 4 GB il minimo per un server di test. L’impostazione dei valori per singolo servizio è descritta in limiti di memoria in Docker Compose.
Un altro dettaglio del browser può costare un’intera serata di troubleshooting. Docker assegna a un container 64 MB di memoria condivisa in /dev/shm. Chromium vi colloca i buffer dei renderer, quindi può arrestarsi durante il caricamento di pagine pesanti. Entrambi gli stack browser aumentano questo valore. Il file compose di Hound include shm_size: "1gb". Copia quella riga in qualsiasi image che costruisci attorno a Playwright.
Qualità dell'estrazione sulle pagine con molto JavaScript
Su HTML statico, blog con rendering lato server, pagine di documentazione e articoli di news restituiscono tutti Markdown quasi identico, quindi vince il più veloce. La differenza emerge sulle pagine con rendering lato client, dove l'HTML ricevuto è solo un guscio vuoto e il testo viene caricato da JavaScript dopo il caricamento.
Draco aumenta il livello di elaborazione per tier. Il tier 0 e il tier 1 analizzano l'HTML senza eseguire JavaScript. Il tier 2 esegue il JavaScript della pagina all'interno di un isolate V8 in-process, cioè il motore JavaScript senza un browser che lo ospiti; il README specifica che, in questo ambiente, il codice della pagina non riceve binding alle funzionalità dell'host. Questo copre molte applicazioni single-page usando una frazione della memoria richiesta da un browser. Quando Draco incontra un ostacolo che non può superare, draco scrape termina con codice 3, needs_browser. Verificatelo negli script, perché un file vuoto con codice di uscita 0 è il tipo di errore che contamina silenziosamente il contesto di un agente:
draco scrape https://example.com > page.md
echo "exit=$?"Il servizio playwright-service di Firecrawl utilizza un'istanza reale di Chromium, quindi esegue il rendering come un browser. La build self-hosted non equivale comunque al prodotto in hosting: la documentazione specifica che le istanze self-hosted non hanno accesso a Fire Engine. Di conseguenza mancano le funzioni anti-blocking e la rotazione degli indirizzi IP del servizio cloud, mentre gli endpoint /agent e /browser non sono supportati. Hound si colloca intenzionalmente tra le due soluzioni. Recupera i contenuti tramite HTTP e aumenta il livello di elaborazione per ogni richiesta; il browser mantenuto in memoria viene chiuso dopo un timeout di inattività, quindi un server poco utilizzato resta vicino al consumo di base.
Installare Draco con una versione bloccata
Il README documenta un installer a riga singola. Leggere cosa fa prima di passarlo a una shell: installa in $HOME/.draco/bin/draco, usa sempre la release latest e non verifica alcuna firma o hash. Su un server, bloccare la versione e verificare il download.
cd /tmp
curl -fsSLO https://github.com/0xchasercat/draco/releases/download/v0.20.5/draco-linux-x86-64.tar.gz
curl -fsSLO https://github.com/0xchasercat/draco/releases/download/v0.20.5/SHA256SUMS
sha256sum --ignore-missing -c SHA256SUMSQuesto comando stampa draco-linux-x86-64.tar.gz: OK. Una riga FAILED indica che i byte disponibili localmente non corrispondono a quelli pubblicati dal progetto. Eliminarli e riprovare.
mkdir -p draco-v0.20.5
tar -xzf draco-linux-x86-64.tar.gz -C draco-v0.20.5
sudo install -m 755 "$(find draco-v0.20.5 -type f -name draco | head -n1)" /usr/local/bin/draco
draco scrape https://example.comL'ultimo comando stampa la pagina di esempio in formato Markdown in molto meno di un secondo. Il valore find non è ornamentale: la struttura dell'archivio non fa parte del contratto pubblico del progetto e l'installer ufficiale individua il binario nello stesso modo.
Eseguire il daemon con un account dedicato invece che con l'utente con cui si effettua l'accesso. Scrivere /etc/systemd/system/draco.service:
[Unit]
Description=Draco fetch daemon
After=network-online.target
Wants=network-online.target
[Service]
User=draco
ExecStart=/usr/local/bin/draco serve --host 127.0.0.1 --port 3002 --max-concurrency 4
Restart=on-failure
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
[Install]
WantedBy=multi-user.targetsudo useradd --system --no-create-home --shell /usr/sbin/nologin draco
sudo systemctl daemon-reload
sudo systemctl enable --now draco
curl -s http://127.0.0.1:3002/health/health risponde non appena il daemon è in ascolto. Connection refused indica che non è in ascolto; leggere quindi journalctl -u draco -n 50. La causa più comune è che un altro processo stia già usando la porta 3002, che è anche la porta predefinita di Firecrawl; --port sposta uno dei due servizi. Per maggiori dettagli sui file unit: file unit e timer di systemd.
Ora eseguire il fetch nel modo in cui lo farà il proprio agent:
curl -X POST http://127.0.0.1:3002/v1/scrape \
-H 'content-type: application/json' \
-d '{"url": "https://example.com", "formats": ["markdown"]}'Mantieni il daemon di fetch fuori da Internet pubblico
Un'API di fetch senza autenticazione è un proxy aperto. Chiunque possa raggiungere la porta può fare in modo che il server effettui richieste verso qualsiasi URL usando il tuo indirizzo IP. La segnalazione di abuso arriverà al tuo provider, non all'autore dell'abuso. I flag documentati di Draco serve non includono una API key, quindi la protezione deve essere a livello di rete. Mantieni il bind predefinito 127.0.0.1 quando l'agent viene eseguito sullo stesso server. Quando l'agent si trova altrove, collega entrambe le estremità tramite un tunnel privato. Una VPN WireGuard ospitata autonomamente è in genere la soluzione più indicata. Esegui il bind sull'indirizzo del tunnel anziché su 0.0.0.0. Verifica quindi da un'altra macchina che l'IP pubblico non risponda a nessuna richiesta. Le nozioni di base del firewall ufw e gli account utente con privilegi minimi coprono i due aspetti.
Il codice del tuo agent cambierà? Compatibilità API nella pratica
Draco risponde alle route v1 di Firecrawl: /v1/scrape, /v1/map, /v1/crawl, /v1/batch/scrape e /v1/search, e il relativo README specifica che i campi sconosciuti vengono accettati e ignorati. Un agent che invia già richieste a /v1/scrape richiede soltanto un nuovo URL di base. Osserva invece cosa è cambiato dall'altra parte: la pagina ufficiale di self-hosting di Firecrawl ora esegue i test con /v2/crawl e gli SDK attuali usano v2. Di conseguenza, un client v2 indirizzato a Draco richiede una route che Draco non pubblica. Testa ogni chiamata con curl prima di modificare il codice dell'agent e leggi il corpo JSON anziché limitarti al codice di stato, perché le differenze tra queste implementazioni riguardano soprattutto i nomi dei campi.
Robots.txt, limiti di frequenza e il limite da non superare
Per impostazione predefinita, Draco legge robots.txt e --ignore-robots disabilita questo comportamento. Firecrawl documenta la stessa impostazione predefinita. Lascia entrambi invariati. Imposta quindi la frequenza desiderata: --delay inserisce un intervallo di millisecondi tra le richieste e --max-concurrency limita i processi paralleli, con 8 come valore predefinito del demone. Un valore compreso tra 2 e 4 è meno gravoso su una connessione VPS condivisa e raramente rallenta il risultato complessivo, perché un sito che inizia ad applicare il rate limiting richiede più minuti di quanti ne faccia risparmiare l'aumento della concorrenza. Memorizza nella cache i dati recuperati, così una seconda esecuzione dell'agente non genera costi per la fonte. Questa è anche la voce di costo più bassa in controllare quanto ti costa un agente AI.
I sistemi di challenge sono un argomento distinto e Trawl è progettato proprio per gestirli: Cloudflare Turnstile, reCAPTCHA, hCaptcha e GeeTest. Un sistema di challenge è, in termini semplici, il rifiuto del traffico automatizzato da parte di un sito. Aggirarlo può violare i termini di utilizzo del sito e, in alcuni luoghi, anche la legge; per questo questa guida tratta l'infrastruttura di recupero dei dati e si ferma a quel punto. Le stesse tecniche che consentono di superare un sistema di challenge sono quelle che i proprietari dei siti monitorano e bloccano, rendendo fragile e inoltre poco rispettosa qualsiasi pipeline basata su di esse. Quando una fonte è così importante, cerca il relativo feed RSS, la sua API pubblica oppure un'esportazione in blocco. Ognuna di queste opzioni è più economica da eseguire e nessuna smette di funzionare nella settimana in cui cambia il sistema di challenge.
Collegarlo a un agente tramite MCP
MCP (model context protocol) è l’interfaccia che un agente usa per chiamare uno strumento. Draco include un server MCP nello stesso binario, tramite stdio:
{ "mcpServers": { "draco": { "command": "draco", "args": ["mcp"] } } }Gli strumenti vengono quindi mostrati all’agente come draco_scrape, draco_search e l’insieme draco_interact_*. Stdio funziona solo quando il processo dell’agente e il binario si trovano sulla stessa macchina, perché il trasporto usa lo standard input di quel processo. Per un agente su un altro host, Hound espone invece MCP tramite HTTP: hound --http --host 127.0.0.1 --port 8765 pubblica un endpoint all’indirizzo http://127.0.0.1:8765/mcp, raggiungibile attraverso il tunnel. Le opzioni di trasporto e gli elementi da esporre sono descritte in esecuzione di server MCP su un VPS.
Recuperare le coppie tramite la ricerca. Un agente che può solo recuperare contenuti attende che gli si forniscano gli URL. Aggiungi un’istanza SearXNG self-hosted per la ricerca e potrà trovarli autonomamente, con la stessa struttura della competenza di ricerca nel browser basata su SearXNG. Quando il demone è attivo, diventa un servizio condiviso per qualunque agente tra quelli self-hosted che esegui.
FAQ
Per recuperare pagine per un agente AI serve un browser headless?
Non per la maggior parte delle pagine. La documentazione renderizzata dal server, i blog e gli articoli di notizie vengono restituiti completi con una semplice richiesta HTTP seguita dalla conversione da HTML a Markdown. È il metodo usato da Draco nei suoi piani inferiori, con un tempo di circa 300 ms per pagina senza browser, secondo i dati del progetto. Un browser è utile per le applicazioni renderizzate sul client, nelle quali l'HTML restituito è soltanto uno scheletro vuoto. L'isolamento V8 di Draco copre gran parte dei casi intermedi senza avviare un processo browser e restituisce il codice di uscita 3, needs_browser, quando non è sufficiente.
Quanta RAM richiede Firecrawl self-hosted su un VPS?
Il relativo file compose imposta un limite di 8 GB per il container api e di 4 GB per il container Playwright. Lo stesso stack avvia inoltre Redis, RabbitMQ, PostgreSQL e FoundationDB. Prevedere 8 GB. Su un server da 2 GB, il kernel OOM killer rimuove i container sotto carico. Il primo segnale è un container riavviato in docker compose ps, senza informazioni utili nel log dell'applicazione. Confermare la causa con dmesg -T | tail.
Draco è un sostituto diretto dell'API Firecrawl?
Per gli endpoint v1, è molto simile. Espone /v1/scrape, /v1/map, /v1/crawl, /v1/batch/scrape e /v1/search e ignora i campi delle richieste che non riconosce. Un client scritto per Firecrawl v1 richiede quindi di solito soltanto un nuovo URL di base. Non è il prodotto hosted: non dispone di un pool di proxy gestito e le route v2 più recenti di Firecrawl non fanno parte dell'interfaccia disponibile. Verificare prima ogni chiamata effettuata dall'agente con curl.
Il self-hosting di uno scraper significa che posso ignorare robots.txt?
No. Il luogo in cui viene eseguito il codice non modifica ciò che un sito ha pubblicato né ciò che i suoi termini consentono. Per impostazione predefinita, Draco e Firecrawl rispettano entrambi robots.txt. Il flag di override è disponibile per i siti di cui si è proprietari o per i quali si dispone di un'autorizzazione scritta al crawling. I limiti di frequenza vengono applicati comunque dal sistema remoto. Un --delay configurato con una bassa concorrenza mantiene operativo il proprio indirizzo IP. Uno stack che funziona soltanto aggirando una challenge wall è uno stack che può smettere di funzionare senza preavviso.