Alternative self-hosted a Firecrawl per un VPS
Confronto tra Draco, Hound e Firecrawl self-hosted: RAM, browser headless, API compatibili, installazione con versione bloccata e collegamento MCP.
Che cosa deve fare un'alternativa self-hosted a Firecrawl
Un'alternativa self-hosted a Firecrawl ha un solo compito: ricevere un URL e restituire la pagina come markdown pulito che un agent possa leggere. Le API in hosting applicano un costo per pagina. La spesa quindi aumenta in base a quanto il tuo agent esplora. Un VPS che già paghi può svolgere lo stesso lavoro. I progetti si distinguono per una domanda: sul tuo server deve essere avviato un browser headless, cioè un motore di browser reale eseguito senza interfaccia grafica?
La risposta determina l'occupazione di memoria, il costo per pagina e le pagine che vengono restituite vuote. Questa guida confronta Draco, Hound e la release self-hosted di Firecrawl, installa quella più leggera a una versione bloccata e la collega a un agent tramite MCP (model context protocol).
I quattro progetti e che cosa sono realmente
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 fornisce alcuna container image e non avvia browser.
Firecrawl self-hosted è il motore alla base del prodotto ospitato, 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. Include la coda di crawling completa, ma richiede l'esecuzione di un piccolo sistema distribuito.
Hound risiede 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 il fetch semplice viene bloccato.
Trawl è incluso perché compare durante le ricerche sugli altri progetti, ma svolge un compito diverso. Risolve challenge JavaScript e CAPTCHA con Firefox modificato per applicare patch al fingerprint, come alternativa a FlareSolverr in uno stack multimediale *arr. Non è un estrattore di markdown. La sezione seguente sull'uso corretto spiega perché questa distinzione determina se includerlo o meno nello stack dell'agent.
Perché il pool di browser è il punto in cui i piccoli VPS esauriscono le risorse
Ogni scheda del browser aperta corrisponde a un processo renderer separato, che mantiene il proprio DOM (document object model) e il proprio heap JavaScript. Il consumo di memoria cresce quindi con il numero di pagine aperte nello stesso momento, non con il 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. Questi sono limiti scelti dai progetti e valori pubblicati, non misurazioni di 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 serve. Quando il server esaurisce la memoria, il kernel termina un processo tramite l'out-of-memory killer. Di conseguenza, un container scompare da docker compose ps senza che venga scritto alcun errore nel log dell'applicazione. Dopo ogni riavvio di cui non riesci a spiegare la causa, leggi dmesg -T | tail. Riserva 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.
C'è un altro dettaglio del browser che può costare un'intera serata di troubleshooting. Docker assegna a ogni container 64 MB di memoria condivisa in /dev/shm, e Chromium vi colloca i buffer dei renderer; per questo può arrestarsi durante il caricamento di pagine pesanti. Entrambi gli stack browser aumentano questo valore: il file compose di Hound contiene shm_size: "1gb". Copia quella riga in qualsiasi immagine che costruisci intorno a Playwright.
Qualità dell’estrazione sulle pagine con molto JavaScript
Con HTML statico, un blog con rendering lato server, una pagina di documentazione o un articolo di cronaca restituiscono tutti markdown quasi identico e vince l’opzione più veloce. La differenza emerge sulle pagine con rendering lato client, in cui l’HTML fornito è un guscio vuoto e il testo arriva da JavaScript dopo il caricamento.
Draco aumenta il livello di elaborazione per gradi. Il livello 0 e il livello 1 analizzano l’HTML senza eseguire JavaScript. Il livello 2 esegue il JavaScript della pagina all’interno di un isolamento V8 in-process, cioè il motore JavaScript senza un browser che lo gestisca, e il README specifica che in questo contesto il codice della pagina non riceve binding alle funzionalità dell’host. Questo copre molte applicazioni a pagina singola usando una frazione della memoria richiesta da un browser. Quando Draco incontra un limite che non può superare, draco scrape termina con codice 3, needs_browser. Verificalo negli script, perché un file vuoto con codice di uscita zero è il tipo di errore che altera silenziosamente il contesto di un agente:
draco scrape https://example.com > page.md
echo "exit=$?"Il servizio playwright-service di Firecrawl controlla un’istanza reale di Chromium, quindi esegue il rendering come farebbe un browser. La versione self-hosted non è comunque il prodotto hosted: la documentazione specifica che le istanze self-hosted non hanno accesso a Fire Engine, quindi mancano le funzionalità anti-blocco e la rotazione degli indirizzi IP del servizio cloud; inoltre, gli endpoint /agent e /browser non sono supportati. Hound si colloca volutamente nel mezzo. Recupera i contenuti tramite HTTP e aumenta il livello di elaborazione per ogni richiesta. Il browser mantenuto attivo viene chiuso dopo un timeout di inattività, quindi un host poco utilizzato resta vicino al consumo di base.
Installare Draco con una versione bloccata
Il README documenta un installer a riga singola. Leggere cosa esegue 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 SHA256SUMSIl 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 ricominciare.
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 find non è decorativo: 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 accede al sistema. 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 restituisce una risposta non appena il daemon è in ascolto. Connection refused indica che non lo è; quindi leggere 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 l'uno o l'altro. Per maggiori dettagli sui file di unità: unità e timer di servizio systemd.
Ora eseguire il fetch nel modo in cui lo farà l'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 demone fetch fuori da Internet pubblico
Un'API fetch senza autenticazione è un proxy aperto. Chiunque possa raggiungere la porta può fare in modo che il server richieda qualsiasi URL usando il tuo indirizzo IP. La segnalazione di abuso arriverà al provider, non all'autore dell'abuso. I flag documentati serve di Draco non includono una chiave API. La protezione deve quindi essere affidata alla rete. Mantieni il bind predefinito 127.0.0.1 quando l'agent viene eseguito sullo stesso host. Se l'agent si trova altrove, collega entrambi gli endpoint a un tunnel privato. Una VPN WireGuard gestita autonomamente è in genere la soluzione più adatta. Esegui il bind sull'indirizzo del tunnel anziché su 0.0.0.0. Verifica quindi da un altro computer che l'IP pubblico non risponda. Nozioni di base sul firewall ufw e account utente con privilegi minimi coprono i due aspetti della configurazione.
Il codice del tuo agent cambierà? La compatibilità delle 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 un nuovo URL di base e nient'altro. Verifica che cosa è cambiato dall'altra parte: la pagina di self-hosting di Firecrawl ora esegue i test con /v2/crawl e gli SDK attuali usano v2, quindi 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 invece del solo codice di stato, perché è nei nomi dei campi che queste implementazioni differiscono.
Robots.txt, i limiti di frequenza e il limite da non superare
Draco legge robots.txt per impostazione predefinita e --ignore-robots disabilita questa funzione. Anche Firecrawl documenta lo stesso comportamento predefinito. Non modificare nessuna delle due impostazioni. Imposta quindi la velocità desiderata: --delay inserisce un intervallo in millisecondi tra le richieste e --max-concurrency limita i processi paralleli, con 8 come valore predefinito del daemon. Su una connessione VPS condivisa, un valore compreso tra 2 e 4 riduce il carico e raramente rallenta il lavoro complessivo, perché un sito che inizia ad applicare il rate limiting può farti perdere più minuti di quanti ne risparmi aumentando la concorrenza. Memorizza nella cache i dati recuperati, così una seconda esecuzione dell'agent non genera alcun costo per la fonte. Questo è anche il costo più basso nella sezione controllare quanto ti costa un agent 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 rifiuta esplicitamente il traffico automatizzato proveniente dal sito. Aggirarlo può violare i termini di utilizzo del sito e, in alcuni luoghi, anche la legge. Per questo motivo, questa guida tratta solo l'infrastruttura per il recupero dei dati e si ferma a questo 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 inopportuna qualsiasi pipeline basata su di esse. Quando una fonte è così importante, cerca il relativo feed RSS, l'API pubblica oppure un'esportazione in blocco. Ognuna di queste opzioni è più economica da eseguire e nessuna smette di funzionare nella settimana in cui il sistema di challenge cambia.
Collegarlo a un agent tramite MCP
MCP (model context protocol) è l’interfaccia che un agent 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 visualizzati dall’agent come draco_scrape, draco_search e l’insieme draco_interact_*. Stdio funziona soltanto quando il processo dell’agent e il binario si trovano sulla stessa macchina, perché il trasporto usa lo standard input di quel processo. Per un agent 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 modalità di trasporto e gli elementi da esporre sono descritti in eseguire server MCP su un VPS.
Recuperare le coppie tramite la ricerca. Un agent che può solo recuperare contenuti resta in attesa che gli vengano forniti gli URL. Aggiungi un’istanza SearXNG self-hosted per la ricerca e potrà trovarli autonomamente, con la stessa struttura della funzionalità di ricerca nel browser basata su SearXNG. Dopo l’avvio del demone, il servizio è condiviso da tutti gli agent AI self-hosted che esegui.
FAQ
Mi serve un browser headless per recuperare pagine per un agente AI?
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 da una conversione da HTML a Markdown. È il metodo usato da Draco nei livelli inferiori, con un tempo di circa 300 ms per pagina senza browser, secondo i dati del progetto. Un browser è utile per le applicazioni renderizzate dal client, nelle quali l'HTML ricevuto è soltanto uno scheletro vuoto. L'isolato V8 di Draco gestisce gran parte dei casi intermedi senza avviare un processo browser e restituisce il codice di uscita 3, needs_browser, quando non è in grado di farlo.
Quanta RAM richiede Firecrawl self-hosted su un VPS?
Il file compose imposta un limite di 8 GB per il container api e di 4 GB per il container Playwright. Lo stesso stack avvia anche Redis, RabbitMQ, PostgreSQL e FoundationDB. Prevedi 8 GB. Su una macchina da 2 GB, il kernel OOM killer termina i container sotto carico. Il primo segnale è un container riavviato in docker compose ps, senza informazioni utili nel log dell'applicazione. Conferma 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. Ignora i campi della richiesta che non riconosce, quindi un client sviluppato per Firecrawl v1 richiede in genere 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. Verifica prima ogni chiamata eseguita dal tuo 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. Draco e Firecrawl rispettano robots.txt per impostazione predefinita. Il flag di override esiste per i siti che possiedi o per i quali hai un'autorizzazione scritta alla scansione. I limiti di frequenza vengono applicati comunque dal server remoto, quindi un --delay corretto con una concorrenza bassa mantiene operativo il tuo indirizzo IP. Uno stack che funziona soltanto aggirando una challenge wall è uno stack che può smettere di funzionare senza preavviso.