Deployment guide

Firecrawl

Overview

This guide covers deploying and configuring Firecrawl on Ubuntu 24.04 on Azure, using SecureAnyCloud Azure Marketplace images. Firecrawl is an open source web scraping and crawling API. Point it at a URL, and it fetches the page, renders it in a real headless browser so JavaScript-heavy sites work, and returns clean markdown or structured data. You can feed that output to a language model or a data pipeline.

Firecrawl also crawls whole sites. It follows links up to a depth and page limit that you choose, so you can build search indexes, knowledge bases and retrieval-augmented generation datasets from live web content.

The image ships the free and open source, AGPL-3.0 licensed Firecrawl, run the officially supported self-hosted way as upstream containers pinned by image digest. These are the Node/TypeScript API and worker harness, a bundled headless Chromium renderer, a Postgres job queue, a RabbitMQ broker and a Redis cache. All images are captured into the VM, so your instance starts in seconds and scrapes entirely on the box with no external cloud key.

Self-hosted Firecrawl has no built-in authentication and is open by default, so this image closes that gap. On the first boot of each VM, before the port is reachable, it generates a unique API bearer token and unique database, cache, broker and admin secrets. nginx enforces the token at the front door, so every request except the health check must carry it. Backed by 24/7 SecureAnyCloud support.

Firecrawl is a trademark of its respective owner. This image is produced by SecureAnyCloud and is not affiliated with, endorsed by, or sponsored by Firecrawl. It ships the free and open source AGPL-3.0 licensed self-hosted software, unmodified.

What you get:

  • Firecrawl v2.11.118 (the AGPL-3.0 licensed self-hosted API and worker), pinned by image digest
  • A bundled headless Chromium renderer (playwright service), pinned by digest, that renders JavaScript pages on the instance so scraping needs no external cloud key
  • A bundled Postgres job queue, RabbitMQ broker and Redis cache, pinned by digest, reachable only inside a private Docker network (never published to a host port)
  • Docker Engine with the Firecrawl API published to the loopback interface only, fronted by nginx on port 80
  • firecrawl.service, firecrawl-firstboot.service, firecrawl-postboot.service and nginx.service as systemd units, enabled and active on boot
  • A unique API bearer token, and unique Postgres, Redis, RabbitMQ and admin secrets, generated per VM on first boot, never baked into the image, with the token enforced at the nginx front door
  • A clean, empty queue database on first boot: no default token, no shipped secret, no prior data
  • Ubuntu 24.04 LTS base with latest security patches applied at build time
  • Azure Linux Agent for seamless cloud integration and SSH key injection
  • 24/7 SecureAnyCloud support with guaranteed 24 hour response SLA

Prerequisites

  • An active Azure subscription, an SSH public key, and a VNet with a subnet in the target region
  • A subscription to the Firecrawl listing on Azure Marketplace

Recommended virtual machine size: the stack runs a Node API and worker, a headless Chromium renderer, Postgres, RabbitMQ and Redis on one instance. Start with Standard_B4ms (4 vCPU, 16 GB RAM). For heavy or highly concurrent crawling, use a larger size such as Standard_D4s_v5 or Standard_D8s_v5.

In the network security group (NSG), allow inbound 22/tcp from your management network and 80/tcp for the API from the networks that call it.

1

Deploy from the Azure Portal

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Firecrawl by SecureAnyCloud, and select Create.

On the Basics tab, pick your subscription, resource group, region and size (Standard_B4ms or larger). Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22) and HTTP (80).

Select Review + create, then Create.

2

Deploy from the Azure CLI

RG="firecrawl-prod"; LOCATION="eastus"; VM_NAME="firecrawl-01" IMAGE_URN="sac1647283583153:firecrawl-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION" az network vnet create -g "$RG" --name firecrawl-vnet --address-prefix 10.100.0.0/16 --subnet-name firecrawl-subnet --subnet-prefix 10.100.1.0/24 az network nsg create -g "$RG" --name firecrawl-nsg az network nsg rule create -g "$RG" --nsg-name firecrawl-nsg --name allow-ssh --priority 100 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp az network nsg rule create -g "$RG" --nsg-name firecrawl-nsg --name allow-http --priority 110 \
  --destination-port-ranges 80 --access Allow --protocol Tcp
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:firecrawl-ubuntu-24-04:default:latest
az vm create -g "$RG" --name "$VM_NAME" --image "$IMAGE_URN" \
  --size Standard_B4ms --storage-sku StandardSSD_LRS \
  --admin-username azureuser --ssh-key-values "$SSH_KEY" \
  --vnet-name firecrawl-vnet --subnet firecrawl-subnet --nsg firecrawl-nsg --public-ip-sku Standard
3

Connect to your VM

ssh azureuser@<vm-ip>
4

Confirm the services are running

The stack runs as five containers (the Firecrawl API and worker, the headless Chromium renderer, Postgres, RabbitMQ and Redis) under one firecrawl.service, fronted by nginx. Confirm that the services are active, list the containers, and check which ports are bound.

nginx listens on :80. The Firecrawl API is published to 127.0.0.1:3002 (loopback only). Postgres, RabbitMQ and Redis have no host port at all.

sudo systemctl is-active docker firecrawl nginx
sudo docker compose --env-file /etc/firecrawl/images.env --env-file /etc/firecrawl/firecrawl.env -f /etc/firecrawl/compose.yaml ps --format 'table {{.Service}}\t{{.Status}}'
sudo ss -tlnp | grep -E ':80 |:3002 '

All three services report active, and the five containers are running. The API, Postgres, RabbitMQ and Redis report healthy. nginx is bound on :80, and the Firecrawl API is bound only on 127.0.0.1:3002.

Postgres (5432), RabbitMQ (5672) and Redis (6379) never appear on a host port, because they are reachable only inside the private Docker network.

5

Read the per-instance credentials

On the first boot, before the port was reachable, this VM generated a unique API bearer token and unique Postgres, Redis, RabbitMQ and admin secrets. It wrote them to a root-only file. Read them:

sudo cat /root/firecrawl-credentials.txt

The file (mode 0600 root:root) holds the bootstrap API_TOKEN that you authenticate the API with and FIRECRAWL_URL (the address to reach this VM on). It also holds POSTGRES_PASSWORD, REDIS_PASSWORD, RABBITMQ_PASSWORD and BULL_AUTH_KEY.

None of these values ship in the image. Every value is unique to this VM, so no two instances share a credential, and there is no default token to change.

6

Verify the API security model

Self-hosted Firecrawl has no built-in authentication, so SecureAnyCloud fronts it with nginx and enforces the per-instance bearer token. The health endpoint is public, but every other endpoint requires the token. Confirm that a request with no token or a wrong token is rejected with 401, while the per-instance token is accepted:

FC_TOKEN=$(sudo grep '^API_TOKEN=' /root/firecrawl-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'health (no auth): %{http_code}\n' http://localhost/health
curl -s -o /dev/null -w 'scrape (no token): %{http_code}\n' -X POST http://localhost/v1/scrape -H 'Content-Type: application/json' -d '{"url":"https://example.com"}'
curl -s -o /dev/null -w 'scrape (wrong token): %{http_code}\n' -X POST http://localhost/v1/scrape -H 'Authorization: Bearer wrong-token' -H 'Content-Type: application/json' -d '{"url":"https://example.com"}'
curl -s -o /dev/null -w 'scrape (with token): %{http_code}\n' -X POST http://localhost/v1/scrape -H "Authorization: Bearer $FC_TOKEN" -H 'Content-Type: application/json' -d '{"url":"https://example.com","formats":["markdown"]}'

The health check returns 200 with no authentication. The scrape requests with no token or a wrong token are rejected with 401 at the nginx front door. The same call with the per-instance token returns 200.

That is the whole security model. The health probe is open for load balancers, and the bearer token gates everything else.

7

Scrape your first page

Scraping a page returns clean markdown that is ready to feed to a language model. Send a POST /v1/scrape with the per-instance token, and print the returned markdown:

FC_TOKEN=$(sudo grep '^API_TOKEN=' /root/firecrawl-credentials.txt | cut -d= -f2-)
curl -s -m 120 -X POST http://localhost/v1/scrape \
  -H "Authorization: Bearer $FC_TOKEN" -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com","formats":["markdown"]}' \
  | python3 -c 'import sys,json;d=json.load(sys.stdin);print("success:",d["success"]);print();print(d["data"]["markdown"])'

The response reports success: True and the page rendered to markdown, for example a heading Example Domain followed by the page text.

The headless Chromium renderer fetched and rendered the page entirely on the instance. Nothing was sent to an external Firecrawl cloud service, and no cloud API key is required. Add "html" or "rawHtml" to formats, or "screenshot", to get those representations too.

8

Crawl a whole site

Crawling follows links from a starting URL and scrapes every page it finds, up to the limit you set. It returns a job ID immediately and runs in the background. Poll the job to watch it complete and to collect the markdown for each page:

FC_TOKEN=$(sudo grep '^API_TOKEN=' /root/firecrawl-credentials.txt | cut -d= -f2-)
CRAWL_ID=$(curl -s -X POST http://localhost/v1/crawl \
  -H "Authorization: Bearer $FC_TOKEN" -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com","limit":5}' \
  | python3 -c 'import sys,json;print(json.load(sys.stdin)["id"])')
echo "crawl job: $CRAWL_ID" sleep 12 curl -s http://localhost/v1/crawl/$CRAWL_ID -H "Authorization: Bearer $FC_TOKEN" \
  | python3 -c 'import sys,json;d=json.load(sys.stdin);print("status:",d.get("status"),"| completed:",d.get("completed"),"/",d.get("total"))'

The first call returns a crawl job ID, and the job runs in the background. Polling GET /v1/crawl/<id> reports the status moving to completed, with the count of pages scraped.

For a larger site, raise limit and add options such as includePaths, excludePaths or maxDepth to scope the crawl.

9

Call the API from your own machine

From anywhere that can reach the VM on port 80, use the bootstrap token and the VM public IP. Retrieve the token once over SSH, then call the API with your VM address:

curl -X POST http://<vm-ip>/v1/scrape \
  -H "Authorization: Bearer <API_TOKEN>" -H 'Content-Type: application/json' \
  -d '{"url":"https://firecrawl.dev","formats":["markdown"]}'

For production, use the official Firecrawl SDKs, which are available for Python and Node/TypeScript. Point the SDK at your instance by setting the API URL to http://<vm-ip>/ (or your own domain, see Step 11). Pass the bootstrap token as the API key.

You can then call scrape, crawl and the other endpoints from your application code, exactly as the curl calls above do. When you connect an SDK to a self-hosted instance, the API key is the per-instance token that this image generated.

10

Optional integrations

Basic scrape and crawl to markdown work out of the box with no external keys. You can enable two optional features by adding your own third-party keys to /etc/firecrawl/firecrawl.env and running sudo systemctl restart firecrawl:

  • LLM extraction (the json format and /v1/extract): set OPENAI_API_KEY (or point OLLAMA_BASE_URL at your own Ollama) so that Firecrawl extracts structured data with a model.
  • Web search (/v1/search): point SEARXNG_ENDPOINT at a SearXNG instance you run.

Both features are optional. The self-hosted image does not include Firecrawl's cloud-only anti-bot engine, which does not affect standard scraping and crawling.

11

Use your own domain and HTTPS (production)

The image serves the API over plain HTTP on port 80, which suits a load balancer or private network. For production, terminate TLS in front of Firecrawl so that the token and payloads travel encrypted:

  • Front the VM with Azure Application Gateway, a load balancer with a managed certificate, or a CDN, and forward to nginx on port 80.
  • Or install your own certificate. Add a TLS server block that references your certificate and key to /etc/nginx/sites-available/firecrawl, open 443/tcp on the NSG, then run sudo systemctl reload nginx.
  • Point a DNS name that you control at the VM public IP, and use that name (and https://) as the API URL in your Firecrawl SDK.
12

Server components

Component Version / Detail

Scraping API and worker Firecrawl v2.11.118 (Node/TypeScript, pinned by image digest)

Page renderer bundled headless Chromium (playwright service, private Docker network, no host port)

API front nginx on :80 reverse proxying to 127.0.0.1:3002, enforcing the per instance bearer token

Job queue bundled Postgres (private Docker network, no host port)

Broker bundled RabbitMQ (private Docker network, no host port)

Cache and rate limiting bundled Redis (private Docker network, no host port, password protected)

Orchestration Docker Compose under firecrawl.service

API token per instance, generated first boot into /root/firecrawl-credentials.txt, enforced at nginx

Operating system Ubuntu 24.04 LTS (patched at build)

License AGPL-3.0 (Firecrawl)

13

Managing the Firecrawl service

sudo systemctl status firecrawl --no-pager | head -12 sudo docker compose --env-file /etc/firecrawl/images.env --env-file /etc/firecrawl/firecrawl.env -f /etc/firecrawl/compose.yaml logs --tail 40 api

Restart the whole stack with sudo systemctl restart firecrawl. Follow the API logs live with sudo docker compose --env-file /etc/firecrawl/images.env --env-file /etc/firecrawl/firecrawl.env -f /etc/firecrawl/compose.yaml logs -f api. View the configuration in /etc/firecrawl/firecrawl.env and /etc/firecrawl/compose.yaml. After you change the environment file, restart the service to apply the change.

14

Security recommendations

  • Restrict the NSG. Allow TCP 80 (and 22 for admin) only from the networks that call the API.
  • Terminate TLS in front of Firecrawl (Step 11) so the bearer token and payloads are encrypted in transit.
  • Treat the token like a password. It is stored in /root/firecrawl-credentials.txt (root only) and enforced at the nginx front door. If it is ever exposed, rotate it by editing /etc/nginx/conf.d/firecrawl-auth.conf.
  • Keep the bundled database, broker and cache private. Postgres, RabbitMQ and Redis sit on a private Docker network with no host port. Keep them that way.
  • Keep the OS patched. Unattended security upgrades remain enabled on the running VM.
15

Support and Licensing

Firecrawl is distributed under the GNU Affero General Public License v3.0 (AGPL-3.0). This SecureAnyCloud image bundles the unmodified official open source Firecrawl release; SecureAnyCloud provides the packaging, the bundled renderer, queue, broker and cache, the per-instance secret generation and the nginx-enforced bearer token, the paired deploy guide, and 24/7 support with a guaranteed 24 hour response SLA. Firecrawl is an independent open source project and this image is not affiliated with or endorsed by Firecrawl.

Deploy on Azure

Find Firecrawl on Ubuntu 24.04 on the Azure Marketplace, published by SecureAnyCloud. Deploy from the Portal or the Azure CLI as shown above.

Need Help?

Email sales@secureanycloud.com for deployment help, configuration questions, or licensing enquiries.

Deploy on Azure

Launch Firecrawl on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure