Skip to content

[miniflare] Clean up Containers on shutdown - #15756

Draft
ghostwriternr wants to merge 3 commits into
mainfrom
naresh/managed-workerd-shutdown
Draft

ghostwriternr wants to merge 3 commits into
mainfrom
naresh/managed-workerd-shutdown

Conversation

@ghostwriternr

@ghostwriternr ghostwriternr commented Sep 21, 2026

Copy link
Copy Markdown
Member

Container-backed workerd processes can remove their application containers and proxy sidecars only during graceful shutdown. Miniflare currently terminates every workerd process immediately, while Wrangler and Vite attempt incomplete Docker cleanup from image tags.

Use bounded SIGTERM shutdown for Container-enabled runtimes, wait for the exact child to exit, and escalate to SIGKILL after five seconds. Keep ordinary and emergency shutdown immediate. Remove caller-owned Docker sweeps so workerd remains the sole owner of runtime containers, sidecars, snapshot volumes, and cleanup ordering.


  • Tests
    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because:
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because: This change only corrects internal local runtime lifecycle.

SIGKILL is correct for ordinary Workers: dispose and reload should
not wait for HTTP connections to drain.

Durable Object containers need a different path. workerd can remove
the application container and proxy sidecar only during SIGTERM
drain, so those runtimes get SIGTERM with a timeout back to SIGKILL.

Track disposal with the exact child so concurrent callers and
replacements observe one completed termination.
@changeset-bot

changeset-bot Bot commented Sep 21, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 0a5f2c4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 8 packages
Name Type
miniflare Patch
wrangler Patch
@cloudflare/vite-plugin Patch
@cloudflare/deploy-helpers Patch
@cloudflare/pages-shared Patch
@cloudflare/remote-bindings Patch
@cloudflare/runtime-types Patch
@cloudflare/vitest-plugin Patch

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

@ghostwriternr
ghostwriternr added this pull request to stack #15757 September 21, 2026 17:23
@github-project-automation github-project-automation Bot moved this to Untriaged in workers-sdk Sep 21, 2026
@ghostwriternr ghostwriternr changed the title naresh/managed workerd shutdown [miniflare] Clean up Containers on shutdown Sep 21, 2026
@pkg-pr-new

pkg-pr-new Bot commented Sep 21, 2026

Copy link
Copy Markdown
@cloudflare/autoconfig

npm i https://pkg.pr.new/@cloudflare/autoconfig@15756

@cloudflare/build-output-utils

npm i https://pkg.pr.new/@cloudflare/build-output-utils@15756

@cloudflare/codemods

npm i https://pkg.pr.new/@cloudflare/codemods@15756

@cloudflare/config

npm i https://pkg.pr.new/@cloudflare/config@15756

@cloudflare/containers-shared

npm i https://pkg.pr.new/@cloudflare/containers-shared@15756

create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@15756

@cloudflare/deploy-helpers

npm i https://pkg.pr.new/@cloudflare/deploy-helpers@15756

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@15756

miniflare

npm i https://pkg.pr.new/miniflare@15756

@cloudflare/pages-functions

npm i https://pkg.pr.new/@cloudflare/pages-functions@15756

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@15756

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@15756

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@15756

@cloudflare/vitest-plugin

npm i https://pkg.pr.new/@cloudflare/vitest-plugin@15756

@cloudflare/workers-auth

npm i https://pkg.pr.new/@cloudflare/workers-auth@15756

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@15756

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@15756

wrangler

npm i https://pkg.pr.new/wrangler@15756

commit: 0a5f2c4

Comment on lines +956 to +962
void this.#runtime?.disposeImmediately();
// This exit hook is synchronous — the event loop will never run again
// after it returns, so any operation that schedules a microtask (like
// fs.promises.rm) will never even be executed, let alone completed.
// We must use the sync variant here.
// `Runtime#dispose()` should kill the runtime immediately but it might not,
// so we only clean up on a best effort basis.
// `Runtime#disposeImmediately()` sends SIGKILL synchronously, but this hook
// cannot wait for the child to exit, so subsequent cleanup is best-effort.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

disposeImmediately() forces SIGKILL for every exitHook path. The SIGINT/SIGTERM listeners call this callback before exiting, so Ctrl-C cannot give container-enabled workerd its required SIGTERM drain and managed Docker resources can leak. dispose() sends the appropriate signal synchronously, so it can safely be invoked from this hook.

Suggested change
void this.#runtime?.disposeImmediately();
// This exit hook is synchronous — the event loop will never run again
// after it returns, so any operation that schedules a microtask (like
// fs.promises.rm) will never even be executed, let alone completed.
// We must use the sync variant here.
// `Runtime#dispose()` should kill the runtime immediately but it might not,
// so we only clean up on a best effort basis.
// `Runtime#disposeImmediately()` sends SIGKILL synchronously, but this hook
// cannot wait for the child to exit, so subsequent cleanup is best-effort.
void this.#runtime?.dispose();
// This exit hook is synchronous — the event loop will never run again
// after it returns, so any operation that schedules a microtask (like
// fs.promises.rm) will never even be executed, let alone completed.
// We must use the sync variant here.
// `Runtime#dispose()` sends the shutdown signal synchronously. Container
// runtimes receive SIGTERM to drain managed resources, while ordinary
// runtimes still receive SIGKILL.

@ask-bonk

ask-bonk Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

I'm Bonk, and I've done a quick review of your PR.

Moves Container cleanup responsibility to workerd's managed shutdown path.

  1. High packages/miniflare/src/index.ts:956: Exit hooks force SIGKILL, bypassing Container workerd's required graceful drain on Ctrl-C/SIGTERM. Posted an inline suggestion.

github run

Wrangler and Vite know which images they prepare, but not every runtime
resource workerd creates. Image-tag sweeps can remove the application
container while missing proxy sidecars and snapshot volumes.

Keep image preparation and awaited Miniflare disposal, remove
caller-owned Docker scans, and cover exact application and sidecar
cleanup through managed close.
@ghostwriternr
ghostwriternr force-pushed the naresh/managed-workerd-shutdown branch from a2b3200 to 0a5f2c4 Compare September 22, 2026 00:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

2 participants