---
description: Replace a Container application that uses the default scheduling policy with a Durable Object-managed application.
title: Migrate to the Durable Object scheduling policy
image: https://developers.cloudflare.com/containers/guides/migrate-to-durable-object-scheduling-policy/og.png?v=c4a9b833bc8d835b
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/containers/llms.txt  
> Use this file to discover all available pages before exploring further.

# Migrate to the Durable Object scheduling policy

Last updated Sep 30, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/containers/guides/migrate-to-durable-object-scheduling-policy/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

You cannot change the scheduling policy of an existing Container application. To move from the `default` policy to the `durable_object` policy, create a replacement Container application and cut traffic over to it.

The replacement application must use a new Durable Object class and namespace. The old and replacement applications cannot attach to the same Durable Object namespace.

Caution

This process does not transfer existing Container instances or Durable Object storage. The `default` scheduling policy does not support snapshots, so you cannot use a Container snapshot to transfer a filesystem.

If the existing Durable Objects contain data, design an application-specific transfer process before continuing.

## Before you begin

- **Replace the `Container` class.** The [`Container` class](https://developers.cloudflare.com/containers/api/container-class/) does not support the `durable_object` policy. If your existing class extends `Container`, the replacement class must use the [Durable Object Container API](https://developers.cloudflare.com/containers/api/durable-object-container/) directly. Rebuild any `Container` class helpers your application relies on, such as port readiness checks, request proxying, and sleep timeouts. Refer to [Migrate to the Durable Object Container API](https://developers.cloudflare.com/containers/guides/migrate-to-durable-object-container-api/).
- **Check how the Worker declares Durable Object classes.** A Worker uses either the [`exports` field](https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations/) or the [legacy `migrations` array](https://developers.cloudflare.com/durable-objects/reference/durable-object-class-migrations-legacy/), not both. Declare the replacement class with the same mechanism the Worker already uses. Moving from `migrations` to `exports` cannot be undone. If you want to move, do it as a separate change. Refer to [Migrate from the legacy `migrations` flow](https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations/#migrate-from-the-legacy-migrations-flow).
- **Move external images to the Cloudflare managed registry.** Named images with the `durable_object` policy must use a Dockerfile or a digest-pinned reference in the Cloudflare managed registry. If the existing `image` references Docker Hub, Amazon ECR, or Google Artifact Registry, [push the image to the Cloudflare managed registry](https://developers.cloudflare.com/containers/guides/image-management/#use-an-external-image) first.

## Configuration changes

Map the existing application configuration to its `durable_object` equivalent:

| `default` policy | `durable_object` policy |
| --- | --- |
| `image` (Dockerfile path) | `dockerfile` on a named `images` entry, selected with `ctx.container.start()` |
| `image` (registry reference) | `image` on a named `images` entry. Must be a digest-pinned reference in the Cloudflare managed registry |
| `image_build_context` | `build_context` on the named image |
| `image_vars` | `build_vars` on the named image |
| `instance_type` | The `instance` option in `ctx.container.start()`. Refer to [Instance size changes](#instance-size-changes) |
| `max_instances` | Not supported. Running instances count toward [account limits](https://developers.cloudflare.com/containers/platform/limits/#account-limits). Enforce any per-application cap in application code |
| Application-wide image rollouts | Application code that stops and starts each Container |
| `observability` | Keep in Wrangler configuration, subject to `durable_object` restrictions |
| `unsafe.configuration.experimental_flags` | Keep in Wrangler configuration |
| `ssh` and `authorized_keys` | Keep in Wrangler configuration |
| Placement constraints, rollout settings, `wrangler_ssh`, and `trusted_user_ca_keys` | Not supported on the replacement application |

For the complete field compatibility list, refer to [Wrangler configuration](https://developers.cloudflare.com/workers/wrangler/configuration/#containers).

### Instance size changes

With the `durable_object` policy, you set the instance size with the `instance` option of `ctx.container.start()` instead of `instance_type` in Wrangler configuration. `instance` accepts `lite`, `standard-1`, `standard-2`, `standard-3`, and `standard-4`. If your existing application uses one of the following `instance_type` values, choose a replacement:

- `dev`: Use `lite`.
- `standard`: Use `standard-1`.
- `basic`: Choose `lite` or `standard-1`. Custom instance types require at least 1 vCPU, so you cannot reproduce `basic` with a custom instance.

Custom instance objects use camel case at runtime. Rename `memory_mib` to `memoryMib` and `disk_mb` to `diskMb`. Refer to [Choose an instance size at runtime](https://developers.cloudflare.com/containers/configuration/scheduling-policy/#choose-an-instance-size-at-runtime).

## Move the application

1. **Add a new Durable Object class and Container application.**

   You cannot move the existing application by changing its `scheduling_policy` to `durable_object`, because the scheduling policy cannot be changed after creation. You also cannot reuse the existing Durable Object class, because each Durable Object namespace attaches to one Container application.

   Caution

   Do not change `scheduling_policy` on the existing Container entry. `wrangler deploy` deploys the new Worker version before it configures the Container application. The deploy then fails with the new Worker code already live and no `durable_object` application behind it.

   Instead, add all of the following to the Wrangler configuration:
   - A new SQLite-backed Durable Object class.
   - A Durable Object binding for the new class.
   - A new Container entry with `"scheduling_policy": "durable_object"`, a different `name`, and `class_name` set to the new class.

   Leave the existing Container entry, Durable Object class, and binding unchanged so that you can route traffic back to them. Changing the existing entry's `name` or `class_name` can cause Wrangler to create a new application instead of updating the existing one.

   The following example uses `exports`. If the Worker uses the legacy `migrations` array, add a new migration with `new_sqlite_classes: ["DurableSandbox"]` instead.

   ```jsonc
   {
       "name": "sandbox-worker",
       "main": "src/index.ts",
       "compatibility_date": "2026-09-29",
       "containers": [
           // Existing application. Keep this entry unchanged.
           {
               "class_name": "Sandbox",
               "image": "./container/Dockerfile",
               "instance_type": "standard-2",
               "max_instances": 10,
           },
           // Replacement application.
           {
               "name": "sandbox-durable-object",
               "class_name": "DurableSandbox",
               "scheduling_policy": "durable_object",
               "images": {
                   "base": {
                       "dockerfile": "./container/Dockerfile",
                   },
               },
           },
       ],
       "durable_objects": {
           "bindings": [
               {
                   "name": "SANDBOX",
                   "class_name": "Sandbox",
               },
               {
                   "name": "DURABLE_SANDBOX",
                   "class_name": "DurableSandbox",
               },
           ],
       },
       "exports": {
           "Sandbox": {
               "type": "durable-object",
               "storage": "sqlite",
           },
           "DurableSandbox": {
               "type": "durable-object",
               "storage": "sqlite",
           },
       },
   }
   ```

   ```toml
   name = "sandbox-worker"
   main = "src/index.ts"
   compatibility_date = "2026-09-29"

   [[containers]]
   class_name = "Sandbox"
   image = "./container/Dockerfile"
   instance_type = "standard-2"
   max_instances = 10

   [[containers]]
   name = "sandbox-durable-object"
   class_name = "DurableSandbox"
   scheduling_policy = "durable_object"

   [containers.images.base]
   dockerfile = "./container/Dockerfile"

   [[durable_objects.bindings]]
   name = "SANDBOX"
   class_name = "Sandbox"

   [[durable_objects.bindings]]
   name = "DURABLE_SANDBOX"
   class_name = "DurableSandbox"

   [exports.Sandbox]
   type = "durable-object"
   storage = "sqlite"

   [exports.DurableSandbox]
   type = "durable-object"
   storage = "sqlite"
   ```


2. **Move startup configuration into the replacement class.**

   Select the image and instance size when the replacement Durable Object starts its Container:

   *src/index.jsjs*

   

   ```js
   import { DurableObject } from "cloudflare:workers";

   export class DurableSandbox extends DurableObject {
   	startContainer() {
   		if (this.ctx.container.running) {
   			return;
   		}

   		this.ctx.container.start({
   			image: this.ctx.container.images.base,
   			instance: "standard-2",
   			// Match the existing application's outbound access.
   			enableInternet: true,
   		});
   	}
   }
   ```

   *src/index.tsts*

   

   ```ts
   import { DurableObject } from "cloudflare:workers";

   export class DurableSandbox extends DurableObject {
       startContainer() {
           if (this.ctx.container.running) {
               return;
           }

           this.ctx.container.start({
               image: this.ctx.container.images.base,
               instance: "standard-2",
               // Match the existing application's outbound access.
               enableInternet: true,
           });
       }
   }
   ```

   Move other supported startup settings, such as `env` and `entrypoint`, into the same call. Set `enableInternet` to match the existing application. The `Container` class allows outbound Internet access unless you set `enableInternet = false`.

   `ctx.container.start()` returns before the Container is ready to accept requests. Add an application-specific readiness check before sending traffic to the Container.
3. **Deploy and validate the replacement application.**

   Deploy both applications:npmyarnpnpm

   ```
   npx wrangler deploy
   ```

   ```
   yarn wrangler deploy
   ```

   ```
   pnpm wrangler deploy
   ```

   Start a replacement Container without changing production routing, for example through a test-only route that uses the `DURABLE_SANDBOX` binding. Confirm that the image, instance size, environment, entrypoint, network access, and readiness behavior match the existing application.
4. **Cut traffic over to the replacement namespace.**

   Update the Worker routing logic to resolve Container IDs through the replacement Durable Object binding. The same name resolves to a different Durable Object in each namespace. A Durable Object in the replacement namespace cannot access storage from the old namespace.

   Choose the binding per logical Container, not per request. If requests for the same name can reach both bindings, such as during a percentage-based rollout or a [gradual deployment](https://developers.cloudflare.com/workers/versions-and-deployments/gradual-deployments/), two Containers can run for one logical sandbox and their state can diverge. For example, record which namespace each sandbox uses and read that record when routing:

   ```ts
   // isMigrated() is application code that reads a per-sandbox record.
   const binding = (await isMigrated(sandboxName))
       ? env.DURABLE_SANDBOX
       : env.SANDBOX;
   const sandbox = binding.getByName(sandboxName);
   ```

   If Durable Object state must move, complete the application-specific transfer for each sandbox before routing it to the replacement namespace.
5. **Observe the replacement application.**

   Keep the old application, class, and binding during the observation period. To roll back, route traffic to the old Durable Object binding again.

   Writes made after cutover stay in the replacement namespace. If the application accepts writes, plan how to reconcile that data before rolling back.
6. **Delete the old Container application.**

   After the rollback period, remove the old Container entry from the Wrangler configuration and the old routing path from the Worker code. Deploy the updated Worker. Keep the old Durable Object class and binding until you no longer need its stored data.

   Removing the entry from Wrangler configuration does not delete the existing Container application. List the applications and copy the old application ID:npmyarnpnpm

   ```
   npx wrangler containers list
   ```

   ```
   yarn wrangler containers list
   ```

   ```
   pnpm wrangler containers list
   ```

   Delete the old application:npmyarnpnpm

   ```
   npx wrangler containers delete <OLD_APPLICATION_ID>
   ```

   ```
   yarn wrangler containers delete <OLD_APPLICATION_ID>
   ```

   ```
   pnpm wrangler containers delete <OLD_APPLICATION_ID>
   ```

   This command deletes the application and its Container instances.

   Delete the old Durable Object class only when its stored data is no longer needed. Deleting a Durable Object class permanently deletes its namespace and stored data. Refer to [Durable Object class exports](https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations/) or [Durable Object class migrations (legacy)](https://developers.cloudflare.com/durable-objects/reference/durable-object-class-migrations-legacy/) for the applicable cleanup process.

## Related resources

- [Scheduling Policies](https://developers.cloudflare.com/containers/configuration/scheduling-policy/)
- [Image Management](https://developers.cloudflare.com/containers/guides/image-management/)
- [Durable Object Container API](https://developers.cloudflare.com/containers/api/durable-object-container/)
- [Migrate to the Durable Object Container API](https://developers.cloudflare.com/containers/guides/migrate-to-durable-object-container-api/)

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/containers/guides/migrate-to-durable-object-scheduling-policy/#page","headline":"Migrate to the Durable Object scheduling policy","description":"Replace a Container application that uses the default scheduling policy with a Durable Object-managed application.","url":"https://developers.cloudflare.com/containers/guides/migrate-to-durable-object-scheduling-policy/","inLanguage":"en","image":"https://developers.cloudflare.com/containers/guides/migrate-to-durable-object-scheduling-policy/og.png?v=c4a9b833bc8d835b","dateModified":"2026-09-30","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```
