feat: add BullMQ background job provider with Bull Board dashboard (#2657)

Add a new BullMQ/Redis-backed job provider as an alternative to the
existing Inngest and Local providers. Includes Bull Board UI for job
monitoring at /api/jobs/board (admin-only in production, open in dev).
This commit is contained in:
Lucas Smith
2026-04-01 13:07:47 +11:00
committed by GitHub
parent 025a27d385
commit ad559f72dd
18 changed files with 1576 additions and 321 deletions
@@ -0,0 +1,187 @@
---
title: Background Jobs
description: Configure how Documenso processes background tasks like email delivery, document processing, and webhook dispatch.
---
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
## Overview
Documenso processes background jobs for email delivery, document sealing, webhook dispatch, and scheduled maintenance tasks. Three providers are available:
| Provider | Backend | Best For | Infrastructure |
| -------- | ---------- | ----------------------------------------------- | -------------- |
| Inngest | Managed | Production with zero ops overhead | None |
| BullMQ | Redis | Self-hosted production with full control | Redis |
| Local | PostgreSQL | Development and small self-hosted deployments | None |
Select a provider with the `NEXT_PRIVATE_JOBS_PROVIDER` environment variable:
```bash
NEXT_PRIVATE_JOBS_PROVIDER=inngest # or bullmq, local
```
<Callout type="info">
The default provider is `local`. It requires no additional infrastructure and works well for development and small deployments, but is not recommended for production workloads.
</Callout>
---
## Inngest (Recommended)
[Inngest](https://www.inngest.com/) is a managed background job service. It handles scheduling, retries, concurrency, and observability without any infrastructure to manage. This is the recommended provider for production deployments.
### Setup
{/* prettier-ignore */}
1. Create an account at [inngest.com](https://www.inngest.com/)
2. Create an app and obtain your event key and signing key
3. Configure the environment variables:
```bash
NEXT_PRIVATE_JOBS_PROVIDER=inngest
NEXT_PRIVATE_INNGEST_EVENT_KEY=your-event-key
INNGEST_SIGNING_KEY=your-signing-key
```
### Environment Variables
| Variable | Description | Required |
| -------------------------------- | -------------------------------------------- | -------- |
| `NEXT_PRIVATE_INNGEST_EVENT_KEY` | Inngest event key | Yes |
| `INNGEST_EVENT_KEY` | Alternative Inngest event key | No |
| `INNGEST_SIGNING_KEY` | Inngest signing key for webhook verification | Yes |
| `NEXT_PRIVATE_INNGEST_APP_ID` | Custom Inngest app ID | No |
### Advantages
- No infrastructure to manage
- Built-in monitoring dashboard
- Automatic retries with backoff
- Cron scheduling handled externally
- Scales automatically
---
## BullMQ
[BullMQ](https://docs.bullmq.io/) is a Redis-backed job queue that runs inside the Documenso process. It provides higher throughput than the local provider, configurable concurrency, and a built-in dashboard for monitoring jobs.
### Requirements
- **Redis 6.2+** - any Redis-compatible service works (Redis, KeyDB, Dragonfly, AWS ElastiCache, Upstash, etc.)
### Setup
```bash
NEXT_PRIVATE_JOBS_PROVIDER=bullmq
NEXT_PRIVATE_REDIS_URL=redis://localhost:6379
```
### Environment Variables
| Variable | Description | Default |
| ---------------------------------- | -------------------------------------------------------------------------- | ----------- |
| `NEXT_PRIVATE_REDIS_URL` | Redis connection URL | _(required)_ |
| `NEXT_PRIVATE_REDIS_PREFIX` | Key prefix for Redis queues (useful when sharing an instance) | `documenso` |
| `NEXT_PRIVATE_BULLMQ_CONCURRENCY` | Number of concurrent jobs to process | `10` |
### Dashboard
BullMQ includes a job monitoring dashboard at `/api/jobs/board`. In production, only admin users can access the dashboard. In development, it is open to all users.
The dashboard provides visibility into queued, active, completed, and failed jobs.
### Docker Compose with Redis
If you're using Docker Compose, add a Redis service:
```yaml
services:
redis:
image: redis:8-alpine
ports:
- '6379:6379'
volumes:
- redis_data:/data
volumes:
redis_data:
```
Then set `NEXT_PRIVATE_REDIS_URL=redis://redis:6379` in your Documenso environment.
### Advantages
- Self-hosted with no external service dependencies beyond Redis
- Configurable concurrency
- Built-in job monitoring dashboard
- Reliable retries with exponential backoff
- Queue namespacing for shared Redis instances
---
## Local
The local provider uses your PostgreSQL database as a job queue. Jobs are stored in the `BackgroundJob` table and processed via internal HTTP requests that Documenso sends to itself.
### Setup
No configuration required. The local provider is the default when `NEXT_PRIVATE_JOBS_PROVIDER` is unset or set to `local`.
```bash
# Optional - this is the default
NEXT_PRIVATE_JOBS_PROVIDER=local
```
### Internal URL
Background jobs in the local provider work by Documenso sending HTTP requests to itself. If your reverse proxy or network setup causes issues with the app reaching its own public URL, set the internal URL:
```bash
NEXT_PRIVATE_INTERNAL_WEBAPP_URL=http://localhost:3000
```
This tells the job system to use the internal address instead of `NEXT_PUBLIC_WEBAPP_URL` for self-requests.
<Callout type="warn">
The local provider is suitable for development and small deployments. For production workloads, use Inngest or BullMQ.
</Callout>
### Limitations
- No concurrency control - jobs are processed one at a time per request cycle
- No built-in monitoring
- Depends on the application being able to reach itself over HTTP
- Not suitable for high-throughput workloads
---
## Choosing a Provider
<Tabs items={['Managed hosting', 'Self-hosted production', 'Development']}>
<Tab value="Managed hosting">
Use **Inngest**. Zero infrastructure, automatic scaling, and built-in observability. The simplest path to reliable background jobs in production.
</Tab>
<Tab value="Self-hosted production">
Use **BullMQ**. Add a Redis instance to your infrastructure and get reliable job processing with a monitoring dashboard. Good fit if you already run Redis or want to keep everything self-hosted.
</Tab>
<Tab value="Development">
Use **Local** (the default). No additional setup required. Works out of the box with just PostgreSQL.
</Tab>
</Tabs>
---
## See Also
- [Environment Variables](/docs/self-hosting/configuration/environment) - Complete configuration reference
- [Requirements](/docs/self-hosting/getting-started/requirements) - Infrastructure requirements
- [Docker Compose](/docs/self-hosting/deployment/docker-compose) - Deploy with Docker Compose
@@ -268,20 +268,40 @@ AI features must also be enabled in organisation/team settings after configurati
## Background Jobs
Documenso uses a PostgreSQL-based job queue by default. Jobs (email delivery, document processing, webhook dispatch) are stored in the `BackgroundJob` table and processed via internal HTTP requests. No external queue service like Redis is required.
Documenso supports multiple background job providers for processing emails, documents, webhooks, and scheduled tasks.
| Variable | Description | Default |
| ---------------------------- | ------------------------------------------------------------------------------ | ------- |
| `NEXT_PRIVATE_JOBS_PROVIDER` | Jobs provider: `local` (PostgreSQL-based queue) or `inngest` (managed service) | `local` |
### Provider Selection
### Inngest Configuration
| Variable | Description | Default |
| ---------------------------- | -------------------------------------------------------------------------------------- | ------- |
| `NEXT_PRIVATE_JOBS_PROVIDER` | Jobs provider: `local` (PostgreSQL), `bullmq` (Redis), or `inngest` (managed service) | `local` |
| Variable | Description |
| -------------------------------- | -------------------------------------------- |
| `NEXT_PRIVATE_INNGEST_EVENT_KEY` | Inngest event key |
| `INNGEST_EVENT_KEY` | Alternative Inngest event key |
| `INNGEST_SIGNING_KEY` | Inngest signing key for webhook verification |
| `NEXT_PRIVATE_INNGEST_APP_ID` | Custom Inngest app ID |
### Local (local)
No additional configuration required. Jobs are stored in PostgreSQL and processed via internal HTTP requests.
| Variable | Description | Default |
| ---------------------------------- | ------------------------------------------------------------ | -------------------------------- |
| `NEXT_PRIVATE_INTERNAL_WEBAPP_URL` | Internal URL for the app to send job requests to itself | Same as `NEXT_PUBLIC_WEBAPP_URL` |
### BullMQ (bullmq)
| Variable | Required | Description | Default |
| ---------------------------------- | -------- | ------------------------------------------------------------- | ----------- |
| `NEXT_PRIVATE_REDIS_URL` | Yes | Redis connection URL (e.g., `redis://localhost:6379`) | |
| `NEXT_PRIVATE_REDIS_PREFIX` | No | Key prefix for Redis queues (useful when sharing an instance) | `documenso` |
| `NEXT_PRIVATE_BULLMQ_CONCURRENCY` | No | Number of concurrent jobs to process | `10` |
### Inngest (inngest)
| Variable | Required | Description |
| -------------------------------- | -------- | -------------------------------------------- |
| `NEXT_PRIVATE_INNGEST_EVENT_KEY` | Yes | Inngest event key |
| `INNGEST_EVENT_KEY` | No | Alternative Inngest event key |
| `INNGEST_SIGNING_KEY` | Yes | Inngest signing key for webhook verification |
| `NEXT_PRIVATE_INNGEST_APP_ID` | No | Custom Inngest app ID |
For setup guides and provider recommendations, see [Background Jobs](/docs/self-hosting/configuration/background-jobs).
---
@@ -5,6 +5,7 @@
"database",
"email",
"storage",
"background-jobs",
"signing-certificate",
"telemetry",
"advanced"
@@ -85,9 +85,13 @@ See [Storage Configuration](/docs/self-hosting/configuration/storage) for setup
### Background Jobs
Documenso processes background jobs (email delivery, document processing) using a PostgreSQL-based queue. No additional services like Redis are required: the job queue is built into the application and uses your existing database.
Documenso processes background jobs (email delivery, document processing) using a PostgreSQL-based queue by default. No additional services are required: the job queue is built into the application and uses your existing database.
For high-throughput deployments, Documenso optionally supports [Inngest](https://www.inngest.com/) as an alternative job provider. Set `NEXT_PRIVATE_JOBS_PROVIDER=inngest` and configure `INNGEST_EVENT_KEY` and `INNGEST_SIGNING_KEY`. Most self-hosted instances do not need this.
For production deployments that need higher throughput or more reliable job processing, Documenso supports [BullMQ](https://docs.bullmq.io/) as an alternative provider. BullMQ requires a **Redis** instance (v6.2+). Set `NEXT_PRIVATE_JOBS_PROVIDER=bullmq` and configure `NEXT_PRIVATE_REDIS_URL`.
For managed/cloud deployments, [Inngest](https://www.inngest.com/) is also supported as a job provider. Set `NEXT_PRIVATE_JOBS_PROVIDER=inngest` and configure `INNGEST_EVENT_KEY` and `INNGEST_SIGNING_KEY`.
See [Background Jobs Configuration](/docs/self-hosting/configuration/background-jobs) for full details.
---
@@ -144,19 +144,13 @@ See [Storage Configuration](/docs/self-hosting/configuration/storage) for full s
---
## Background Jobs Don't Need Redis
## Background Jobs
Documenso uses a PostgreSQL-based job queue by default. No Redis, no external message broker. The job system uses your existing database to store and process background tasks like email delivery and document processing.
Documenso uses a PostgreSQL-based job queue by default (`local` provider). No Redis or external message broker is required for basic deployments.
For high-throughput deployments, Documenso optionally supports [Inngest](https://www.inngest.com/) as an alternative job provider:
For production workloads, consider switching to **Inngest** (managed) or **BullMQ** (self-hosted with Redis) for better reliability and throughput.
```bash
NEXT_PRIVATE_JOBS_PROVIDER=inngest
INNGEST_EVENT_KEY=your-event-key
INNGEST_SIGNING_KEY=your-signing-key
```
Most self-hosted instances do not need Inngest.
See [Background Jobs Configuration](/docs/self-hosting/configuration/background-jobs) for setup instructions and provider comparison.
---
+1
View File
@@ -99,6 +99,7 @@ app.route('/api/ai', aiRoute);
// API servers.
app.route('/api/v1', tsRestHonoApp);
app.use('/api/jobs/*', jobsClient.getApiHandler());
app.use('/api/trpc/*', trpcRateLimitMiddleware);
app.use('/api/trpc/*', reactRouterTrpcServer);
+13
View File
@@ -1,3 +1,4 @@
import { defaultOptions as devServerDefaults } from '@hono/vite-dev-server';
import { lingui } from '@lingui/vite-plugin';
import { reactRouter } from '@react-router/dev/vite';
import autoprefixer from 'autoprefixer';
@@ -46,6 +47,18 @@ export default defineConfig({
tsconfigPaths(),
serverAdapter({
entry: 'server/router.ts',
exclude: [
// Spread the defaults but replace the /.css$/ rule so that Bull
// Board's static CSS at /api/jobs/board/static/** passes through to Hono.
...devServerDefaults.exclude.map((pattern) =>
pattern instanceof RegExp && pattern.source === '.*\\.css$'
? /^(?!\/api\/jobs\/board\/).*\.css$/
: pattern,
),
'/assets/**',
'/src/app/**',
/\?(?:inline|url|no-inline|raw|import(?:&(?:inline|url|no-inline|raw)?)?)$/,
],
}),
],
ssr: {