Firecrawl self-hosted nhẹ cho VPS: Draco hay Hound?
So sánh Draco, Hound và Firecrawl self-hosted trên VPS theo RAM, nhu cầu headless browser và API compatibility, kèm cài version cố định và nối MCP.
Một giải pháp thay thế Firecrawl tự host phải làm được gì
Một giải pháp thay thế Firecrawl tự host chỉ có một nhiệm vụ: nhận URL và trả về nội dung trang dưới dạng markdown sạch để agent đọc được. Các API hosted tính phí theo từng trang, nên chi phí tăng theo mức độ agent truy vấn. VPS bạn đã trả phí có thể thực hiện cùng công việc. Các project khác nhau ở một câu hỏi: trên máy của bạn có phải khởi chạy headless browser (browser engine thực chạy mà không mở cửa sổ) hay không?
Câu trả lời này quyết định mức sử dụng memory, chi phí cho mỗi trang và những trang có thể trả về nội dung rỗng. Hướng dẫn này so sánh Draco, Hound và bản Firecrawl self-hosted, cài đặt lựa chọn nhẹ nhất tại một version cố định, rồi kết nối nó với agent qua MCP (model context protocol).
Bốn dự án và bản chất thực sự của từng dự án
Draco là một binary duy nhất, được viết bằng Rust và cấp phép theo MIT hoặc Apache-2.0. Bản phát hành v0.20.5 được công bố vào ngày 16 tháng 7 năm 2026. draco scrape <url> in markdown ra stdout. draco serve chạy một daemon phản hồi trên 127.0.0.1:3002, là cổng mà Firecrawl sử dụng. Dự án không cung cấp container image và không khởi động browser.
Firecrawl self-hosted là engine phía sau sản phẩm hosted, được cấp phép theo AGPL-3.0. docker-compose.yaml của dự án định nghĩa 7 service: playwright-service, api, redis, rabbitmq, nuq-postgres, foundationdb và foundationdb-init. Bạn có được crawl queue thực sự, đổi lại phải vận hành một distributed system nhỏ.
Hound nằm trong repository master-fetch và được phát hành lên PyPI với tên hound-mcp, cấp phép MIT, phiên bản 13.0.1 tính đến ngày 3 tháng 8 năm 2026. Dự án yêu cầu Python 3.11 trở lên. Hound trước hết là một MCP server, sau đó mới là fetcher: nó thử HTTP thông thường và chỉ khởi động browser Patchright khi lần fetch thông thường bị chặn.
Trawl được đề cập vì khi tìm các dự án trên, bạn có thể bắt gặp nó, nhưng nó thực hiện một công việc khác. Trawl xử lý các thử thách JavaScript và CAPTCHA bằng Firefox đã được patch fingerprint, để thay thế FlareSolverr trong media stack dùng *arr. Đây không phải markdown extractor. Phần nói về cách sử dụng có trách nhiệm bên dưới giải thích vì sao điểm khác biệt này quyết định Trawl có phù hợp với agent stack của bạn hay không.
Vì sao browser pool là nơi các VPS nhỏ hết tài nguyên
Mỗi tab trình duyệt đang mở là một renderer process riêng, giữ DOM (document object model) và JavaScript heap riêng. Vì vậy, mức dùng RAM tăng theo số trang mở cùng lúc, không phải theo số trang được fetch trong ngày. Hai dự án này ghi rõ chi phí đó trong các file compose của chúng.
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
}
]File compose của Firecrawl giới hạn api container ở mức 8 GB và Playwright container ở mức 4 GB, kèm swap limit tương ứng. Compose của Hound đặt mức 3 GB cho một container chứa Chromium đi kèm. Đây là các giới hạn do dự án lựa chọn và công bố, không phải số đo của một hệ thống đang nhàn rỗi. Redis, RabbitMQ, PostgreSQL và FoundationDB vẫn cần phần RAM riêng ngoài các con số của Firecrawl.
Một giới hạn cao hơn dung lượng RAM bạn có không giúp ích gì. Khi máy hết RAM, kernel out-of-memory killer sẽ kết thúc một process, nên container biến mất khỏi docker compose ps mà không ghi lỗi vào application log. Hãy đọc dmesg -T | tail sau mọi lần restart mà bạn không giải thích được. Dự trù 8 GB cho toàn bộ Firecrawl stack và coi 4 GB là mức tối thiểu cho máy test. Cách đặt các con số theo từng service được trình bày trong memory limit trong Docker Compose.
Còn một chi tiết về browser có thể khiến bạn mất cả buổi tối. Docker cấp cho container 64 MB shared memory tại /dev/shm, còn Chromium đặt renderer buffer ở đó nên có thể crash khi xử lý các trang nặng. Cả hai browser stack đều tăng mức này: compose của Hound có shm_size: "1gb". Hãy sao chép dòng đó vào mọi image bạn build quanh Playwright.
Chất lượng trích xuất trên các trang phụ thuộc nhiều vào JavaScript
Với HTML tĩnh, blog render phía server, trang tài liệu và bài báo đều trả về markdown gần như giống nhau, nên công cụ nhanh nhất sẽ thắng. Khác biệt xuất hiện trên các trang render phía client, nơi HTML được gửi xuống chỉ là một shell rỗng còn văn bản được JavaScript tải sau khi trang load xong.
Draco nâng cấp theo từng tier. Tier 0 và tier 1 phân tích HTML hoàn toàn không chạy JavaScript. Tier 2 chạy JavaScript của trang trong một V8 isolate nằm trong cùng tiến trình. Đây là JavaScript engine không có browser bao quanh, và README nêu rõ mã của trang không được cấp các binding truy cập host tại đó. Cách này xử lý được nhiều single-page application với mức dùng memory chỉ bằng một phần so với browser. Khi Draco gặp giới hạn không thể vượt qua, draco scrape thoát với code 3, needs_browser. Hãy kiểm tra điều này trong script, vì file rỗng nhưng exit code bằng 0 là lỗi âm thầm làm hỏng context của agent:
draco scrape https://example.com > page.md
echo "exit=$?"playwright-service của Firecrawl điều khiển một Chromium thật, nên nó render đúng những gì browser render. Bản self-hosted vẫn không phải hosted product: tài liệu nêu rõ instance self-hosted không truy cập được Fire Engine, nên tính năng anti-blocking và IP rotation của cloud service không có, đồng thời các endpoint /agent và /browser không được hỗ trợ. Hound được thiết kế để nằm ở giữa. Nó fetch qua HTTP và nâng cấp theo từng request. Browser warm của nó sẽ đóng sau một khoảng idle timeout, nên máy chủ ít hoạt động vẫn gần với mức tài nguyên cơ bản.
Cài đặt Draco với phiên bản cố định
README có hướng dẫn cài đặt bằng một lệnh. Hãy đọc kỹ lệnh này trước khi pipe vào shell: nó cài vào $HOME/.draco/bin/draco, luôn lấy bản release latest và không kiểm tra chữ ký hoặc hash. Trên server, hãy cố định phiên bản và xác minh file tải xuống.
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 SHA256SUMSLệnh này in ra draco-linux-x86-64.tar.gz: OK. Dòng FAILED cho biết các byte bạn đang có không giống với bản mà project đã phát hành. Hãy xóa file đó và bắt đầu lại.
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ệnh cuối in trang ví dụ dưới dạng markdown trong thời gian chưa đến 1 giây. find không phải là phần trang trí: layout của archive không nằm trong public contract của project, và installer chính thức cũng tìm binary theo cách tương tự.
Hãy chạy daemon bằng account riêng thay vì user dùng để đăng nhập. Ghi nội dung sau vào /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 trả về ngay khi daemon bắt đầu listening. Connection refused cho biết daemon chưa listening, vì vậy hãy đọc journalctl -u draco -n 50. Nguyên nhân thường gặp là một process khác đang giữ port 3002, vì đây cũng là port mặc định của Firecrawl; --port sẽ thay đổi port của một trong hai process. Xem thêm về unit file: systemd service unit và timer.
Bây giờ hãy fetch theo cách agent của bạn sẽ dùng:
curl -X POST http://127.0.0.1:3002/v1/scrape \
-H 'content-type: application/json' \
-d '{"url": "https://example.com", "formats": ["markdown"]}'Giữ fetch daemon không công khai trên Internet
Một fetch API không có authentication là một open proxy. Bất kỳ ai truy cập được vào cổng này đều có thể yêu cầu server của bạn truy cập bất kỳ URL nào dưới địa chỉ IP của bạn. Khi đó, báo cáo lạm dụng sẽ được gửi đến nhà cung cấp của bạn, không phải người thực hiện hành vi đó. Các flag được tài liệu hóa của Draco trong serve không bao gồm API key, nên biện pháp bảo vệ phải nằm ở network. Giữ nguyên bind mặc định 127.0.0.1 khi agent chạy trên cùng máy. Khi agent chạy ở nơi khác, hãy đặt cả hai đầu trong một private tunnel. WireGuard VPN do bạn tự host là lựa chọn thường dùng. Bind vào địa chỉ của tunnel thay vì 0.0.0.0. Sau đó, dùng một máy khác để kiểm tra rằng public IP không phản hồi gì. Kiến thức cơ bản về ufw firewall và tài khoản user theo nguyên tắc đặc quyền tối thiểu bao quát hai phần của việc này.
Mã agent của bạn có thay đổi không? Khả năng tương thích API trong thực tế
Draco trả lời các route Firecrawl v1: /v1/scrape, /v1/map, /v1/crawl, /v1/batch/scrape và /v1/search, đồng thời README nêu rõ rằng các field không xác định sẽ được chấp nhận và bỏ qua. Agent đã gửi request đến /v1/scrape chỉ cần base URL mới, không cần thay đổi gì khác. Hãy kiểm tra những gì đã thay đổi ở phía bên kia: trang self-hosting của Firecrawl hiện kiểm tra bằng /v2/crawl, còn các SDK hiện tại sử dụng v2. Vì vậy, client v2 trỏ đến Draco sẽ yêu cầu một route mà Draco không cung cấp. Hãy kiểm tra từng request bằng curl trước khi sửa code của agent, đồng thời đọc JSON body thay vì chỉ xem status code, vì tên field là điểm khác nhau giữa các implementation này.
Robots.txt, giới hạn tốc độ và ranh giới cần tuân thủ
Draco đọc robots.txt theo mặc định, còn --ignore-robots sẽ tắt tính năng đó. Tài liệu Firecrawl cũng ghi nhận cùng mặc định này. Hãy giữ nguyên cả hai. Sau đó tự đặt tốc độ: --delay thêm số mili giây giữa các request, còn --max-concurrency giới hạn số job chạy song song, với giá trị mặc định của daemon là 8. Đặt từ 2 đến 4 sẽ ít gây tải hơn trên đường truyền của VPS dùng chung và hiếm khi chậm hơn về tổng thể, vì một website bắt đầu rate limit bạn sẽ làm mất nhiều phút hơn số thời gian tiết kiệm được nhờ tăng concurrency. Hãy cache nội dung đã fetch để lần chạy thứ hai của agent không tạo thêm tải cho nguồn. Đây cũng là khoản rẻ nhất trong kiểm soát chi phí của AI agent.
Challenge wall là một chủ đề riêng, và Trawl được xây dựng chính xác cho việc này: Cloudflare Turnstile, reCAPTCHA, hCaptcha và GeeTest. Challenge wall là cách một website nói rõ rằng nó từ chối network traffic tự động. Việc vượt qua nó có thể vi phạm điều khoản sử dụng của website và ở một số nơi còn vi phạm pháp luật, nên hướng dẫn này chỉ đề cập đến hạ tầng fetch rồi dừng tại đó. Những kỹ thuật vượt qua challenge wall cũng là những kỹ thuật mà chủ website theo dõi và chặn, khiến mọi pipeline dựa trên chúng vừa mong manh vừa thiếu tôn trọng. Khi một nguồn quan trọng đến mức đó, hãy tìm RSS feed, public API hoặc bulk export của nguồn. Mỗi lựa chọn đều tốn ít tài nguyên hơn và không bị hỏng ngay trong tuần mà challenge wall thay đổi.
Kết nối với agent qua MCP
MCP (model context protocol) là interface để agent gọi một tool. Draco tích hợp MCP server trong cùng một binary và giao tiếp qua stdio:
{ "mcpServers": { "draco": { "command": "draco", "args": ["mcp"] } } }Các tool sau đó xuất hiện với agent dưới dạng draco_scrape, draco_search và bộ draco_interact_*. Stdio chỉ hoạt động khi process của agent và binary chạy trên cùng một máy, vì transport sử dụng standard input của process đó. Với agent chạy trên host khác, Hound cung cấp MCP qua HTTP: hound --http --host 127.0.0.1 --port 8765 publish một endpoint tại http://127.0.0.1:8765/mcp, và bạn truy cập endpoint đó qua tunnel. Các lựa chọn transport và nội dung cần expose được trình bày trong chạy MCP server trên VPS.
Tìm kiếm để lấy các cặp dữ liệu. Agent chỉ có khả năng fetch sẽ phải chờ bạn cung cấp URL. Thêm một instance SearXNG tự host để agent tự tìm URL, theo cùng mô hình với skill tìm kiếm trên browser được xây dựng trên SearXNG. Khi daemon đã chạy, đây là một service dùng chung cho bất kỳ agent nào trong số các AI agent tự host mà bạn chạy.
FAQ
Tôi có cần headless browser để lấy trang cho AI agent không?
Không cần cho hầu hết các trang. Tài liệu, blog và bài báo được render ở server thường trả về đầy đủ khi dùng HTTP fetch thông thường rồi chuyển HTML sang markdown. Đây là cách Draco hoạt động ở các tier thấp hơn, với thời gian khoảng 300 ms mỗi trang theo số liệu của dự án và không cần browser. Browser đáng dùng cho các ứng dụng render ở client, nơi HTML được trả về chỉ là một shell rỗng. V8 isolate của Draco xử lý được phần lớn trường hợp ở giữa mà không cần tiến trình browser. Khi không xử lý được, nó thoát với code 3, needs_browser.
Self-hosted Firecrawl cần bao nhiêu RAM trên VPS?
File compose đặt giới hạn 8 GB cho api container và 4 GB cho Playwright container. Cùng stack đó cũng khởi động Redis, RabbitMQ, PostgreSQL và FoundationDB. Hãy dự trù 8 GB. Trên máy 2 GB, kernel out-of-memory killer sẽ dừng các container khi tải cao. Dấu hiệu đầu tiên là container được restart trong docker compose ps nhưng application log không có thông tin hữu ích. Hãy xác nhận bằng dmesg -T | tail.
Draco có phải là bản thay thế drop-in cho Firecrawl API không?
Đối với các endpoint v1, mức tương thích khá cao. Nó cung cấp /v1/scrape, /v1/map, /v1/crawl, /v1/batch/scrape và /v1/search. Nó bỏ qua các field trong request mà nó không nhận biết. Vì vậy, client được viết cho Firecrawl v1 thường chỉ cần đổi base URL. Draco không phải hosted product. Phía sau nó không có managed proxy pool. Các route v2 mới hơn của Firecrawl cũng không nằm trong API surface này. Trước tiên, hãy xác minh từng call mà agent thực hiện bằng curl.
Self-hosting scraper có nghĩa là tôi có thể bỏ qua robots.txt không?
Không. Code chạy ở đâu không thay đổi nội dung site đã công bố hoặc những gì điều khoản của site cho phép. Draco và Firecrawl đều tôn trọng robots.txt theo mặc định. Override flag dành cho các site mà bạn sở hữu hoặc có văn bản cho phép crawl. Dù vậy, rate limit vẫn được áp dụng ở phía đích. Vì thế, một --delay có concurrency thấp sẽ giúp IP address của bạn tiếp tục hoạt động. Một stack chỉ hoạt động khi vượt qua challenge wall sẽ ngừng hoạt động mà không báo trước.