2.0.0-beta.60 - Docker, MicroVMs & RSC
beta.60 is a big one: a new Docker provider, AWS Lambda MicroVMs, Workers for Platforms, React Server Components on the Vite pipeline, class-based Workflows on async Workers, Cloudflare Containers that start fast and stay up, and a ground-up overhaul of the docs. No breaking changes this release.
Docker provider
Section titled “Docker provider”alchemy/Docker brings Image, RemoteImage, Container,
Volume, and Network as Stack resources
(#649).
They drive the docker CLI’s active context — Docker Desktop, a
remote/SSH context, a CI daemon — and live in the same Stack as
your cloud resources. Thanks Austin!
const image = yield* Docker.RemoteImage("postgres-image", { name: "postgres", tag: "18-alpine",});const network = yield* Docker.Network("app-network");const data = yield* Docker.Volume("postgres-data");
const postgres = yield* Docker.Container("postgres", { image, environment: { POSTGRES_PASSWORD: password }, // Redacted-safe ports: [{ external: 15432, internal: 5432 }], volumes: [{ hostPath: data.name, containerPath: "/var/lib/postgresql/data" }], networks: [{ name: network.name, aliases: ["postgres"] }], start: true,});Secrets are passed via process env (never on the CLI), pulled tags are pinned so re-deploys are no-ops, and containers created outside alchemy follow the standard adoption rules. See the new Docker hub and the local services guide.
AWS Lambda MicroVMs
Section titled “AWS Lambda MicroVMs”AWS.Lambda.MicrovmImage is a new Platform for snapshot-booted
microVMs (#712).
Author the in-VM server in TypeScript — alchemy bundles it,
generates the Dockerfile, and builds the snapshot server-side — or
bring your own Dockerfile or a prebuilt artifact:
export class Sandbox extends AWS.Lambda.MicrovmImage< Sandbox, { hello: (message: string) => Effect.Effect<string> }>()("Sandbox") {}
export default Sandbox.make( { main: import.meta.filename, buildRole }, Effect.gen(function* () { return { hello: (message) => Effect.succeed(`hello, ${message}!`), fetch: Effect.gen(function* () { /* raw HTTP route */ }), }; }),);Every instance operation — RunMicrovm, GetMicrovm,
suspend/resume/terminate, auth tokens, image builds — is a
Binding.Service with least-privilege IAM scoping, and
connectMicrovm gives you typed RPC into the running VM:
const sandbox = yield* AWS.Lambda.connectMicrovm(Sandbox, { endpoint, authToken });const reply = yield* sandbox.hello("world");It works cross-cloud too: binding a MicroVM operation from a
Cloudflare.Worker mints an IAM user + assume-role Role so the
Worker can drive AWS MicroVMs at runtime.
We also benchmarked MicroVMs against Cloudflare Containers — 700 isolated cold boots. MicroVMs boot in 2–4.5s regardless of what’s inside; full results in Benchmarking Cloudflare Containers vs AWS MicroVMs. Docs: MicroVMs.
Workers for Platforms
Section titled “Workers for Platforms”Deploy user Workers into a dispatch namespace and route to them
dynamically (#715).
A Worker deploys into a namespace with the new namespace prop —
no duplicate resource type:
const ns = yield* Cloudflare.WorkersForPlatforms.DispatchNamespace("Customers", {});
const userWorker = yield* Cloudflare.Worker("CustomerA", { namespace: ns.name, script: `export default { fetch() { return new Response("hi") } }`,});The platform Worker binds the namespace Effect-natively, or via
env for async Workers:
// Effect-native — yield the binding, get a typed clientconst dispatch = yield* Cloudflare.WorkersForPlatforms.Get(namespace);const userWorker = yield* dispatch.get("customer-a");// async — DispatchNamespace is a valid env bindingconst platform = Cloudflare.Worker("Platform", { main: "./handler.ts", env: { DISPATCH: namespace },});// handler.ts: env.DISPATCH.get("customer-a").fetch(request)Docs: Workers for Platforms.
React Server Components on Vite
Section titled “React Server Components on Vite”Cloudflare.Vite now supports builds that emit more than one
server environment — most notably RSC
(#685).
viteEnvironments selects which environment produces the deployed
Worker entry and which additional server environments are bundled
alongside it:
const app = yield* Cloudflare.Vite("ReactRouterRSC", { compatibility: { flags: ["nodejs_compat"] }, viteEnvironments: { entry: "rsc", children: ["ssr"] },});A single-environment SSR build needs no configuration — the
default is { entry: "ssr", children: [] } — and the client
environment always deploys as static assets. The same PR replaces
the dev-server restart fingerprint with an exact structural
signature, so config changes are never silently missed. Docs:
Frontends on Cloudflare.
Workflows on async Workers
Section titled “Workflows on async Workers”Class-based Cloudflare Workflows now bind to async (non-Effect)
Workers via env, mirroring class-based Durable Objects
(#707):
export const AsyncWorker = Cloudflare.Worker("Async", { main: "./src/worker.ts", env: { MY_WORKFLOW: Cloudflare.Workflow<{ value: string }>("MyWorkflow", { className: "MyWorkflow", }), },});export type Env = Cloudflare.InferEnv<typeof AsyncWorker>;// env.MY_WORKFLOW is the native Workflow<{ value: string }>Your WorkflowEntrypoint class stays plain cloudflare:workers
code; alchemy provisions the workflow and emits the binding.
Cross-script references work with scriptName, same as async DOs.
Docs: Workflows.
Containers start fast and stay up
Section titled “Containers start fast and stay up”Cloudflare Containers deployed with alchemy behaved much worse than the same image deployed with wrangler — at 100 concurrent cold starts, ~2/100 succeeded. Two root causes, both fixed (#708):
- Wrangler-parity defaults —
maxInstancesdefaulted to1, serializing every Durable Object through one container slot. NowmaxInstances: 20, scale-from-zero, andinstanceType: "lite"when no custom limits are set, matching wrangler. - Readiness polling — probes had no per-probe timeout, so a
not-yet-listening port could hang for minutes. The loop now
mirrors
@cloudflare/containersexactly.
Result: 100/100 cold starts, on par with wrangler. And a container that stops or crashes is now transparently restarted on the next request, while a crash-looping container fails fast instead of burning the readiness budget (#711). Docs: Containers.
Effectful compute on ECS and EC2
Section titled “Effectful compute on ECS and EC2”The ServerHost + { fetch } pattern now works end to end on
AWS.ECS.Task
(#713):
the container entry bundles a Bun HTTP server, ships every chunk
into the image, and builds for the architecture the task declares
— verified by a smoke test that deploys a real Fargate service
behind an ALB.
The hosted AWS.EC2.Instance serves HTTP the same way, with a
reboot-safe systemd unit that self-heals transient boot failures
(#714).
And there’s a new AWS.EC2.KeyPair resource — the generated
private key is captured once as a Redacted secret:
const keyPair = yield* AWS.EC2.KeyPair("DeployKey", { keyType: "ed25519" });// keyPair.keyName -> AWS.EC2.Instance({ keyName })// keyPair.privateKey -> Redacted<string>The docs got a ground-up overhaul
Section titled “The docs got a ground-up overhaul”The website is restructured into per-provider hubs behind a
horizontal tab bar — Core · CLI · Cloudflare · AWS · PlanetScale · Neon · More ▾ — with ~100 new or reworked pages
(#721):
- Core — Infrastructure as Code, Infrastructure as Effects, APIs, Environments, State Store, Project structure, Testing.
- CLI — every command documented, including the first-ever
docs for
alchemy unsafe nukeand adopting resources. - Cloudflare / AWS hubs — setup, five-part tutorials, block pages and guides grouped by role (Compute, Frontend, APIs, Data, Messaging, AI, …).
- Every page written against source, tests, and fixtures; every moved URL 301s.
Start at the docs and pick your provider.
Also in this release
Section titled “Also in this release”--yesis fully non-interactive (#728) —alchemy deploy --yesnow auto-accepts the Cloudflare state store upgrade and deploy prompts instead of hanging on a non-TTY, so CI needs no workarounds.- Partial state-store deploys are detected and recovered (#700) — the store health check now verifies a real store is serving, not just that a pre-create stub exists.
Command.Buildmemo includes command + env (#738) — twoStaticSitebuilds differing only in env (per-stageEXPO_PUBLIC_*, API ids) no longer reuse each other’s output.- Retained resources report as
retained(#739) — destroy no longer listsRemovalPolicy: "retain"resources as deleted. Thanks bjorntechTobbe! - The CLI works on Windows
(#696) —
the stack entry loads via a
file://URL. Thanks d3lay! - Durable Object classes created outside alchemy adopt cleanly (#495) — class migrations fall back to matching the observed binding, so adopting a wrangler/dashboard Worker reuses its DO classes instead of failing to re-create them.
- Access Application survives state loss
(#742) —
readfalls back to a domain scan and gates takeover behind--adopt, instead of blindly creating a duplicate app with a freshaud. Thanks Andy Jefferson! - Pooled connection origins — Neon
Project/Branch(#718) and PlanetScalePostgresRole(#717) exposepooledOrigin, and PlanetScale branch replica intent persists soreplicas: 0converges (#719). Thanks Alex! - AWS env-auth fixes —
Regionis provided to STS during login (#709) and theRegion/AWSEnvironmentlayer cycle in the account-id lookup is broken (#710).