Durable Object class exports
Last updated Sep 28, 2026
The exports field in your Wrangler configuration file is the declarative way to manage Durable Object class lifecycle. You declare each Durable Object class your Worker exports — along with whether it is live, deleted, renamed, or transferred — and Cloudflare reconciles your declaration against the namespaces that have already been provisioned for your Worker.
This page covers how to:
- Create a new Durable Object class.
- Delete a Durable Object class and its data.
- Rename a Durable Object class.
- Transfer a Durable Object class between Workers.
Looking for the legacy `migrations` array?
The exports field replaces the imperative migrations array used by older Workers. Both flows are supported, but a Worker can only use one at a time. To move an existing Worker from migrations to exports, refer to Migrate from the legacy migrations flow.
How exports works
When you deploy a Worker that declares exports, Cloudflare compares three sources of truth:
- Your code — the set of Durable Object classes the Worker actually exports.
- Your
exportsconfiguration — what you have declared about each class. - Provisioned state — the Durable Object namespaces that already exist for this Worker.
A class that appears only in your code is ignored until you declare it in exports; Cloudflare does not provision a namespace implicitly. Other disagreements, such as a live entry whose class is missing from code or a provisioned namespace with no matching entry, are surfaced as structured errors or, where the intent is unambiguous, actions that Cloudflare applies for you.
A minimal exports block declares each Durable Object class as a live entry:
{
"durable_objects": {
"bindings": [
{
"name": "MY_DURABLE_OBJECT",
"class_name": "MyDurableObject",
},
],
},
"exports": {
"MyDurableObject": {
"type": "durable-object",
"storage": "sqlite",
},
},
}
[[durable_objects.bindings]]
name = "MY_DURABLE_OBJECT"
class_name = "MyDurableObject"
[exports.MyDurableObject]
type = "durable-object"
storage = "sqlite"
To declare a destructive operation — deleting, renaming, or transferring a class — you change the entry's state to a tombstone variant. The class name remains the same; the value tells Cloudflare what to do with the existing namespace.
| Operation | state |
Required fields |
|---|---|---|
| Define a new class (default) | "created" (or omitted) |
storage |
| Delete a class | "deleted" |
(none) |
| Rename a class | "renamed" |
renamed_to |
| Transfer a class to another Worker | "transferred" |
transferred_to |
| Receive a transfer from another Worker | "expecting-transfer" |
storage, transfer_from |
The rest of this page describes each operation in detail and links to the exports configuration reference for the full schema.
Define a Durable Object class
To define a new Durable Object class, add an entry to exports keyed by the class name and set storage to "sqlite".
Recommended SQLite-backed Durable Objects
Cloudflare recommends all new Durable Object namespaces use the SQLite storage backend. These Durable Objects can continue to use storage key-value API.
Additionally, SQLite-backed Durable Objects allow you to store more types of data (such as tables), and offer Point In Time Recovery API which can restore a Durable Object's embedded SQLite database contents (both SQL data and key-value data) to any point in the past 30 days.
Creating new namespaces with the key-value storage backend is no longer supported for accounts without an existing key-value-backed namespace. The key-value storage backend remains available for existing namespaces, and a migration path from the key-value storage backend to the SQLite storage backend will be available in the future.
- Add the class to your Worker code and export it:
src/index.tsts
import { DurableObject } from "cloudflare:workers";
export class MyDurableObject extends DurableObject {
// ...
}
```
2. Add a binding for the class (if your Worker needs to access it through `env`) and declare the class in `exports`:
```jsonc
{
"durable_objects": {
"bindings": [
{
"name": "MY_DURABLE_OBJECT",
"class_name": "MyDurableObject"
}
]
},
"exports": {
"MyDurableObject": {
"type": "durable-object",
"storage": "sqlite"
}
}
}
```
```toml
[[durable_objects.bindings]]
name = "MY_DURABLE_OBJECT"
class_name = "MyDurableObject"
[exports.MyDurableObject]
type = "durable-object"
storage = "sqlite"
```
3. Deploy the Worker:
```sh
npx wrangler deploy
```
Cloudflare provisions a namespace for the class the first time you deploy. On subsequent deploys with the same entry, no namespace changes are made — the entry simply confirms that the class is still live.
Note
`storage` is required for live entries. New Durable Object namespaces created through `exports` always use the SQLite storage backend. The `legacy-kv` value is only accepted for namespaces that were already provisioned with key-value storage; refer to [Storage backends](#storage-backends).
## Delete a Durable Object class
Replace the live entry with a `deleted` tombstone to retire a Durable Object class. Deleting a class removes its namespace and **all of its stored data permanently** — this is not a soft delete.
1. Remove the class from your Worker code.
2. Remove the binding for the class from `durable_objects.bindings`.
3. Change the `exports` entry for the class to a `deleted` tombstone:
```jsonc
{
"exports": {
"OldDurableObject": {
"type": "durable-object",
"state": "deleted"
}
}
}
```
```toml
[exports.OldDurableObject]
type = "durable-object"
state = "deleted"
```
4. Deploy the Worker:
```sh
npx wrangler deploy
```
Deleting a class destroys its data
There is no Trash for Durable Object namespaces deleted through `exports`. Copy any data you need to keep to another location before deploying a `deleted` tombstone.
A `deleted` tombstone has two preconditions enforced at deploy time:
- The class must not be present in your Worker code. Cloudflare will not delete a namespace whose class is still being shipped, because runtime bindings would resolve to a deleted namespace.
- No other Worker in your account may bind to the namespace. If another Worker still binds to the class, the deploy is rejected with `tombstone_delete_blocked_by_external_bindings` and the list of referencing scripts is returned. Redeploy those Workers without the binding first, then re-run your deploy.
Once the namespace has been deleted, the tombstone becomes stale. Cloudflare reports this in the [reconciliation output](#reading-the-reconciliation-output) and lists the entry in `removable_entries` so you can safely remove it from `exports`.
## Rename a Durable Object class
Renaming a Durable Object class moves stored data from one class to another within the same Worker. Renaming requires:
- A `renamed` tombstone keyed by the old class name, with `renamed_to` pointing at the new class name.
- A live entry for the new class name in the same `exports` map (so the data has somewhere to land).
For a brand-new deploy where the old class is no longer in your code, a single deploy is enough:
```jsonc
{
"exports": {
"OldName": {
"type": "durable-object",
"state": "renamed",
"renamed_to": "NewName"
},
"NewName": {
"type": "durable-object",
"storage": "sqlite"
}
}
}
[exports.OldName]
type = "durable-object"
state = "renamed"
renamed_to = "NewName"
[exports.NewName]
type = "durable-object"
storage = "sqlite"
After the rename applies, the namespace's class name is updated to NewName and runtime bindings that reference the new class name resolve to the same data.
Avoid downtime during a rename
A Durable Object class rename involves two updates that are not perfectly atomic at the runtime layer: the namespace's class pointer and the Worker code that exports the class. During a deploy rollout, one update may be visible before the other for a few seconds. To avoid runtime errors during this window, use a three-deploy rename:
- Alias the new name to the old class. Add the new class name to your code as the canonical class, and re-export it under the old name so existing instances keep resolving:
src/index.tsts
```ts import { DurableObject } from "cloudflare:workers";
export class NewName extends DurableObject { // ... } export { NewName as OldName }; ```
Leave exports unchanged. Deploy.
2. Apply the rename while the alias is still in place. Update exports to add the renamed tombstone and the new live entry:
jsonc
{
"exports": {
"OldName": {
"type": "durable-object",
"state": "renamed",
"renamed_to": "NewName"
},
"NewName": {
"type": "durable-object",
"storage": "sqlite"
}
}
}
```toml [exports.OldName] type = "durable-object" state = "renamed" renamed_to = "NewName"
[exports.NewName] type = "durable-object" storage = "sqlite" ```
Deploy. Cloudflare applies the rename and surfaces a tombstone_class_still_in_code info notice — this is expected and confirms the safe rollout pattern.
3. Remove the alias. Once the previous deploy has fully rolled out, remove the OldName alias from your code:
src/index.tsts
```ts import { DurableObject } from "cloudflare:workers";
export class NewName extends DurableObject { // ... } ```
Leave exports unchanged. Deploy. The OldName tombstone is now stale and Cloudflare lists it in removable_entries — you can delete the entry from exports in your next config edit.
The renamed_to target must:
- Be a valid JavaScript identifier and differ from the source class name.
- Appear as a live entry (
state: "created"or omitted) in the sameexportsmap. Cloudflare rejects arenamed_tovalue that names another tombstone or is missing entirely. - Not collide with an existing namespace under the same name on this Worker. If a namespace already exists under the target name, delete it via its own
deletedtombstone in a prior deploy first.
Transfer a Durable Object class between Workers
Transferring moves an existing Durable Object namespace from one Worker (the source) to another Worker (the target) in the same account. Because two Workers must coordinate, transfer is a multi-deploy flow.
The target Worker declares an expecting-transfer entry that names the source Worker. The source Worker declares a transferred tombstone that names the target Worker. The actual handoff commits when the source Worker's deploy lands.
The recommended sequence is four deploys:
- Source Worker — initial state. The source declares
MyDOas a live class. Nothing else has changed yet.
jsonc
{
"exports": {
"MyDO": { "type": "durable-object", "storage": "sqlite" }
}
}
toml
[exports.MyDO]
type = "durable-object"
storage = "sqlite"
- Target Worker — receive the transfer. The target adds
MyDOto its code and declares anexpecting-transferentry naming the source Worker. Do not add adurable_objects.bindingsentry forMyDOon the target yet — Cloudflare does not route self-referencing bindings through the source's namespace during this phase.
