[wrangler] Allow containers to be attached to Durable Objects via exports - #15026
[wrangler] Allow containers to be attached to Durable Objects via exports#15026petebacondarwin wants to merge 2 commits into
exports#15026Conversation
…ports`
A container can now be linked to its Durable Object from the export side,
using a new `container` field that names an entry in the `containers`
array:
"containers": [{ "name": "my-container", "image": "./Dockerfile" }],
"exports": {
"MyContainerDO": {
"type": "durable-object",
"storage": "sqlite",
"container": "my-container"
}
}
As a result `containers[].class_name` is now optional — a container that
is referenced this way only needs a `name`. This decouples container
configuration from the Durable Object class, which is a prerequisite for
configuring containers as standalone resources.
The link is resolved by a shared helper in `@cloudflare/workers-utils`,
used by wrangler's container normalization, the Vite plugin's dev/preview
container builds, and `unstable_getMiniflareWorkerOptions`. Upload
metadata sends both directions as configured (`containers[].name` and
`exports[].container`) so that the API can do its own resolution;
wrangler only pre-resolves for local dev and Cloudchamber app creation.
Cross-field validation now reports dangling `container` references, two
exports claiming the same container, the two directions disagreeing, a
container linked to no Durable Object, `class_name` with no live export,
and duplicate container names.
…exports` Deploys a Worker whose container is linked only from the Durable Object's `exports` entry, then asserts `ctx.container` is present on the deployed Durable Object. That is only true when the API resolved the link from the export side, so it covers the server half of the feature end to end. A second case moves the same link back to `containers[].class_name` and re-deploys, verifying the container stays attached and that the two directions really are interchangeable. The image is pushed once up front under a known tag so that teardown can delete it deterministically, and so neither deploy has to build it. The block is skipped when Docker is unavailable.
🦋 Changeset detectedLatest commit: 69c5d21 The changes in this PR will be included in the next version bump. This PR includes changesets to release 5 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Codeowners approval required for this PR:
Show detailed file reviewers
|
|
|
Here I've completed a thorough review. The changes are well-structured, the validation is comprehensive, edge cases (tombstones, inheritance, duplicate names, both-direction consistency) are handled, and test coverage is extensive. I found no logic bugs, security issues, backward-compat violations, or incorrect API behavior. LGTM |
@cloudflare/autoconfig
@cloudflare/build-output-utils
@cloudflare/config
create-cloudflare
@cloudflare/deploy-helpers
@cloudflare/kv-asset-handler
miniflare
@cloudflare/pages-functions
@cloudflare/pages-shared
@cloudflare/unenv-preset
@cloudflare/vite-plugin
@cloudflare/vitest-pool-workers
@cloudflare/workers-auth
@cloudflare/workers-editor-shared
@cloudflare/workers-utils
wrangler
commit: |
Fixes DEVX-2628.
Containers can now be attached to a Durable Object from the export side, using a new
containerfield on adurable-objectexport that names an entry in thecontainersarray:{ "name": "my-worker", "main": "worker.js", "compatibility_date": "2026-07-01", "containers": [ { "name": "my-container", "image": "./Dockerfile", "max_instances": 1 } ], "exports": { "MyContainerDO": { "type": "durable-object", "storage": "sqlite", "container": "my-container" } } }As a result
containers[].class_nameis now optional — a container referenced this way only needs aname. This decouples container configuration from the Durable Object class, which is a prerequisite for configuring containers as standalone resources.The existing
containers[].class_namedirection keeps working, and either direction may be used, but a Durable Object and its container must reference each other consistently when both are set.How the link is resolved
Wrangler sends both directions in the upload metadata exactly as configured (
containers[].nameandexports[Class].container) and the API does its own resolution. Wrangler only pre-resolves the class name locally where it genuinely needs it — local dev image tagging and Cloudchamber app creation — via a single shared helper (resolveContainerClassNamein@cloudflare/workers-utils) used by wrangler, the Vite plugin, andunstable_getMiniflareWorkerOptions.New validation
containeris only valid on livedurable-objectexports (createdandexpecting-transfer) and requiresstorage: "sqlite". Cross-field validation now reports:containerreference naming a container that isn't definedclass_namepointing at a class with no livedurable-objectexport (only when theexportsflow is in use — the legacymigrationsflow keeps its silent-ignore behaviour)name(behaviour change: previously the duplicate was silently tolerated)Also included
@cloudflare/config:containeradded toexports.durableObject()for the experimentalcloudflare.config.tsformat.fixtures/container-appnow uses the new shape (wrangler.registry.jsoncretains theclass_name+migrationsequivalent for comparison).validateContainerAppwhere thenametype check tested the wholecontainersarray rather than each entry, so it never fired.partitionExportsagainst malformed/unknown export types, which previously threw rather than letting validation report them.Test coverage added:
packages/workers-utils/tests/config/containers.test.ts— unit tests for the shared resolver.packages/workers-utils/tests/config/validation/normalize-and-validate-config.test.ts— all six validation rules, pluscontainershape andlegacy-kvrejection.packages/wrangler/src/__tests__/containers/{config,deploy,schema}.test.tsandcreate-worker-upload-form/metadata.test.ts— resolution, deploy, and upload-metadata coverage.packages/wrangler/src/__tests__/dev.test.tsandpackages/vite-plugin-cloudflare/src/__tests__/containers.spec.ts— local dev paths.packages/config/src/__tests__/{convert,schema}.test.ts.packages/wrangler/e2e/durable-objects-exports.test.ts— a livewrangler deploye2e that assertsctx.containeris present on the deployed Durable Object (so it covers the API's half of the resolution end to end), plus a case that moves the link toclass_nameand re-deploys to confirm the two directions are interchangeable. Skipped when Docker is unavailable.A picture of a cute animal (not mandatory, but encouraged)
Note
This is a contribution from an AI agent: OpenCode, claude-opus-5.