View .md

Self-hosting

The whole platform (API, crawl workers, dashboard and these docs) is in one repository and runs on your own servers with Docker Compose. This page covers the essentials; the repository guides have the full detail:

  • RUNNING.md: step-by-step local setup, with Docker or from source.
  • README.md: features, configuration and deployment notes.
  • OPERATIONS.md: production deploys, rollback, backups, monitoring and alerts.
  • SCALING.md: capacity settings and running several API and worker nodes.

What runs

ServiceRole
serverThe API. Listens on port 8080.
workerRuns crawls and batch scrapes from the queue. Includes headless Chromium. Scale it for throughput.
webThe dashboard and docs. Its nginx forwards /api/ to the API with the prefix removed.
postgres, pgbouncerUsers, organizations, keys, usage, crawl history.
redisJob queue, page cache, rate limits and shared usage counters.
minioS3-compatible storage for crawl results.

Run it with Docker Compose

bash
git clone https://github.com/sadeghianme/Crawler.git
cd Crawler
cp .env.example .env   # then set JWT_SECRET and SECRETS_KEY to long random strings
docker compose up --build

Open http://localhost:8088 and sign up. The API is also reachable directly at http://localhost:8080. Without SMTP settings, verification and password-reset emails are written to the server log (docker compose logs server). Add crawl workers with docker compose up --scale worker=4.

For production, the repository layers docker-compose.prod.yml over the base file (docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d). It adds memory limits and restart policies, removes host port bindings and expects a reverse proxy on an external web_proxy network. It is the hosted service's own configuration (for example, APP_URL is set to the hosted domain), so review it before reusing it. OPERATIONS.md describes the deploy and rollback scripts built around it.

Configuration

Settings are environment variables, documented in .env.example. The ones most deployments need:

VariablePurpose
JWT_SECRETRequired. Signs dashboard sessions; use the same value on every API node. A server started without it uses a temporary secret, so sessions end at every restart, and docker-compose.yml falls back to a development placeholder.
SECRETS_KEYEncrypts stored secrets such as organizations' model keys. Set a stable value in production; falls back to JWT_SECRET.
APP_URLThe public URL of the dashboard. Used in email links and payment redirects, as the OAuth issuer for MCP clients, and as an allowed browser origin.
DATABASE_URLPostgreSQL connection string. Without it a source build uses a local SQLite file.
REDIS_ADDRRedis for the queue, cache, rate limits and usage counters shared across nodes. In-memory when unset.
R2_ENDPOINT, R2_ACCESS_KEY, R2_SECRET_KEY, R2_BUCKET, R2_USE_SSLS3-compatible storage (MinIO, Cloudflare R2, S3) for crawl results. Results stay in the database when unset.
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SMTP_FROMEmail for verification, password reset and approval notices.
LLM_API_KEY, LLM_BASE_URL, LLM_MODELThe server's default OpenAI-compatible model, used by research, extract, include_answer and model-based formats such as json. Organizations can bring their own under AI Extraction.
SEARCH_PROVIDER, BRAVE_SEARCH_API_KEY, SERPER_API_KEYSearch engines for search and research. SEARCH_PROVIDER is a comma-separated list (brave, serper); the default is brave.
METRICS_TOKENEnables and protects the Prometheus /metrics endpoint.
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_PRICE_PRO, STRIPE_PRICE_ENTERPRISEPaid plans through Stripe checkout. Without them, owners switch plans directly.
ORG_RATE_LIMITSoff disables the per-plan requests-per-minute budgets.
ADMISSION_MAX_INFLIGHT, ADMISSION_MAX_QUEUE, ADMISSION_MAX_WAITSynchronous requests per API node: running (128), waiting (1024) and how long they may wait (5s) before SERVER_BUSY.

A super admin can also set the model, the search engines and their keys in the dashboard under Global Settings; values stored there take precedence over the environment.

Compose passes only the variables it lists

The Compose files hand the server and worker only the variables named in their x-backend-env block. docker-compose.yml does not currently list SUPER_ADMIN_EMAILS, REGISTRATION_APPROVAL or the search settings, so setting them in .env alone has no effect there. Add the ones you use to that block.

Point the SDKs at your server

Set the base URL to your server's API root, including /api when requests go through the web container (for the default Compose setup, http://localhost:8088/api). The API container itself serves the same routes without the prefix, at http://localhost:8080.

bash
export XDIVE_BASE_URL="https://your-host/api"   # read by both SDKs
curl "$XDIVE_BASE_URL/whoami" -H "X-API-Key: $XDIVE_API_KEY"

API keys are issued by your server, in its dashboard, exactly as on the hosted service. The stdio MCP server in the repository (cmd/mcp) reads its base URL from CRAWLER_API_URL, not from XDIVE_BASE_URL, and its key from CRAWLER_API_KEY.

Egress proxy

Some sites refuse all connections from data-centre addresses, which is common for government and banking sites. Set EGRESS_PROXY_URL to an HTTP (CONNECT) or SOCKS5 proxy, typically a residential proxy with country targeting in the username:

bash
EGRESS_PROXY_URL=http://USER-country-in:[email protected]:7000
EGRESS_PROXY_DOMAINS=boi.gov.in,bankofindia.bank.in   # optional: always proxy these

Configuring the proxy does not let anyone use it yet. Proxy access is off for every organization, existing and new, until a super admin turns it on under Global Settings → Organizations, where each organization also gets a monthly cap of proxied pages (200 by default, 0 to allow none, at most 100,000) and shows this month's use. The same settings are available as PATCH /admin/orgs/{id}/proxy with {"enabled": true, "monthly_pages": 500}, and every change is written to the organization's audit log. Only scrapes, crawls and batch scrapes use the proxy; search, research, extract, map and llms.txt never do.

For an enabled organization, fetches go direct first and use the proxy only when the site cannot be reached or stays blocked; domains in EGRESS_PROXY_DOMAINS always use it. Once the organization's cap is reached, its fetches go direct only until the next calendar month. The SSRF guard still applies, so the proxy cannot be used to reach private addresses. Proxied pages report fetch_route: "proxy". A site that blocks data-centre networks returns 403 with PROXY_NOT_ENABLED for an organization without access (or past its cap), and 403 with SITE_RESTRICTED when the proxy provider refuses the destination.

Access control

By default anyone can sign up and use the server immediately. Three variables change that:

  • SUPER_ADMIN_EMAILS: a comma-separated list of addresses that become platform super admins. An address is promoted only after its owner verifies it, at verification or at sign-in; until then, signing in returns 403 EMAIL_NOT_VERIFIED and emails a new link. Super admins see Global Settings in the dashboard.
  • Invite-only sign-up: super admins turn it on under Global Settings → Access requests → Sign-up policy, and keep an allowlist of addresses and @domain entries there. Allowlisted addresses are approved as soon as they sign up; everyone else is held as an access request, as with REGISTRATION_APPROVAL=required. Changes apply without a restart. REGISTRATION_ALLOWED_EMAILS adds entries from the environment and also turns invite-only on.
  • REGISTRATION_APPROVAL=required: new sign-ups are held for review. Sign-up answers 202 with "status": "pending_approval", the addresses in SUPER_ADMIN_EMAILS are emailed, and super admins approve or reject requests under Global Settings → Access requests. The user is emailed when approved; until then signing in returns 403 ACCOUNT_PENDING_APPROVAL. Rejecting an account ends its sessions and OAuth grants at once and suspends the organizations it is the only member of.

Accounts listed in SUPER_ADMIN_EMAILS are approved when their address is verified, so they are never left waiting for another administrator.