Firecrawl is an open-source API that crawls websites and turns them into LLM-ready data (markdown, HTML, structured JSON). This chart packages the self-hosted topology — API, workers, Playwright renderer and the bundled Redis / RabbitMQ / NuQ-Postgres backends — using the bjw-s common library, with official ghcr.io images.
helm repo add obeone https://charts.obeone.cloud
helm repo update
helm install firecrawl obeone/firecrawl
Firecrawl is a web scraping and crawling API that turns websites into LLM-ready data: clean markdown, structured extraction, screenshots, search. This chart deploys the complete self-hosted stack: the API and its workers, the Playwright service used for browser rendering, plus the Redis, RabbitMQ and nuq-postgres backing services.
ghcr.io/firecrawl/firecrawl, plus the bundled Playwright, Redis, RabbitMQ and nuq-postgres images (see values.yaml)charts/firecrawlkubeVersion constraint (see Chart.yaml)This chart is built on the bjw-s-labs common library.
Most configuration keys (controllers, service, ingress, persistence,
…) follow its schema; see the common library documentation
for everything it supports beyond what is spelled out in values.yaml.
Defaults are meant to work out of the box on any cluster. The full list of
options lives in values.yaml, is validated by
values.schema.json at install time, and is documented
in the Values section below. Override it with your own values file:
helm install firecrawl obeone/firecrawl -f my-values.yaml
helm repo update
helm upgrade firecrawl obeone/firecrawl
Each release lists its changes in the Artifact Hub changelog; give it a look before jumping across several chart versions.
helm uninstall firecrawl
PersistentVolumeClaims created by the chart are kept around: delete them manually if you also want the data gone.
Kubernetes: >=1.25.0-0
| Repository | Name | Version |
|---|---|---|
| https://bjw-s-labs.github.io/helm-charts | common | 4.6.2 |
| Key | Type | Default | Description | |
|---|---|---|---|---|
| configMaps | object | {"config":{"data":{"ENV":"production","EXTRACT_WORKER_PORT":"3004","FIRECRAWL_APP_SCHEME":"http","HOST":"0.0.0.0","IS_KUBERNETES":"true","LOGGING_LEVEL":"INFO","MODEL_EMBEDDING_NAME":"","MODEL_NAME":"","NUM_WORKERS_PER_QUEUE":"8","NUQ_PREFETCH_WORKER_PORT":"3011","NUQ_WORKER_COUNT":"1","NUQ_WORKER_PORT":"3006","OLLAMA_BASE_URL":"","OPENAI_BASE_URL":"","PORT":"3002","PROXY_SERVER":"","PROXY_USERNAME":"","SEARXNG_CATEGORIES":"","SEARXNG_ENDPOINT":"","SEARXNG_ENGINES":"","SELF_HOSTED_WEBHOOK_URL":"","USE_DB_AUTHENTICATION":"false","WORKER_PORT":"3005"},"enabled":true},"playwright-config":{"data":{"ALLOW_LOCAL_WEBHOOKS":"","BLOCK_MEDIA":"true","MAX_CONCURRENT_PAGES":"10","PORT":"3000","PROXY_SERVER":"","PROXY_USERNAME":""},"enabled":true}} |
————————————————————————— Shared by the API and worker containers. Connection URLs are NOT here — they need template rendering and live in env (see the anchors at the top). | |
| configMaps.config.data.ENV | string | "production" |
Runtime environment (production | development). |
| configMaps.config.data.FIRECRAWL_APP_SCHEME | string | "http" |
URL scheme the app advertises for its own public URLs (http | https). Set to https when serving the API over TLS. |
| configMaps.config.data.LOGGING_LEVEL | string | "INFO" |
Log verbosity (e.g. INFO, DEBUG, WARN, ERROR). | |
| configMaps.config.data.MODEL_EMBEDDING_NAME | string | "" |
Embedding model name. | |
| configMaps.config.data.MODEL_NAME | string | "" |
LLM model name used for JSON mode / extract. | |
| configMaps.config.data.NUM_WORKERS_PER_QUEUE | string | "8" |
Concurrent jobs each worker pulls per queue. Raise for per-pod throughput, or add replicas to scale across pods. | |
| configMaps.config.data.NUQ_WORKER_COUNT | string | "1" |
NuQ worker loops per pod (intra-pod concurrency). Total concurrency = controllers.nuq-worker.replicas x this value. | |
| configMaps.config.data.OLLAMA_BASE_URL | string | "" |
Local Ollama base URL — must include /api, e.g. http://ollama:11434/api. For Ollama set this + MODEL_NAME + MODEL_EMBEDDING_NAME and leave OPENAI_* empty. | |
| configMaps.config.data.OPENAI_BASE_URL | string | "" |
OpenAI-compatible base URL (leave empty for OpenAI itself; set OPENAI_API_KEY in the secret). | |
| configMaps.config.data.PROXY_SERVER | string | "" |
Proxy server URL or IP:PORT. | |
| configMaps.config.data.PROXY_USERNAME | string | "" |
Proxy username (leave blank if unauthenticated). | |
| configMaps.config.data.SEARXNG_CATEGORIES | string | "" |
Optional comma-separated categories. | |
| configMaps.config.data.SEARXNG_ENDPOINT | string | "" |
URL of an external SearXNG metasearch instance (required for /search). | |
| configMaps.config.data.SEARXNG_ENGINES | string | "" |
Optional comma-separated engines, e.g. google,bing. | |
| configMaps.config.data.SELF_HOSTED_WEBHOOK_URL | string | "" |
Receives crawl/scrape completion callbacks; signed by SELF_HOSTED_WEBHOOK_HMAC_SECRET (in the secret) when set. | |
| configMaps.config.data.USE_DB_AUTHENTICATION | string | "false" |
DB-backed auth needs Supabase, which self-hosted cannot configure; leave false (this deliberately differs from upstream .env.example, which ships true). With false the API performs NO request authentication — protect it at the network/ingress layer. | |
| configMaps.playwright-config.data.ALLOW_LOCAL_WEBHOOKS | string | "" |
Let the renderer POST to RFC1918/localhost targets. Off by default for SSRF safety; use with care. | |
| configMaps.playwright-config.data.BLOCK_MEDIA | string | "true" |
Block images/video/fonts in the headless browser (faster scrapes). | |
| configMaps.playwright-config.data.MAX_CONCURRENT_PAGES | string | "10" |
Max concurrent browser pages. | |
| configMaps.playwright-config.data.PROXY_SERVER | string | "" |
Headless-browser proxy server URL or IP:PORT. | |
| configMaps.playwright-config.data.PROXY_USERNAME | string | "" |
Headless-browser proxy username (leave blank if unauthenticated). | |
| controllers | object | {"api":{"containers":{"api":{"args":["--max-old-space-size=2048","dist/src/index.js"],"command":["node"],"env":{"<<":[{"NUQ_DATABASE_URL":"","NUQ_DATABASE_URL_LISTEN":"","NUQ_RABBITMQ_URL":"","PLAYWRIGHT_MICROSERVICE_URL":"","REDIS_RATE_LIMIT_URL":"","REDIS_URL":""},{"NUQ_POD_NAME":{"valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}}],"FLY_PROCESS_GROUP":"app"},"envFrom":[{"configMap":"config"},{"secret":"secrets"}],"image":{"pullPolicy":"IfNotPresent","repository":"ghcr.io/firecrawl/firecrawl","tag":""},"probes":{"liveness":{"custom":true,"enabled":true,"spec":{"failureThreshold":3,"httpGet":{"path":"/v0/health/liveness","port":3002},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5}},"readiness":{"custom":true,"enabled":true,"spec":{"failureThreshold":3,"httpGet":{"path":"/v0/health/readiness","port":3002},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5}}},"resources":{"limits":{"cpu":"2","memory":"2560Mi"},"requests":{"cpu":"250m","memory":"512Mi"}}}},"enabled":true,"strategy":"RollingUpdate"},"extract-worker":{"containers":{"extract-worker":{"args":["--max-old-space-size=1536","dist/src/services/extract-worker.js"],"command":["node"],"env":{"<<":[{"NUQ_DATABASE_URL":"","NUQ_DATABASE_URL_LISTEN":"","NUQ_RABBITMQ_URL":"","PLAYWRIGHT_MICROSERVICE_URL":"","REDIS_RATE_LIMIT_URL":"","REDIS_URL":""},{"NUQ_POD_NAME":{"valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}}],"FLY_PROCESS_GROUP":"extract-worker"},"envFrom":[{"configMap":"config"},{"secret":"secrets"}],"image":{"pullPolicy":"IfNotPresent","repository":"ghcr.io/firecrawl/firecrawl","tag":""},"probes":{"liveness":{"custom":true,"enabled":true,"spec":{"failureThreshold":3,"httpGet":{"path":"/liveness","port":3004},"initialDelaySeconds":10,"periodSeconds":10,"timeoutSeconds":5}},"readiness":{"custom":true,"enabled":true,"spec":{"failureThreshold":3,"httpGet":{"path":"/health","port":3004},"initialDelaySeconds":10,"periodSeconds":10,"timeoutSeconds":5}}},"resources":{"limits":{"cpu":"1","memory":"2Gi"},"requests":{"cpu":"250m","memory":"512Mi"}}}},"enabled":true,"strategy":"RollingUpdate"},"nuq-postgres":{"containers":{"postgres":{"env":{"PGDATA":"/var/lib/postgresql/data/pgdata","POSTGRES_DB":"","POSTGRES_PASSWORD":"","POSTGRES_USER":""},"image":{"digest":"sha256:f9388bd25ae2e1f1d034518236f993ce236173c1d8800ce24092ea6643a95a33","pullPolicy":"IfNotPresent","repository":"ghcr.io/firecrawl/nuq-postgres","tag":"latest"},"probes":{"liveness":{"custom":true,"enabled":true,"spec":{"failureThreshold":6,"initialDelaySeconds":20,"periodSeconds":10,"tcpSocket":{"port":5432},"timeoutSeconds":5}},"readiness":{"custom":true,"enabled":true,"spec":{"failureThreshold":6,"initialDelaySeconds":10,"periodSeconds":5,"tcpSocket":{"port":5432},"timeoutSeconds":3}}},"resources":{"limits":{"cpu":"500m","memory":"1Gi"},"requests":{"cpu":"100m","memory":"256Mi"}}}},"enabled":true,"strategy":"Recreate"},"nuq-prefetch-worker":{"containers":{"nuq-prefetch-worker":{"args":["--max-old-space-size=1024","dist/src/services/worker/nuq-prefetch-worker.js"],"command":["node"],"env":{"<<":[{"NUQ_DATABASE_URL":"","NUQ_DATABASE_URL_LISTEN":"","NUQ_RABBITMQ_URL":"","PLAYWRIGHT_MICROSERVICE_URL":"","REDIS_RATE_LIMIT_URL":"","REDIS_URL":""},{"NUQ_POD_NAME":{"valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}}],"FLY_PROCESS_GROUP":"nuq-prefetch-worker","NUQ_PREFETCH_REPLICAS":"1"},"envFrom":[{"configMap":"config"},{"secret":"secrets"}],"image":{"pullPolicy":"IfNotPresent","repository":"ghcr.io/firecrawl/firecrawl","tag":""},"probes":{"liveness":{"custom":true,"enabled":true,"spec":{"failureThreshold":3,"httpGet":{"path":"/health","port":3011},"initialDelaySeconds":10,"periodSeconds":10,"timeoutSeconds":5}},"readiness":{"custom":true,"enabled":true,"spec":{"failureThreshold":3,"httpGet":{"path":"/health","port":3011},"initialDelaySeconds":10,"periodSeconds":10,"timeoutSeconds":5}}},"resources":{"limits":{"cpu":"500m","memory":"1536Mi"},"requests":{"cpu":"100m","memory":"256Mi"}}}},"enabled":true,"strategy":"RollingUpdate"},"nuq-worker":{"containers":{"nuq-worker":{"args":["--max-old-space-size=1536","dist/src/services/worker/nuq-worker.js"],"command":["node"],"env":{"<<":[{"NUQ_DATABASE_URL":"","NUQ_DATABASE_URL_LISTEN":"","NUQ_RABBITMQ_URL":"","PLAYWRIGHT_MICROSERVICE_URL":"","REDIS_RATE_LIMIT_URL":"","REDIS_URL":""},{"NUQ_POD_NAME":{"valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}}],"FLY_PROCESS_GROUP":"nuq-worker"},"envFrom":[{"configMap":"config"},{"secret":"secrets"}],"image":{"pullPolicy":"IfNotPresent","repository":"ghcr.io/firecrawl/firecrawl","tag":""},"probes":{"liveness":{"custom":true,"enabled":true,"spec":{"failureThreshold":3,"httpGet":{"path":"/health","port":3006},"initialDelaySeconds":5,"periodSeconds":5,"timeoutSeconds":5}},"readiness":{"custom":true,"enabled":true,"spec":{"failureThreshold":3,"httpGet":{"path":"/health","port":3006},"initialDelaySeconds":5,"periodSeconds":5,"timeoutSeconds":5}}},"resources":{"limits":{"cpu":"1","memory":"2Gi"},"requests":{"cpu":"250m","memory":"512Mi"}}}},"enabled":true,"replicas":1,"strategy":"RollingUpdate"},"playwright":{"containers":{"playwright":{"envFrom":[{"configMap":"playwright-config"},{"secret":"secrets"}],"image":{"digest":"sha256:1f6eba6403207d501a63d9b1d4685b6316b4fc7968fe64a5839ab1bfec9d1c14","pullPolicy":"IfNotPresent","repository":"ghcr.io/firecrawl/playwright-service","tag":"latest"},"probes":{"liveness":{"custom":true,"enabled":true,"spec":{"failureThreshold":3,"httpGet":{"path":"/health","port":3000},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5}},"readiness":{"custom":true,"enabled":true,"spec":{"failureThreshold":3,"httpGet":{"path":"/health","port":3000},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5}}},"resources":{"limits":{"cpu":"2","memory":"2Gi"},"requests":{"cpu":"250m","memory":"512Mi"}}}},"enabled":true,"strategy":"RollingUpdate"},"rabbitmq":{"containers":{"rabbitmq":{"env":{"RABBITMQ_DEFAULT_PASS":"","RABBITMQ_DEFAULT_USER":""},"image":{"pullPolicy":"IfNotPresent","repository":"docker.io/library/rabbitmq","tag":"3.13-management-alpine"},"probes":{"liveness":{"custom":true,"enabled":true,"spec":{"failureThreshold":6,"initialDelaySeconds":20,"periodSeconds":10,"tcpSocket":{"port":5672},"timeoutSeconds":5}},"readiness":{"custom":true,"enabled":true,"spec":{"failureThreshold":6,"initialDelaySeconds":10,"periodSeconds":5,"tcpSocket":{"port":5672},"timeoutSeconds":3}}},"resources":{"limits":{"cpu":"500m","memory":"512Mi"},"requests":{"cpu":"100m","memory":"256Mi"}}}},"enabled":true},"redis":{"containers":{"redis":{"args":["--bind","0.0.0.0"],"command":["redis-server"],"image":{"pullPolicy":"IfNotPresent","repository":"docker.io/library/redis","tag":"7.4-alpine"},"probes":{"liveness":{"custom":true,"enabled":true,"spec":{"failureThreshold":3,"initialDelaySeconds":15,"periodSeconds":20,"tcpSocket":{"port":6379},"timeoutSeconds":5}},"readiness":{"custom":true,"enabled":true,"spec":{"failureThreshold":3,"initialDelaySeconds":10,"periodSeconds":10,"tcpSocket":{"port":6379},"timeoutSeconds":3}}},"resources":{"limits":{"cpu":"250m","memory":"256Mi"},"requests":{"cpu":"50m","memory":"64Mi"}}}},"enabled":true},"worker":{"containers":{"worker":{"args":["--max-old-space-size=1536","dist/src/services/queue-worker.js"],"command":["node"],"env":{"<<":[{"NUQ_DATABASE_URL":"","NUQ_DATABASE_URL_LISTEN":"","NUQ_RABBITMQ_URL":"","PLAYWRIGHT_MICROSERVICE_URL":"","REDIS_RATE_LIMIT_URL":"","REDIS_URL":""},{"NUQ_POD_NAME":{"valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}}],"FLY_PROCESS_GROUP":"worker"},"envFrom":[{"configMap":"config"},{"secret":"secrets"}],"image":{"pullPolicy":"IfNotPresent","repository":"ghcr.io/firecrawl/firecrawl","tag":""},"probes":{"liveness":{"custom":true,"enabled":true,"spec":{"failureThreshold":3,"httpGet":{"path":"/liveness","port":3005},"initialDelaySeconds":10,"periodSeconds":10,"timeoutSeconds":5}},"readiness":{"enabled":false}},"resources":{"limits":{"cpu":"1","memory":"2Gi"},"requests":{"cpu":"250m","memory":"512Mi"}}}},"enabled":true,"strategy":"RollingUpdate"}} |
————————————————————————— Each controller’s probe port/path is specific to its own HTTP health server and is NOT interchangeable. Every numeric probe port must match the matching *_PORT in configMaps.config below — change one without the other and the pod crash-loops on failing health checks. | |
| controllers.api.containers.api.args | list | ["--max-old-space-size=2048","dist/src/index.js"] |
V8 old-space heap ceiling (MiB). Keep it safely below the memory limit (2048 heap vs 2560Mi limit); the gap absorbs non-heap usage. For heavy crawls raise this AND limits.memory together — raising only one risks OOMKill or premature GC. | |
| controllers.extract-worker.containers.extract-worker.args | list | ["--max-old-space-size=1536","dist/src/services/extract-worker.js"] |
V8 old-space heap ceiling (MiB); keep below this container’s limit. | |
| controllers.nuq-postgres.containers.postgres.env.PGDATA | string | "/var/lib/postgresql/data/pgdata" |
Postgres data dir. MUST be a subdirectory of the mounted volume, not the mount root: a real PVC (any ext4/xfs CSI volume, e.g. Longhorn) ships a lost+found, and initdb refuses a non-empty data dir → CrashLoopBackOff. Pointing PGDATA at a subdir sidesteps it. |
|
| controllers.nuq-postgres.containers.postgres.image.tag | string | "latest" |
Upstream only publishes a floating latest; pinned by digest. The digest takes precedence over the tag, so bump BOTH together. Current digests: ghcr.io/firecrawl/nuq-postgres. |
|
| controllers.nuq-prefetch-worker.containers.nuq-prefetch-worker.args | list | ["--max-old-space-size=1024","dist/src/services/worker/nuq-prefetch-worker.js"] |
V8 old-space heap ceiling (MiB); keep below this container’s limit. | |
| controllers.nuq-prefetch-worker.containers.nuq-prefetch-worker.env.NUQ_PREFETCH_REPLICAS | string | "1" |
Total number of prefetch-worker pods; MUST equal this controller’s replicas (unset above, so it defaults to 1). Prefetch workers shard the queue by replica index, so a mismatch drops or double-processes jobs. To scale, raise BOTH together. |
|
| controllers.nuq-worker.containers.nuq-worker.args | list | ["--max-old-space-size=1536","dist/src/services/worker/nuq-worker.js"] |
V8 old-space heap ceiling (MiB); keep below this container’s limit. | |
| controllers.nuq-worker.replicas | int | 1 |
Number of nuq-worker pods. Total NuQ concurrency = replicas x configMaps.config.NUQ_WORKER_COUNT (per-pod loops): scale pods for throughput, raise NUQ_WORKER_COUNT for per-pod concurrency. Keep NUQ_WORKER_COUNT sensible relative to the pods you run. | |
| controllers.playwright.containers.playwright.image.tag | string | "latest" |
Upstream only publishes a floating latest; pinned by digest for reproducibility. The digest takes precedence over the tag, so bump BOTH together (changing only the tag has no effect). Current digests: ghcr.io/firecrawl/playwright-service. |
|
| controllers.redis.containers.redis.args | list | ["--bind","0.0.0.0"] |
Binds 0.0.0.0 with no password; reachable by any in-cluster client (ClusterIP only). Rely on NetworkPolicy / namespace isolation, or use an external authenticated Redis via firecrawl.redis.url. | |
| controllers.worker.containers.worker.args | list | ["--max-old-space-size=1536","dist/src/services/queue-worker.js"] |
V8 old-space heap ceiling (MiB); keep below this container’s limit. | |
| defaultPodOptions | object | {"automountServiceAccountToken":false} |
————————————————————————— | |
| firecrawl | object | {"database":{"url":""},"playwright":{"url":""},"rabbitmq":{"url":""},"redis":{"url":""}} |
————————————————————————— | |
| firecrawl.database.url | string | "" |
External NuQ Postgres URL (postgresql://user:pass@host:5432/db). MUST be the upstream nuq-postgres image (custom schema). Same two-step swap. Empty = use the bundled DB. | |
| firecrawl.playwright.url | string | "" |
External Playwright URL. MUST include the /scrape path, e.g. http://playwright.example.com:3000/scrape. Same two-step swap. Empty = use the bundled Playwright. |
|
| firecrawl.rabbitmq.url | string | "" |
External RabbitMQ AMQP URL (amqp://user:pass@host:5672). Same two-step swap. Empty = use the bundled broker. | |
| firecrawl.redis.url | string | "" |
External Redis URL (e.g. redis://user:pass@host:6379). To use it, set this AND controllers.redis.enabled: false + service.redis.enabled: false. Disabling the bundled Redis without setting this breaks every worker (the fallback URL points at the now-missing Service). Empty = use the bundled Redis. |
|
| ingress | object | {"api":{"enabled":false,"hosts":[{"host":"firecrawl.example.com","paths":[{"path":"/","pathType":"Prefix","service":{"identifier":"api","port":"http"}}]}]}} |
————————————————————————— | |
| ingress.api.enabled | bool | false |
Expose the API via Ingress. The API is unauthenticated — protect it before exposing (see USE_DB_AUTHENTICATION and BULL_AUTH_KEY). When serving over TLS, also set configMaps.config FIRECRAWL_APP_SCHEME=https. | |
| ingress.api.hosts | list | [{"host":"firecrawl.example.com","paths":[{"path":"/","pathType":"Prefix","service":{"identifier":"api","port":"http"}}]}] |
Ingress hostnames for the API. | |
| nuqPostgres | object | {"auth":{"database":"postgres","password":"postgres","username":"postgres"}} |
————————————————————————— These feed BOTH the Postgres container env (POSTGRES_USER/PASSWORD/DB) AND the NUQ_DATABASE_URL connection string — keep them consistent. Avoid URL-special characters (@ : / %) in the password: it is embedded unescaped into the postgresql:// URL. The DB is never exposed outside the cluster; for production prefer an external managed Postgres via firecrawl.database.url. |
|
| nuqPostgres.auth.database | string | "postgres" |
Database name (also feeds NUQ_DATABASE_URL). | |
| nuqPostgres.auth.password | string | "postgres" |
Set a strong password before deploying (plaintext in the manifest, as above). | |
| nuqPostgres.auth.username | string | "postgres" |
Postgres superuser name (also feeds NUQ_DATABASE_URL). | |
| persistence | object | {"nuq-postgres-data":{"accessMode":"ReadWriteOnce","advancedMounts":{"nuq-postgres":{"postgres":[{"path":"/var/lib/postgresql/data"}]}},"enabled":true,"size":"10Gi","type":"persistentVolumeClaim"}} |
————————————————————————— Only the NuQ Postgres data volume is stateful. Losing the bundled Redis / RabbitMQ pods drops in-flight queue state and rate-limit counters (acceptable for transient jobs — resubmit if needed). The size below sizes the NuQ queue DB, not scraped output (which is returned to the client, never stored). | |
| persistence.nuq-postgres-data.advancedMounts | object | {"nuq-postgres":{"postgres":[{"path":"/var/lib/postgresql/data"}]}} |
StorageClass for the PVC (omit / empty = cluster default). storageClass: “” advancedMounts targets a controller/container by name: the nuq-postgres key MUST match controllers.nuq-postgres and postgres MUST match its container. Rename either and the volume silently mounts nowhere — Postgres writes to ephemeral storage and loses all data on restart, no error. |
|
| persistence.nuq-postgres-data.size | string | "10Gi" |
PVC size for the NuQ Postgres data volume. | |
| rabbitmq | object | {"auth":{"password":"firecrawl","username":"firecrawl"}} |
————————————————————————— Interpolated into NUQ_RABBITMQ_URL, so these end up visible in plaintext in the rendered manifest (helm get manifest / kubectl get pod -o yaml). For real secrecy, use an external broker via firecrawl.rabbitmq.url. |
|
| rabbitmq.auth.password | string | "firecrawl" |
Set a strong password before deploying to anything reachable. | |
| rabbitmq.auth.username | string | "firecrawl" |
RabbitMQ username; must NOT be guest (RabbitMQ rejects guest over non-loopback connections), hence a named user. |
|
| route | object | {"api":{"enabled":false,"hostnames":["firecrawl.example.com"],"kind":"HTTPRoute","parentRefs":[{"name":"gateway","namespace":"gateway-system","sectionName":"http"}],"rules":[{"backendRefs":[{"identifier":"api","port":"http"}],"matches":[{"path":{"type":"PathPrefix","value":"/"}}]}]}} |
————————————————————————— | |
| route.api.enabled | bool | false |
Expose the API via an HTTPRoute (Gateway API). See the ingress security note above before enabling. | |
| secrets | object | {"secrets":{"enabled":true,"stringData":{"BULL_AUTH_KEY":"CHANGEME","LLAMAPARSE_API_KEY":"","OPENAI_API_KEY":"","PROXY_PASSWORD":"","SELF_HOSTED_WEBHOOK_HMAC_SECRET":"","SLACK_WEBHOOK_URL":"","TEST_API_KEY":""}}} |
————————————————————————— To manage these outside the chart, set enabled: false and create a Secret named <release-fullname>-secrets (the secret: secrets reference is fullname-prefixed by the common library) with the same keys. That Secret is also mounted into the Playwright container (for PROXY_PASSWORD), not only the Firecrawl workers. |
|
| secrets.secrets.stringData.BULL_AUTH_KEY | string | "CHANGEME" |
Protects the admin queue dashboard at /admin/ |
|
| secrets.secrets.stringData.LLAMAPARSE_API_KEY | string | "" |
LlamaParse key for PDF parsing. | |
| secrets.secrets.stringData.OPENAI_API_KEY | string | "" |
OpenAI (or compatible) API key to enable AI features. | |
| secrets.secrets.stringData.PROXY_PASSWORD | string | "" |
Password for the outbound proxy (used by both the HTTP-fetch and Playwright proxies, if PROXY_SERVER is set). | |
| secrets.secrets.stringData.SELF_HOSTED_WEBHOOK_HMAC_SECRET | string | "" |
HMAC secret signing self-hosted webhook payloads (optional). | |
| secrets.secrets.stringData.SLACK_WEBHOOK_URL | string | "" |
Slack webhook for server health notifications. | |
| secrets.secrets.stringData.TEST_API_KEY | string | "" |
API key used by Firecrawl’s own integration/e2e tests against this instance. Not needed for normal operation; leave empty. | |
| service | object | {"api":{"controller":"api","enabled":true,"ports":{"http":{"port":3002}}},"nuq-postgres":{"controller":"nuq-postgres","enabled":true,"ports":{"postgres":{"port":5432}}},"playwright":{"controller":"playwright","enabled":true,"ports":{"http":{"port":3000}}},"rabbitmq":{"controller":"rabbitmq","enabled":true,"ports":{"amqp":{"port":5672},"management":{"port":15672}}},"redis":{"controller":"redis","enabled":true,"ports":{"redis":{"port":6379}}}} |
————————————————————————— Only api (and optionally playwright) is meant to be consumed; redis, rabbitmq and nuq-postgres are internal cluster wiring. Workers expose health ports probed directly by the kubelet, so they need no Service of their own. The RabbitMQ management UI on 15672 is for debugging via port-forward. |
|
| x-firecrawl-connection-env | object | {"NUQ_DATABASE_URL":"","NUQ_DATABASE_URL_LISTEN":"","NUQ_RABBITMQ_URL":"","PLAYWRIGHT_MICROSERVICE_URL":"","REDIS_RATE_LIMIT_URL":"","REDIS_URL":""} |
————————————————————————— The three x-firecrawl-* keys below are anchor SOURCES, not configurable values: Helm ignores top-level x-* keys, so they never render on their own. They are merged into every Firecrawl app container’s env via env: { <<: [*firecrawl-connection-env, *firecrawl-pod-env], ... }. Editing an anchor here changes ALL FIVE Firecrawl containers at once (api, worker, extract-worker, nuq-worker, nuq-prefetch-worker). The Playwright service is configured separately and does NOT receive these. To repoint a backend, set firecrawl.<backend>.url below — do not edit these templates. The values are rendered through the common library’s tpl pass, so the printf fallbacks resolve to the in-cluster Service DNS names ( |
|
| x-firecrawl-env-from[0].configMap | string | "config" |
||
| x-firecrawl-env-from[1].secret | string | "secrets" |
||
| x-firecrawl-pod-env.NUQ_POD_NAME.valueFrom.fieldRef.fieldPath | string | "metadata.name" |
Charts in this repository are signed with GPG and every release ships a
provenance file. The public key is available at
charts.obeone.cloud/public_key.gpg,
fingerprint B9FE852F28888D27F8C9A11CD33E04CD22E335CE.
# Import the signing key into a legacy keyring (helm verifies with GnuPG v1 keyrings)
curl -fsSL https://charts.obeone.cloud/public_key.gpg | gpg --import
gpg --export > ~/.gnupg/pubring.gpg
# Pull the chart and check it against its provenance file
helm pull --verify obeone/firecrawl
This is a personal chart repository, maintained on a best-effort basis. Bug reports and contributions are welcome on GitHub.
Autogenerated from chart metadata using helm-docs v1.14.2