---
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

Point Wrangler at the local API as described in [Deploy Workers](/deploy-workers/),
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 is local to `data-02` and is removed by
`make destroy`.

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.

The queue service exports a continuous first-attempt scheduling SLI, backlog
and oldest-message gauges, scheduling and handler histograms, and delivery
outcome counters. The platform objective and its deliberately narrow ownership
boundary are documented in [Observability](/observability/#queue-delivery-service-level).
Operators can use the provisioned service-level dashboard for error-budget burn,
per-queue attainment, and saturation context; it remains an advanced operator
surface rather than the tenant-facing queue interface.

## End-to-end proof

The example under `examples/queues-r2/` accepts a JSON document over HTTP, enqueues
it, and writes it to an R2 bucket from a background consumer. Run:

```sh
make queue-check
```

The check deploys a deliberately failing consumer, observes a native retry, restarts
the queue service to prove durable recovery, deploys a working consumer revision,
verifies the existing message reaches R2, and finally verifies exhausted delivery in
the dead-letter queue.

For continuity under sustained traffic and Worker updates, run
`make load-services-update`. It measures R2 round trips, durable enqueue latency,
consumer completion lag, and reconciles every accepted job while Wrangler deploys
three revisions. See the [load-test guide](/observability/#verify-and-load-test)
for thresholds, retained evidence, and test-resource names.

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