---
title: "Queues and background events"
---

> Documentation Index
> Fetch the complete documentation index at: https://ermahgerd-docs.t-bd7662a5eeb4.workers.ermahgerd.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Queues and background events

ermahgerd provides organization-scoped durable queues with the familiar
Cloudflare Workers producer and consumer interfaces. Queues are managed through
Wrangler and the console; Worker code sends with `env.QUEUE.send()` and consumes
with an exported `queue(batch, env, ctx)` handler.

## Create queues

Configure your connection as described in [Connect Wrangler](/build/connect-wrangler/),
then create the primary and dead-letter queues:

```sh
cd examples
npx wrangler queues create documents
npx wrangler queues create documents-dlq
```

Queue names are scoped to the authenticated organization. Two organizations may
both own a queue named `documents`; their registries, broker state, messages,
consumers, and DLQs remain distinct.

Queues do not have a public hostname. Their data plane is deliberately exposed
through scoped Worker bindings, while their management plane uses the authenticated
`/accounts/<organization>/queues` API.

## Produce messages

Declare a producer binding in Wrangler configuration:

```json
{
  "queues": {
    "producers": [{"binding": "DOCUMENTS", "queue": "documents"}]
  }
}
```

Then send from request or scheduled code:

```js
await env.DOCUMENTS.send({id, document});
```

The trusted runtime adapter captures the calling organization and queue name.
Worker code cannot replace that scope or obtain the queue service credential.
For diagnosis and manual workflows, a member with queue write access can also use
**Send message** on the queue detail page to enqueue a validated JSON payload with an
optional delivery delay. This uses the same durable broker path as a Worker binding.

## Consume messages

Attach a Worker consumer through the same Wrangler project:

```json
{
  "queues": {
    "consumers": [{
      "queue": "documents",
      "max_batch_size": 10,
      "max_batch_timeout": 2,
      "max_retries": 3,
      "retry_delay": 1,
      "dead_letter_queue": "documents-dlq"
    }]
  }
}
```

The Worker exports the native queue handler:

```js
export default {
  async queue(batch, env) {
    for (const message of batch.messages) {
      await env.DOCUMENTS.put(`documents/${message.id}.json`,
        JSON.stringify(message.body));
      message.ack();
    }
  }
};
```

Wrangler creates or updates the consumer after uploading the Worker. Each delivery
resolves the Worker's current active version, so messages already waiting or retrying
can be handled by a newly deployed revision without rewriting queue state.

## Delivery, retries, and durability

The dedicated `ermahgerd-queue.service` hosts all queue registry and broker Durable
Objects. Messages are committed to Durable Object SQLite before `send()` resolves.
Alarms recover delivery after process restarts. Delivery is at least once: consumers
must make side effects idempotent.

Native `ack()`, `retry()`, `ackAll()`, and `retryAll()` results control settlement.
An unsuccessful handler retries the batch. When retry attempts are exhausted, the
broker moves the message to the configured queue in the same organization or drops it
when no DLQ is configured. Queue state persists on its selected service host;
it is not replicated or automatically failed over.

The console's **Queues** section treats delivery health as a first-class tenant
surface. The inventory calls out blocked delivery, large backlogs, and old messages.
Each queue has live backlog and message-activity charts, oldest-message age,
cumulative delivery/retry/DLQ totals, pending message inspection, consumer settings,
and durable delivery events. The detail view refreshes every ten seconds and can be
paused while investigating. It highlights five-minute and one-hour age thresholds,
backlogs of 100 and 1,000 messages, sudden enqueue growth, recent dead letters, and
messages waiting without a consumer.

![Organization queue inventory and delivery-state summary](/media/screenshots/queue-overview.png)

A DLQ is an ordinary queue and can be inspected there. Deletion is rejected while a
queue contains messages; purging is a separate confirmed operation. Deleting an empty
queue also removes its historical broker state, so recreating the name starts clean.

For a consumer Worker's invocation outcomes and logs, see
[Worker observability](/build/observability/). Operator delivery objectives
are described in [Platform observability](/operations/observability/#queue-delivery-service-level).

## Compose a background workflow

The [queue-to-storage example](https://github.com/nullbytelabs/ermahgerd/blob/main/examples/queues-r2/) accepts a JSON document
over HTTP, enqueues it, and writes it to an R2 bucket from a background consumer.
The [order pipeline](/self-hosted/examples/#order-desk) adds database updates, and the
[photo workflow](/self-hosted/examples/#photo-publishing-workflow) combines queued image
processing and optional AI metadata.

Source: https://ermahgerd-docs.t-bd7662a5eeb4.workers.ermahgerd.io/build/queues/index.mdx
