# Introduction: A Free Job Queue for Your Language

Use bunqueue from Node.js, Deno, Python, PHP, Go, Rust, Elixir or Bun. The free, MIT-licensed server runs on Bun; your application keeps its runtime.

Canonical: https://bunqueue.dev/guide/introduction/

---

import { Tabs, TabItem } from '@astrojs/starlight/components';

<div class="bq-wrap bq-hero">
  <span class="bq-eyebrow">guide · introduction</span>
  <h1 class="bq-hero-h1 bq-bench-h1">A job queue for <em>your language.</em></h1>
  <p class="bq-hero-sub">bunqueue is a free, MIT-licensed job queue for Node.js, Deno, Python, PHP, Go, Rust, Elixir and Bun. The name comes from the core engine, which runs on Bun. Your application and workers use their own runtime through an official client.</p>
</div>

A job queue lets your app hand off slow work (sending emails, processing images, calling APIs) to run in the background instead of blocking a request.

Choose the setup that matches your application:

- **Node.js, Deno, Python, PHP, Go, Rust or Elixir:** run the included bunqueue server, install your language's client, then connect it to the server. Bun is required on the server, not in your application. Follow the [server and client setup](/#quickstart).
- **Bun:** use the server in the same way, or run the queue and worker together with `embedded: true`. Follow the [embedded quickstart](/guide/quickstart/).

Both options are free and include the queue features. No account, paid plan or Redis service is required. Use memory for ephemeral jobs, SQLite for one-process persistence, or PostgreSQL for several active brokers.

## See it in 10 lines

<Tabs syncKey="lang">
<TabItem label="Bun">

```typescript
import { Queue, Worker } from 'bunqueue/client';

const storage = { embedded: true, dataPath: './data/bunq.db' } as const;
const queue = new Queue('emails', storage);

const worker = new Worker(
  'emails',
  async (job) => {
    console.log('Sending to', job.data.to);
    return { sent: true };
  },
  storage
);

await queue.add('welcome', { to: 'user@example.com' });
```

Run it with `bun run app.ts`. No server: the queue runs embedded in your process.

</TabItem>
<TabItem label="Node.js / Deno">

```typescript
import { Queue, Worker } from 'bunqueue-client';

const storage = { embedded: false } as const;
const queue = new Queue('emails', storage);

const worker = new Worker(
  'emails',
  async (job) => {
    console.log('Sending to', job.data.to);
    return { sent: true };
  },
  storage
);

await queue.add('welcome', { to: 'user@example.com' });
```





Start the server once (`bunx bunqueue start`), then run the file with
`node --experimental-strip-types app.ts` (Node 22+) or `deno run -A app.ts`.

</TabItem>
<TabItem label="Python">

```python
from bunqueue import Queue, Worker

queue = Queue("emails")
queue.add("welcome", {"to": "user@example.com"})

def process(job):
    print("Sending to", job.data["to"])
    return {"sent": True}

Worker("emails", process).run()
```

Start the server once (`bunx bunqueue start`), then `python app.py`.

</TabItem>
<TabItem label="PHP">

```php
use Bunqueue\Queue;
use Bunqueue\Worker;

$queue = new Queue('emails');
$queue->add('welcome', ['to' => 'user@example.com']);

$worker = new Worker('emails', function (Bunqueue\Job $job) {
    return ['sent' => true];
});
$worker->run();
```

Start the server once (`bunx bunqueue start`), then `php worker.php`.

</TabItem>
<TabItem label="Go">

```go
queue := bunqueue.NewQueue("emails", bunqueue.Options{})
defer queue.Close()
queue.Add("welcome", map[string]any{"to": "user@example.com"}, nil)

worker := bunqueue.NewWorker("emails", func(job *bunqueue.Job) (any, error) {
    return map[string]any{"sent": true}, nil
}, bunqueue.WorkerOptions{})
worker.Run()
```

Start the server once (`bunx bunqueue start`), then `go run .`.

</TabItem>
<TabItem label="Rust">

```rust
use bunqueue_client::{ConnectionOptions, JobOptions, Queue, Value, Worker, WorkerOptions};

let queue = Queue::new("emails", ConnectionOptions::default());
let data = Value::Map(vec![(Value::from("to"), Value::from("user@example.com"))]);
queue.add("welcome", data, JobOptions::default())?;

let worker = Worker::new("emails", |_job| Ok(Value::from(true)), WorkerOptions::default());
worker.run()?;
```

Start the server once (`bunx bunqueue start`), then `cargo run`.

</TabItem>
<TabItem label="Elixir">

```elixir
queue = Bunqueue.queue("emails")
{:ok, _job} = Bunqueue.Queue.add(queue, "welcome", %{to: "user@example.com"})

worker =
  Bunqueue.Worker.new("emails", fn _job ->
    {:ok, %{sent: true}}
  end)

Bunqueue.Worker.run(worker)
```

Start the server once (`bunx bunqueue start`), then `mix run app.exs`.

</TabItem>
</Tabs>

That is a complete, working queue. The [Quick Start](/guide/quickstart/) walks through it step by step.

## Why bunqueue?

- **Zero external infrastructure.** Add `dataPath` for one-file SQLite persistence, or omit it for memory-only queues. No Redis, no separate broker, and only one runtime dependency.
- **Multi-broker when needed.** PostgreSQL 15–18 coordinates independent standalone servers with transactional claims and fenced leases; 18.6 is recommended.
- **Native Bun.** Local persistence uses `bun:sqlite`; PostgreSQL uses Bun's built-in `SQL` client directly, with no ORM or third-party database driver.
- **Familiar API.** If you know BullMQ, you already know most of bunqueue.
- **Production features.** Retries with backoff (automatic waiting between retry attempts), a dead letter queue (a holding area for jobs that keep failing), cron scheduling, rate limiting, stall detection, and S3 backups.
- **AI-agent ready.** A built-in MCP server with 73 tools lets agents like Claude add jobs, manage crons, and monitor queues via natural language.

## Two ways to run it

| Comparison | Embedded                                 | Server                                          |
| ------------------ | ---------------------------------------- | ----------------------------------------------- |
| **What it is**     | A library inside your process            | A standalone `bunqueue` process                 |
| **Best for**       | Single-process apps, scripts, serverless | Multiple services sharing one queue             |
| **Setup**          | Pass `embedded: true`                    | Run `bunqueue start`, then connect              |
| **Persistence**    | Memory or SQLite via `dataPath`          | Memory/SQLite, or PostgreSQL via URL            |
| **Broker scaling** | One process                              | One SQLite broker or several PostgreSQL brokers |

**Embedded mode** means the queue lives inside your app, like using SQLite instead of Postgres:

```typescript
import { Queue, Worker } from 'bunqueue/client';

// Queue and Worker must use the same embedded storage configuration.
const storage = { embedded: true, dataPath: './data/bunq.db' } as const;
const queue = new Queue('tasks', storage);
const worker = new Worker(
  'tasks',
  async (job) => {
    /* ... */
  },
  storage
);
```

**Server mode** runs bunqueue as a standalone service, and any number of apps connect to it over TCP. Use SQLite for one broker:

```bash
bunqueue start --data-path ./data/queue.db
```

Or point independent servers at the same PostgreSQL namespace, with a unique broker ID for each process:

```bash
BUNQUEUE_POSTGRES_URL='postgres://bunqueue:secret@postgres:5432/bunqueue' \
BUNQUEUE_POSTGRES_NAMESPACE=production \
BUNQUEUE_BROKER_ID=broker-a \
bunqueue start
```

See [Storage backends](/guide/databases/) before choosing a production topology.

<Tabs syncKey="lang">
<TabItem label="Bun">

```typescript
// No embedded option = connects to localhost:6789
const queue = new Queue('tasks');
const worker = new Worker('tasks', async (job) => {
  /* ... */
});
```

</TabItem>
<TabItem label="Node.js / Deno">

```typescript
// No embedded option = connects to localhost:6789
const queue = new Queue('tasks');
const worker = new Worker('tasks', async (job) => {
  /* ... */
});
```

</TabItem>
<TabItem label="Python">

```python
from bunqueue import Queue, Worker

queue = Queue("tasks")  # connects to localhost:6789

def process(job):
    ...

Worker("tasks", process).run()
```

</TabItem>
<TabItem label="PHP">

```php
use Bunqueue\Queue;
use Bunqueue\Worker;

$queue = new Queue('tasks'); // connects to localhost:6789
$worker = new Worker('tasks', function (Bunqueue\Job $job) {
    // ...
});
$worker->run();
```

</TabItem>
<TabItem label="Go">

```go
queue := bunqueue.NewQueue("tasks", bunqueue.Options{}) // localhost:6789

worker := bunqueue.NewWorker("tasks", func(job *bunqueue.Job) (any, error) {
    // ...
    return nil, nil
}, bunqueue.WorkerOptions{})
worker.Run()
```

</TabItem>
<TabItem label="Rust">

```rust
use bunqueue_client::{ConnectionOptions, Queue, Value, Worker, WorkerOptions};

let queue = Queue::new("tasks", ConnectionOptions::default()); // localhost:6789
let worker = Worker::new("tasks", |_job| Ok(Value::Nil), WorkerOptions::default());
worker.run()?;
```

</TabItem>
<TabItem label="Elixir">

```elixir
queue = Bunqueue.queue("tasks")  # connects to localhost:6789

worker = Bunqueue.Worker.new("tasks", fn _job -> {:ok, %{}} end)
Bunqueue.Worker.run(worker)
```

</TabItem>
</Tabs>

Server mode also unlocks clients in other runtimes: Node.js, Deno, Python, PHP, Go, Rust, Elixir, and Cloudflare Workers via the [client SDKs](/guide/sdks/).

## Compared to BullMQ

| Feature                               | bunqueue       | BullMQ       |
| ------------------------------------- | -------------- | ------------ |
| Runtime                               | Bun            | Node.js      |
| Storage                               | Memory/SQLite; optional PostgreSQL | Redis        |
| External deps                         | None by default; PostgreSQL when selected | Redis server |
| Priorities, delays, retries, cron     | Yes            | Yes          |
| Rate limiting, stall detection, flows | Yes            | Yes          |
| Pro-style groups and processor batches | Yes            | Pro package  |
| Pro telemetry / NestJS integration     | No             | Pro package  |
| Advanced DLQ (auto-retry, filters)    | Yes            | Basic        |
| S3 backups                            | SQLite mode    | No           |
| MCP server for AI agents              | Yes (73 tools) | No           |
| Built-in workflow engine              | Yes            | No           |

Migrating? The API is intentionally close to BullMQ, see the [migration guide](/guide/migration/).

## Beyond jobs: workflows

For multi-step processes (validate an order, charge, notify, ship) bunqueue ships a workflow engine with retries, parallel steps, branching, rollback on failure, and human-in-the-loop signals:

```typescript
import { Workflow, Engine } from 'bunqueue/workflow';

const flow = new Workflow('order')
  .step('validate', async (ctx) => ({ ok: true }))
  .step('charge', async (ctx) => ({ txId: 'tx_123' }), { retry: 3 })
  .waitFor('approval')
  .step('ship', async (ctx) => ({ shipped: true }));
```

No Temporal, no extra service. See the [Workflow Engine guide](/guide/workflow/).

## Next steps

- [Installation](/guide/installation/), get bunqueue installed
- [Quick Start](/guide/quickstart/), build your first queue in a minute
- [Server Mode](/guide/server/), run bunqueue as a standalone service
- [MCP Server](/guide/mcp/), connect AI agents to your queues