Skip to content
Get started
Get started
WorkerOptions Reference
View Markdown
guide · worker

Every knob, with its default.

The complete option surface for a Worker, what each one changes, and the defaults you get when you leave it alone.

const worker = new Worker('queue', processor, {
embedded: true,
concurrency: 5,
batchSize: 100, // Pull up to 100 jobs per request
pollTimeout: 5000, // Long-poll: wait up to 5s for jobs instead of busy polling
limiter: { max: 10, duration: 1000 }, // Max 10 jobs per second
});

The table below documents the shared TypeScript Worker options for Bun, Node.js, and Deno. Options for the other SDKs are tabulated in the SDK guide.

OptionTypeDefaultDescription
embeddedbooleanfalseUse in-process mode
concurrencynumber1Parallel job processing. A number > 0 (a fraction rounds up) or Infinity
autorunbooleantrueStart polling automatically
heartbeatIntervalnumber10000Heartbeat interval in ms (0, a negative value or NaN = disabled). Any other value must be a finite number >= 1
batchSizenumber10Jobs to pull per batch (max: 1000). A number > 0 (a fraction rounds up); larger values, Infinity included, are clamped to 1000
batch{ size, minSize?, timeout?, groupAffinity? }-Native batch processor. job.getBatch() exposes members and member.setAsFailed(error) selectively fails one job. minSize waits indefinitely without timeout; grouped batches require groupAffinity to wait.
pollTimeoutnumber0Long-poll timeout in ms (max: 30000). A negative value or NaN means 0; larger values, Infinity included, are clamped to 30000
useLocksbooleantrueEnable BullMQ-style job locks
limiter{ max, duration, groupKey? }-Without groupKey: max job starts per rolling window, acquired atomically even with concurrent or manual processing. With groupKey: per-group concurrency cap of max (jobs grouped by job.data[groupKey], duration unused)
group{ concurrency?, limit?: { max, duration } }unlimited / noneBroker-authoritative per-job-group concurrency and fixed-window rate defaults. See Job Groups
lockDurationnumber30000Job lock TTL in ms. With useLocks, a finite number from 1 to Number.MAX_SAFE_INTEGER (the broker’s lease bound)
maxStalledCountnumber1Accepted for BullMQ compatibility, but not applied by the Worker. Configure the broker’s per-queue maxStalls policy with Queue.setStallConfig() or the HTTP API instead
skipStalledCheckbooleanfalseIn embedded or TCP mode, skip only this Worker’s subscription to stalled notifications. It does not disable broker-side stall detection or recovery
skipLockRenewalbooleanfalseSuppress the per-job heartbeat timer entirely (no JobHeartbeat sent), so both lock renewal and broker-side stall freshness stop; only the worker-registration heartbeat keeps running
drainDelaynumber50Delay between polls when the queue is empty (ms). Without a long-poll, a finite number >= 1
removeOnCompleteboolean | number | KeepJobsfalseAuto-remove completed jobs. Only true is honored; number / { age?, count? } are accepted for BullMQ type compatibility but ignored — the job’s own removeOnComplete option still applies
removeOnFailboolean | number | KeepJobsfalseSame behavior as removeOnComplete: only true is honored, other values are ignored and the job-level removeOnFail option still applies
connectionConnectionOptions-TCP connection (host, port, token, poolSize)
prefixKeystring-Namespace prefix; must match the producing Queue’s. See Namespace Isolation

Connection pool sizing (TCP): when poolSize is not set, it defaults to min(concurrency, 8). Override it by setting poolSize explicitly.

Embedded storage optionTypeDefaultDescription
dataPathstringUnsetSQLite path for the process-wide embedded manager. Use the same path as the producing Queue. A conflicting path throws; TCP storage is configured on the server.

If dataPath is omitted, the Worker uses the existing embedded manager or the configured data-path environment variables. With neither a path nor a configured manager, embedded storage is memory-only. See Persistence.

The constructor checks every duration before it starts a timer or contacts the broker. Leaving an option out (or passing null) keeps its default. A numeric string such as process.env.POLL_TIMEOUT ('1000') is read as that number; any other value that is not a number throws a TypeError. A value the worker could not use, such as NaN where a timer would run, Infinity (except for pollTimeout) or a value below the minimum, throws a RangeError that names the option:

RangeError: Worker: drainDelay must be a finite number of milliseconds >= 1 (got 0)

Long values are honoured exactly. For example, a heartbeatInterval of 30 days sends its first heartbeat after 30 days, not after 1 ms.

OptionAccepts
heartbeatInterval0 (disabled; a negative value or NaN also disables) or a finite number of at least 1. A positive value below 1 would send a heartbeat about every millisecond
pollTimeout0 or more, Infinity included; values above 30000 become 30000, and a negative value or NaN means 0
drainDelaya finite number of at least 1, checked only when pollTimeout is 0 (the only mode that uses it). 0 would re-poll an empty queue without pause
lockDurationa finite number from 1 to Number.MAX_SAFE_INTEGER, checked only with useLocks (the default). A smaller value gives a lock that expires when it is granted; a larger one is refused by the broker

concurrency and batchSize are counts. Each accepts a number greater than 0; a fraction rounds up (concurrency: 2.5 runs up to 3 jobs at once), and Infinity is accepted (concurrency: Infinity removes the limit; batchSize above 1000 becomes 1000). 0, a negative value and NaN throw a RangeError, because a worker with them never processed a job. Under a native batch, whose size is the pull size, batchSize is not read. Assigning worker.concurrency later follows the same rule, except that a value below 1 becomes 1.

worker.delay(ms, abortController?) resolves at once for 0 or a negative ms and waits for any longer finite ms; NaN and Infinity reject its promise. worker.rateLimit(ms) does nothing unless ms is a positive finite number (0, a negative value, NaN, Infinity and null are no-ops). worker.extendJobLocks(ids, tokens, duration) needs a finite duration >= 1; an omitted or null duration renews each lock with its own TTL. A numeric string is read as its number in all three.

Some values only the broker can check: an invalid queue name, an option an older broker rejects, or a missing or wrong auth token. When the broker refuses a pull for such a reason, the worker retries with a backoff from 100 ms up to 30 s and reports the refusal: as an error event with context: 'pull' when the worker has an error listener, otherwise as one console line naming the queue and the reason (at most once a minute). It never ends the process, and the worker resumes as soon as the cause is fixed. A temporary refusal, such as the broker’s rate limit or a lock timeout under heavy broker load, counts as an empty queue, as in earlier releases: the worker polls again after drainDelay (10 ms with pollTimeout), with no growing backoff, and reports nothing. An error listener that throws is logged, never rethrown. worker.getNextJob() resolves undefined on a refusal and reports a permanent one the same way.

A job’s own timeout is enforced by the broker. The worker aborts the processor’s signal only when the broker enforces the timeout too. A timeout of 0 means no timeout, so the job runs to completion. Producers accept any finite timeout of 0 or more (one above about 136,900 years is clamped), as 2.9.10 did. A job that an older release or a direct broker call stored with another value follows the same rule: NaN and Infinity mean no timeout, and a long timeout is honoured exactly.

batch.size must be an integer from 1 through 1000. minSize defaults to 1, must be no larger than size, and waits indefinitely when timeout is omitted or zero. With a global Worker limiter, minSize must also be no larger than limiter.max; batch.size may be larger and is processed in bounded chunks. A positive timeout allows the available partial batch to start after that many milliseconds. groupAffinity: true makes each processor batch homogeneous by group ID; without affinity, batches containing grouped jobs do not wait for minSize.

With native batching, concurrency counts processor invocations rather than individual batch members. The leading job exposes every member through getBatch(), and setAsFailed(error) marks one member for its own failure and retry transition. A Worker limiter counts every member and reserves the whole batch atomically only when it is ready; waiting for minSize consumes no rate slots. Cancelling or timing out any member aborts the one shared processor signal. See Worker Concurrency and Batch Pulling.

GuideWhat it covers
WorkerCreate a worker and process your first job
Worker Concurrency and Batch PullingRun jobs in parallel and pull them in batches
The Job Object Inside a Worker ProcessorEverything the processor receives and can do
Worker Eventscompleted, failed, stalled and the rest
Worker Error Handling, Retries and BackoffRetries, backoff, timeouts and giving up
Worker LifecyclePause, resume and shut down without losing work
Heartbeats, Stall Detection and Lock OwnershipHeartbeats, stall recovery and lock ownership
SandboxedWorkerExperimental isolation for CPU-heavy handlers