Skip to main content
Formbricks uses a shared BullMQ Job Runner for asynchronous work such as response processing, survey scheduling, and Workflows. Formbricks starts the worker inside the web application by default, so a standard self-hosted deployment does not need a separate worker container.
Every Formbricks web process starts one worker with a concurrency of one by default. You can tune these values with environment variables after you have measured your workload.

Runtime architecture

The web application creates jobs and consumes them from a shared Redis-backed queue:
  • Queue name: background-jobs
  • Redis key prefix: {formbricks:jobs}
  • Worker startup: Next.js server instrumentation
  • Default workers per web process: 1
  • Default concurrent jobs per worker: 1
Redis is also used to coordinate recurring jobs. The web process retries worker and recurring-job registration after 30 seconds if startup fails.

Current background jobs

Current workloads on the shared queue include: These are current examples rather than a fixed list. Future Formbricks features can use the same Job Runner for additional background work.

Configure the Job Runner

1

Connect Redis

Set REDIS_URL to a Redis instance reachable by every Formbricks web replica.
All replicas must use the same Redis instance so they share the BullMQ queue and recurring schedules.
2

Keep the in-process worker enabled

BULLMQ_WORKER_ENABLED is enabled by default outside test environments. You can omit it or set it explicitly:
Restart every Formbricks web process after changing these values.
3

Verify startup

Check the web process logs for the exact message BullMQ runtime started. Its structured fields include the queue name, Redis prefix, worker count, and worker concurrency.

Scale across replicas

Each web replica starts its own in-process workers and connects to the same queue. BullMQ coordinates job claims so one available worker ordinarily processes a queued job. Total processing capacity is approximately:
Increase concurrency gradually and monitor database, Redis, and downstream service capacity. Adding replicas also adds workers, so account for both scaling controls.

Retry behavior

BullMQ applies the following defaults to background jobs: Individual workloads can add their own recovery behavior on top of these queue-level retries.

Workflows

Workflows use the shared Job Runner to execute runs and reconcile runs that lost their queue or worker handoff. No additional worker process is required.

Configure action dependencies

Configure the services required by the workflow actions you use. For example, the Send email action uses your deployment’s SMTP transport and sender. Configure SMTP_HOST, SMTP_PORT, MAIL_FROM, MAIL_FROM_NAME, and any authentication or TLS variables required by your provider. See SMTP Configuration for the complete setup.

Retry and recovery behavior

Workflow delivery supports retries and recovery; it is not an exactly-once guarantee. Formbricks uses deterministic job IDs and guarded workflow run and step transitions to make redispatch safe, but operators should still expect jobs to be retried after transient failures.
A recurring reconciliation job runs every three minutes to recover workflow runs that lost their queue or worker handoff:
  • Queued runs older than two minutes are eligible for redispatch.
  • Queued runs older than 24 hours are marked as failed instead of being redispatched indefinitely.
  • Running runs with no executor activity for one hour are marked as failed and unfinished steps are skipped.
  • Each reconciliation sweep processes at most 250 queued and 250 running candidates. Larger backlogs drain over subsequent sweeps.
When reconciliation changes runs, Formbricks writes Workflow run reconciliation acted with structured counts for redispatched, aged-out, and recovered runs.

Troubleshoot workflow jobs

If workflow runs stay queued after the Job Runner is healthy:
  1. Search for BullMQ job failed entries where jobName is workflow-run.process or workflow-run.reconcile.
  2. Search for Workflow run reconciliation acted and inspect the queued and stuck-running counts.
  3. Open the workflow’s Runs view to inspect the run status and step-level error.

Use a custom external worker

The default in-process worker is the supported setup. Formbricks does not ship a standalone worker command or container. The external-worker flags are only for custom deployments that provide their own consumer and register the complete Formbricks application job handlers. In such a custom deployment, configure the web/API processes to keep enqueueing without starting local workers:
BULLMQ_EXTERNAL_WORKER_ENABLED=1 only keeps queueing enabled. It does not provide, start, or deploy a worker. Only use this configuration after your custom consumer registers the complete Formbricks job handlers and is already consuming the same queue. Otherwise, background jobs will remain queued.

Troubleshoot the Job Runner

Formbricks writes structured logs to stdout and stderr. Search the msg field for these exact messages: If background jobs stay queued:
  1. Confirm REDIS_URL is present and identical on all web replicas.
  2. Confirm at least one in-process worker is enabled, or that a compatible external worker is running.
  3. Look for BullMQ runtime started after the latest deployment or restart.
  4. Check Redis connection and BullMQ job failed logs.
  5. Use the jobName field to identify the affected workload and continue in its feature-specific logs or UI.
See Environment Variables for every Job Runner setting and Monitoring for log collection guidance.