---
title: "Maintenance"
description: "Host-scheduled flush, DLQ retry, retention prune, and dead-letter operations."
---

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

# Maintenance

Logbun 1.0 does not run Bun cron or recurring DLQ scan timers. The host must call maintenance.

```ts
await audit.flush();
await audit.retryDlqNow();
await audit.runMaintenance();
```

| Method | Flush | Orphan recover | One DLQ scan | Retention prune |
|--------|:-----:|:--------------:|:------------:|:---------------:|
| `flush()` | yes | no | no | no |
| `retryDlqNow()` | no | yes | yes | no |
| `runMaintenance()` | yes | yes | yes | if `retention` is set |

## `runMaintenance()`

Single-flight. A second caller during a pass queues one extra pass after (no overlap). One invocation is **not** drain-until-idle. Schedule a follow-up if journal recovery is still truncated, DLQ entries remain, or prune throws `prune_incomplete`.

The promise **rejects** on flush, DLQ scan, or prune failure. When more than one phase fails, the rejection is `AggregateError` with message `multiple maintenance phases failed`. Listen for those throws.

After every pass the logger calls `reliability.requestMaintenance()` when present (Cloudflare alarm re-arm). The deprecated `rearmMaintenance()` hook is the compatibility fallback.

In a Durable Object:

```ts
async alarm() {
  await this.audit.runMaintenance();
}
```

Let rejection propagate.

## Retention

```ts
retention: { days: 90 }
```

`days` must be a finite integer `>= 1`, validated in the constructor. Pruning runs only from `runMaintenance()`.

- SQLite / Turso: batched `DELETE … WHERE created_at < cutoff` (1_000 rows × up to 10_000 batches). Throws `prune_incomplete` if a full batch remains after the cap (Turso also throws if `rowsAffected` is missing).
- ClickHouse: `ALTER TABLE … DROP PARTITION` for YYYYMM partitions **strictly older** than the cutoff month (UTC). The cutoff month is left to create-time TTL. Does not throw `prune_incomplete`.

In `database_per_tenant`, prune visits live pool tenants plus `tenancy.knownTenantIds()` only — not the base adapter.

## Dead letters

```ts
const dead = await audit.listDlq({ includeDead: true });
await audit.requeueDead(dead[0]!.id); // preserves id, resets attempts
await audit.deleteDead(dead[0]!.id);
```

Inspect and mutate only by opaque `id`. Filesystem paths in `metadata` are diagnostics, not authority.

Alert on `dlqDead`. Poisoned batches will not retry until an operator requeues or deletes them.

Source: https://logbun-docs.abshahin.workers.dev/guides/maintenance/index.mdx
