Use Redis and BullMQ
Configure a shared Redis queue for producers and workers on separate processes or machines.
Prerequisites
Section titled “Prerequisites”Install BullMQ alongside Outpost to connect producers and workers to the Redis queue.
bullmqAn optional package, installed next to Outpost.- A standalone Redis serverSelf-hosted or managed, reachable by every producer and worker.
- The
noevictionpolicyRequired by the queue, which checks it at open.
Use a dedicated Redis queue for all producers and workers that share these jobs. Configure its eviction policy before connecting BullMQ, so Redis does not discard queue keys under memory pressure.
Enable Redis persistence (AOF or RDB snapshots) if jobs must survive a Redis restart.
Set the eviction policy
Section titled “Set the eviction policy”On your own server, set the policy and keep it across restarts:
On a managed service, change it in the console: on Redis Cloud, set the database Data eviction policy to No eviction; on Amazon ElastiCache, attach a custom parameter group with maxmemory-policy set to noeviction. Then check what Redis reports:
createBullMQTaskQueue() reads the same INFO line and rejects any other policy, or a server that hides it. It never changes the server configuration.
Configure the queue
Section titled “Configure the queue”Import the adapter from its own subpath. The queue implements the same contract as the SQLite queue, so a worker runs on it unchanged (Job queues and workers).
A producer opens the same queue and enqueues a job for the review handler:
API reference: BullMQTaskQueueOptions.
Connect producers and workers
Section titled “Connect producers and workers”Every producer and worker must use the same name, prefix, Redis database and server. A difference in any of them silently gives it a separate, empty queue.
Pass connection settings, not a Redis client. Use prefix rather than connection.keyPrefix, which the adapter rejects. Keep the prefix for Outpost alone: no other BullMQ consumer or cleanup job should touch its keys.
What the adapter owns
Section titled “What the adapter owns”- ConnectionsOne to open the queue, then a BullMQ queue and worker for each handler, opened on first use.
- Lease checksA timer that returns jobs with expired leases to the queue.
- Closing
close()rejects new calls, waits for calls in progress, then closes every connection.
Jobs, leases and results stay in Redis after close(), for the next process. Stop the worker before closing: abort its signal and await runQueueWorker(), as in worker.ts.
Interrupted completion
Section titled “Interrupted completion”The queue records each result in its own Redis state first, then marks the BullMQ job done. That recorded result is authoritative:
- Finalization fails
complete()still succeeds,get()returns the result, and the job never runs again. The error goes toonError. - Enqueue fails midwaySend the same request with the same
idagain; the queue accepts it and publishes it. - A worker crashesIts lease expires and another worker claims the job with the same
idempotencyKey.
Rotate Redis credentials
Section titled “Rotate Redis credentials”Connection settings are read once, when the queue opens. Rotate them by replacing processes:
A credential revoked while a worker still runs makes it lose its lease. Another worker then runs the job again with the same idempotencyKey: your effect service must deduplicate (Job queues and workers).
Limits
Section titled “Limits”- Standalone Redis only: Redis Cluster is not supported. Behavior across a Sentinel or managed primary failover is not guaranteed; test it before relying on it.
- At-least-once effects: Leases stop stale workers from writing results, not from repeating external side effects. Deduplicate with
idempotencyKey. - Durability is Redis durability: Without persistence, a Redis restart loses the queue.
API: createBullMQTaskQueue · BullMQTaskQueueOptions · runQueueWorker.