The short version
What you get
Firecrawl's repository ships a Docker Compose file that runs the API, its workers, a Playwright browser service, Redis, RabbitMQ and PostgreSQL. Only port 3002 is exposed. Scrape, crawl, map and search work with no API keys. LLM features work once you connect an OpenAI-compatible provider or Ollama.
What you give up
The self-hosted build has no Fire-engine, Firecrawl's anti-bot layer, so screenshots and page actions don't work. Agent, Browser and the enhanced proxies are cloud-only. The default stack has no authentication and no persistent volumes, so it is not ready for the public internet as shipped.
We ran every step below on 1 November 2026, on release v2.11.162, the release Firecrawl's self-hosting docs pin. The test machine was a Fedora 44 desktop with 24 CPU threads and 62 GB of RAM, running Docker 29.9 and Compose 5.6. The commands come from Firecrawl's docs, SELF_HOST.md and the docker-compose.yaml. The timings, memory figures and outputs are from our run.
What the self-hosted stack runs
Firecrawl is licensed under AGPL-3.0. You can run it unmodified for yourself. If you change the code and offer it to other people over a network, the AGPL requires you to publish your changes.
The root Compose file defines these services:
api: the Firecrawl API and its workers in one container. It is the only service published to the host, on port 3002. Compose caps it at 4 CPUs and 8 GB of memory.playwright-service: a headless browser for pages that need JavaScript. Capped at 2 CPUs and 4 GB.redis: Redis, which the API also uses for rate limiting.rabbitmq: RabbitMQ, which the job queue connects to.nuq-postgres: PostgreSQL, which holds the default job queue.foundationdbandfoundationdb-init: an experimental queue backend, used only if you setNUQ_BACKEND=fdb.
Firecrawl says the Compose limits are not verified minimums, and it publishes no minimum host size. The two caps add up to 6 CPUs and 12 GB. A third-party guide on Ubuntu gives 8 GB of RAM and 4 cores as a minimum. In our test, after a 20-page crawl and a few scrapes, the whole stack used about 3.4 GB: 2.9 GB for the API container, 210 MB for Playwright and under 200 MB each for RabbitMQ, PostgreSQL, Redis and FoundationDB. That was a light load. We suggest a 16 GB server for steady use, so both caps fit with room for the other services, but check it against your own load.
Setup in five steps
You need Git, Docker Engine or Docker Desktop, Docker Compose v2 (run as docker compose) and curl. Port 3002 must be free.
1. Clone the repository and pin the release
$ git clone https://github.com/firecrawl/firecrawl.git $ cd firecrawl $ git checkout v2.11.162
The docs say to check out an exact tag. The main branch and the floating image tags can change separately, and GitHub issues show what happens when they do. In issue #2111, the API container stopped starting after new images were pulled. In issue #2551, a fresh build of main crashed on launch with a missing database relation.
2. Create the .env file
Put this file in the repository root, next to docker-compose.yaml:
$ cat > .env <<'EOF'
USE_DB_AUTHENTICATION=false
POSTGRES_USER=postgres
POSTGRES_PASSWORD=replace-with-at-least-32-random-characters
POSTGRES_DB=postgres
EOF
- Replace the password before you start the stack, and don't commit
.env. - Keep
POSTGRES_DB=postgres. Inv2.11.162, the bundledpg_cronconfiguration targets that database. - Leave
NUQ_BACKENDandBULL_AUTH_KEYunset for the first run. - Don't copy
apps/api/.env.exampleinstead. Older guides do, but the docs say that file is for API development and is not a Compose file. The root.envonly sets variables thatdocker-compose.yamlreads.
3. Build and start the stack
$ docker compose up --build -d $ docker compose ps --all
The Compose file builds every Firecrawl image from source by default. On our machine the build took 13 minutes and produced three images: firecrawl-api (1.5 GB), firecrawl-playwright-service (2.1 GB) and firecrawl-nuq-postgres (640 MB). Our first attempt stopped after 35 seconds with connection reset by peer while it was downloading a base image. Running the same command again finished without errors, so retry if the network drops.
Compose prints about 30 warnings that optional variables such as OPENAI_API_KEY and PROXY_SERVER are not set. They are expected. docker compose ps --all should then show this:
SERVICE STATUS api Up 5 seconds foundationdb Up 10 seconds foundationdb-init Exited (0) 1 second ago nuq-postgres Up 10 seconds playwright-service Up 10 seconds rabbitmq Up 10 seconds (healthy) redis Up 10 seconds
foundationdb-init is a one-shot job, so Exited (0) is correct. In the API logs, one worker reported a RabbitMQ noproc error at startup and then logged "falling back to postgres". Every test below still passed, so that error alone doesn't mean the stack is broken.
To skip the build, comment out each build: line and uncomment the image: line under it. The prebuilt images are ghcr.io/firecrawl/firecrawl, ghcr.io/firecrawl/playwright-service:latest and ghcr.io/firecrawl/nuq-postgres:latest. Those tags float, so they may not match the v2.11.162 checkout.
4. Check that the API answers
$ curl --fail --silent --show-error --max-time 5 \ http://localhost:3002/v0/health/readiness {"status":"ok"}
Our first check, sent right after docker compose up returned, failed with Connection reset by peer because the API was still starting. A few seconds later it returned {"status":"ok"}. This is only a heartbeat. It does not check Redis, PostgreSQL, RabbitMQ, Playwright, the workers or outbound network access.
5. Run one real scrape
$ curl --fail-with-body --silent --show-error --max-time 75 \
-X POST http://localhost:3002/v2/scrape \
-H 'Content-Type: application/json' \
-d '{"url": "https://example.com", "formats": ["markdown"], "timeout": 60000}'
A working stack returns "success": true with data.markdown and data.metadata.statusCode. The timeout in the body is in milliseconds, and curl's --max-time is in seconds. Keep curl's limit longer, so the API can return its own timeout error. No API key or Authorization header is needed, because authentication is off. Our first scrape of example.com returned "success": true and status code 200 in 1.4 seconds. A second run took 0.7 seconds.
What worked in our test
With the default .env and no LLM, proxy or SearXNG settings, we sent these requests to the stack:
| Request | Result | Time |
|---|---|---|
Scrape example.com to markdown | Worked, status 200 | 0.7 to 1.4 s |
Scrape a JavaScript-rendered page (quotes.toscrape.com/js/) | Worked. The quotes, which load through JavaScript, were in the markdown | 3.9 s |
| Scrape Firecrawl's self-host docs page to markdown and links | Worked, 15,600 characters of markdown | 4.3 s |
Crawl quotes.toscrape.com, limit 20 | Worked, 20 of 20 pages | 28 s |
Map docs.firecrawl.dev | Worked, 1,486 URLs | — |
Map quotes.toscrape.com (no sitemap) | 0 URLs, with a warning to try the base domain | 2.9 s |
| Search "firecrawl self host", limit 5 | Worked through DuckDuckGo, 5 web results | 2.0 s |
| Screenshot format | No screenshot. The response warned that the engine does not support it | 1.2 s |
| JSON extraction with no LLM set | HTTP 500 with a generic error | 1.2 s |
| JSON extraction through OpenRouter | Worked, returned the first three quotes and authors | — |
Scrape, crawl and search worked out of the box, and Playwright handled the JavaScript page. Map depends on what it can discover: it found 1,486 URLs on a docs site with a sitemap and none on a small site that has no sitemap. The screenshot request returned "success": true but no screenshot, so check for a warning field rather than trusting success alone.
Adding AI features, proxies and search
The docs say to get one scrape working first and then add one thing at a time. The Compose file passes these variables through:
- For LLM features such as JSON extraction, set
OPENAI_API_KEY,OPENAI_BASE_URL,MODEL_NAMEandMODEL_EMBEDDING_NAME, orOLLAMA_BASE_URLfor a local model. Any OpenAI-compatible endpoint works throughOPENAI_BASE_URL. We setOPENAI_BASE_URL=https://openrouter.ai/api/v1, an OpenRouter key andMODEL_NAME=openai/gpt-4o-mini, restarted the API withdocker compose up -d api, and JSON extraction worked. We leftMODEL_EMBEDDING_NAMEunset for that test. The restart took 9 seconds. - For proxies, set
PROXY_SERVER,PROXY_USERNAMEandPROXY_PASSWORD, which go to both the API and the Playwright service.BLOCK_MEDIAstops Playwright loading media, which saves proxy bandwidth. - For search, set
SEARXNG_ENDPOINT, plus the optionalSEARXNG_ENGINESandSEARXNG_CATEGORIES. - Throughput is controlled by
NUM_WORKERS_PER_QUEUE(default 8),CRAWL_CONCURRENT_REQUESTS(10),MAX_CONCURRENT_JOBS(5) andBROWSER_POOL_SIZE(5).
In the v2.11.162 source, /search uses Fire-engine if FIRE_ENGINE_BETA_URL is set. Without Fire-engine it uses SearXNG if SEARXNG_ENDPOINT is set, and otherwise it falls back to DuckDuckGo. If a search errors, the function returns an empty list, so your client gets no results and no error message. To choose the search engines yourself, run your own SearXNG instance. Our SearXNG Docker Compose guide shows how to connect it to Firecrawl's api service.
What self-hosting leaves out
Firecrawl's docs list these differences between the self-hosted build and Firecrawl Cloud:
| Feature | Self-hosted | Firecrawl Cloud |
|---|---|---|
| Scrape, crawl, map, search | Included, with fetch and Playwright | Included and managed |
| LLM extraction and formats | Needs your own OpenAI-compatible provider or Ollama | Managed provider |
| Fire-engine anti-bot layer | Not included; run and configure it separately | Managed |
| Screenshots and page actions | Not available in the default stack | Available |
| Agent, Browser, Interact | Not included | By plan |
| Enhanced proxies | Bring your own proxy | Managed |
| Product, menu, audio and video formats | Need external services | Available |
| Security, backups, upgrades | Your job | Firecrawl's job |
Without Fire-engine, pages load through plain fetch or the bundled Playwright browser, so sites with strong bot protection may return errors or empty pages. Issue #2350 describes workers stuck in a loop of "Waterfalling to next engine..." messages on pages that blocked them. Your own proxies help with rate limits, but a proxy does not guarantee access to a site.
Before you expose it to the internet
The docs call the setup above a trusted-network quickstart. Five things are missing for production:
- Authentication. With
USE_DB_AUTHENTICATION=false, anyone who reaches port 3002 can use your server. The docs say switching that variable on is not enough by itself, because authentication also needs a database schema and application setup. - TLS and network rules. Put TLS on a reverse proxy or ingress, and keep the PostgreSQL, Redis, RabbitMQ and worker ports private.
- Persistent storage. The Compose file defines no volumes for PostgreSQL, Redis or RabbitMQ, so their data is lost when a container is replaced. Add volumes and test a backup and restore.
- Secrets. Move the database password from
.envinto a secret manager. - Admin routes. Setting
BULL_AUTH_KEYturns on admin routes, including a queue UI. SettingADMIN_INTERNAL_PORTstarts a second listener that needs no key, so never publish that port.
For Kubernetes, SELF_HOST.md links Kubernetes manifests and a Helm chart in the repository. Firecrawl calls them versioned starting points, not finished production setups.
Cost: a server vs Firecrawl Cloud
The self-hosted software is free. You pay for the server, any proxies, any LLM tokens, and the time spent running it. Firecrawl Cloud charges a monthly plan that includes credits. A scraped or crawled page costs 1 credit, a search costs 2 credits per 10 results, and the JSON format adds 4 credits per page.
| Plan | Monthly | Billed yearly | Credits a month | Concurrent requests |
|---|---|---|---|---|
| Free | $0 | — | 1,000 | 2 |
| Hobby | $19 | $16/mo | 5,000 | 5 |
| Standard | $99 | $83/mo | 100,000 | 25 |
| Growth | $399 | $333/mo | 500,000 | 50 |
| Scale | $749 | $599/mo | 1,000,000 | 100 |
Two examples of a 16 GB server, the size we suggest above:
- DigitalOcean Basic droplet, 8 vCPUs and 16 GiB: $96 a month. The 8 GiB, 4 vCPU droplet is $48.
- Hetzner CX43, 8 vCPUs and 16 GB, in Germany or Finland: €16.49 a month before VAT. The 8 GB CX33 is €8.99. Hetzner's page marked these plans as currently unavailable when we checked.
Where the break-even sits
Per page, the cloud costs about 0.38 cents on Hobby ($19 for 5,000), 0.1 cents on Standard ($99 for 100,000) and 0.075 cents on Scale ($749 for 1,000,000).
A $96 droplet costs about the same as the Standard plan, so on DigitalOcean, self-hosting saves money only above roughly 100,000 pages a month. A €16.49 Hetzner server costs less than the Hobby plan, so it is cheaper from about 5,000 pages a month, as long as one server handles your volume and the sites you scrape don't need Fire-engine.
Proxies, LLM tokens for extraction, and the time to patch, upgrade and debug the stack all come on top of the server price. In our view, at a few thousand pages a month the Free or Hobby plan costs less than the time it takes to maintain a server. Self-hosting pays off at high volume on easy sites, or when the data must stay on your own infrastructure.
Troubleshooting
The docs and the most-commented self-host issues on GitHub cover these problems:
- A container exits on start. Run
docker compose ps --allanddocker compose logs --tail=200. Check that you are onv2.11.162, that Docker has enough CPU, memory and disk, and that the.envvalues are consistent. - "Port 3002 did not become available". On slow machines the API took longer to start than the startup timeout (issue #2366). The default is now 60 seconds, and you can raise it with
HARNESS_STARTUP_TIMEOUT_MS, for example120000. - Redis connection errors. Inside Compose, use
redis://redis:6379.localhostpoints to the container itself, not to Redis. Remove anyREDIS_URLorREDIS_RATE_LIMIT_URLoverride. - A RabbitMQ
noprocerror at startup. We saw it once, followed by "falling back to postgres". The stack kept working. If scrapes fail as well, check that therabbitmqservice shows as healthy. - JSON extraction returns HTTP 500. The response only says "An error occurred" with an error ID. Search the API logs for that ID. In our test the log said
Failed to parse URL from /responses, which means no LLM provider was set. SetOPENAI_API_KEYandOPENAI_BASE_URL, orOLLAMA_BASE_URL. - Map returns 0 URLs. We saw this on a small site that has no sitemap, while a site with a sitemap returned 1,486 URLs. Use
/v2/crawlwith a page limit to discover links on such sites. - Readiness works but scrapes fail. Read
docker compose logs --tail=200 api playwright-service. The readiness check does not test Playwright or the workers. - Scrapes time out. Check that the server can reach
https://example.comand that the API and Playwright are running. - Memory keeps growing. Issue #722 reported memory on a 2-core, 4 GB server rising from 13% to 85% during crawls and staying there until a restart. A fix was merged later. The Ubuntu guide also hit exit code 137, which means the kernel killed a container for using too much memory.
- Errors after an upgrade. Run
docker compose downbefore you rebuild, so old containers don't run against the new images (the maintainers' advice in issue #2551).
Running Fireplexity on self-hosted Firecrawl
Fireplexity, the open-source answer engine this site covers, calls Firecrawl's /v2/search endpoint. It can use a self-hosted Firecrawl after a one-line change to the API address. The self-hosted search returns web results only, so Fireplexity's news and image panels stay empty. Our self-hosting guide covers that change and a fully local setup with Ollama. If you would rather not run Firecrawl at all, our Firecrawl alternatives page compares hosted and self-hosted replacements.
Common questions
Can you self-host Firecrawl?
Yes. Firecrawl's engine is open source under AGPL-3.0, and its repository includes a Docker Compose file that runs the API on port 3002. Scrape, crawl, map and search work in the default stack.
Is self-hosted Firecrawl free?
The software is free, but you pay for the server, any proxies and any LLM tokens. A 16 GB server costs $96 a month at DigitalOcean or €16.49 before VAT at Hetzner. Firecrawl's cloud plans start at $19 a month for 5,000 credits.
What features are missing in self-hosted Firecrawl?
The self-hosted build does not include Fire-engine, the anti-bot layer, so screenshots and page actions are not available. Agent, Browser and the enhanced proxies are cloud-only. LLM extraction needs your own OpenAI-compatible provider or Ollama.
How much RAM does self-hosted Firecrawl need?
Firecrawl publishes no minimum host size. In our test the whole stack used about 3.4 GB under a light load. The Compose file caps the API at 8 GB and the Playwright service at 4 GB, so we suggest a 16 GB server for steady use.
Does self-hosted Firecrawl need an API key?
Not in the default setup. With USE_DB_AUTHENTICATION=false, requests need no key, so anyone who can reach port 3002 can use the server. Keep it on a trusted network or add authentication and TLS first.
How does search work in self-hosted Firecrawl?
The /search endpoint uses Fire-engine if configured, then SearXNG if SEARXNG_ENDPOINT is set, and otherwise DuckDuckGo. It returns web results only.
Sources
- Self-hosting Firecrawl — pinned release,
.env, commands, feature support, before-production list and troubleshooting - Open source or Firecrawl Cloud — cloud-only features and who runs what
- SELF_HOST.md — services, defaults, admin routes, Kubernetes and Helm examples
- docker-compose.yaml at v2.11.162 — services, resource caps, image names and environment variables
- Search backend source at v2.11.162 — Fire-engine, SearXNG and DuckDuckGo order
- firecrawl/firecrawl on GitHub — AGPL-3.0 licence; issues #2111, #2551, #2350, #2366 and #722
- Firecrawl pricing — plan prices, credits, concurrency and credit costs
- DigitalOcean Droplet pricing — Basic droplet prices
- Hetzner Cloud cost-optimized plans — CX33 and CX43 prices
- Self-hosting Firecrawl on Ubuntu 25.04 — stated minimum of 8 GB and 4 cores, exit code 137
Checked on 1 November 2026. The steps come from Firecrawl's documentation and repository at release v2.11.162, and we ran them on 1 November 2026. Prices and the Compose file change often, so check the current release before you copy a command. This site is an independent community page and is not affiliated with Firecrawl, DigitalOcean or Hetzner.
