Local development
alchemy dev runs your stack on your machine. Workers execute in
workerd, KV Namespaces, R2 Buckets, D1 Databases, and Queues are
emulated locally, and code changes hot reload in milliseconds.
Alchemy.remote() runs any resource against the real cloud when you
need it.
How it works
Section titled “How it works”alchemy dev✓ Bucket (Cloudflare.R2.Bucket) created (local) ✓ DB (Cloudflare.D1.Database) created (local) ✓ Worker (Cloudflare.Worker) created (local → workerd) • http://localhost:1337 Watching for changes ...
Three things happen:
- Emulatable services run locally — KV Namespaces, R2 Buckets,
D1 Databases, and Queues are created as local simulators with
dev:-prefixed ids. No cloud calls. D1 appliesmigrationsDirandimportFilesagainst the local database. - Workers run locally in workerd — your code executes in workerd, the same runtime used in production, with bindings wired to the local simulators.
- File changes hot reload — edit your code and the Worker rebuilds instantly.
Resources whose provider has no local implementation (a Hyperdrive,
a Vectorize index) deploy to the real cloud, into your personal
stage (dev_$USER by default), so your loop
never collides with teammates or prod. A stack that mixes emulated
and live-only resources just works.
Hot module reloading
Section titled “Hot module reloading”Watching for changes ... ↻ src/worker.ts changed • Rebuilding worker ... ✓ Worker reloaded in 42ms ↻ src/api.ts changed • Rebuilding worker ... ✓ Worker reloaded in 38ms
- Code changes take effect in milliseconds, not minutes
- Resources stay running across reloads — only your application code is rebuilt
Arbitrary dev processes — Vite, Next, anything with a dev server —
join the loop via Command.Dev: started by
alchemy dev, restarted when its inputs change, and a no-op on
deploy.
Local by default, live on demand
Section titled “Local by default, live on demand”KV, R2, D1, and Queues ship a local provider. In dev they are
simulators running inside workerd, the same runtime used in
production, so binding behavior matches what you deploy. A local
resource’s id is dev:-prefixed, which doubles as proof that no
cloud call ran.
The local simulators reach beyond Worker bindings. Node-side
capability clients follow the same rule: a seed script in an
Action that writes to a dev: KV
Namespace lands in the same local simulator your Worker reads.
Every other resource runs live in dev automatically, and
Alchemy.remote() (below) runs an emulatable resource against the
real cloud when local fidelity isn’t enough.
Worker-only bindings are simulated too: a
send_email binding persists
messages as .eml files under .alchemy/local/email instead of
delivering mail, Browser Rendering drives a locally-launched headless
Chrome, Images transforms run through Sharp, and Stream uploads land
in a local video store. Each of these opts out the same way as a
resource — pipe the binding through Alchemy.remote() (below) to hit
the real service from the dev loop:
env: { EMAIL: email, // Alchemy.remote() on the SendEmail descriptor BROWSER: Cloudflare.Browser("BROWSER").pipe(Alchemy.remote()),}Debugging
Section titled “Debugging”Because Workers run locally in workerd, you can attach a debugger:
- Set breakpoints in your IDE
- Inspect variables and call stacks
- Profile performance
Resource adaptation
Section titled “Resource adaptation”Resources adapt their behavior in dev mode:
- Cloudflare Workers — run locally in workerd with a cloud proxy instead of deploying to the edge
- Vite dev servers — integrate with Alchemy’s dev server for frontend hot reloading alongside your backend
- D1 Databases — emulate locally, with
migrationsDirandimportFilesapplied to the local database on every reconcile
A resource emulates locally when its provider ships a local implementation; everything else is live in dev automatically. See Local Providers to build one.
Running a resource live in dev
Section titled “Running a resource live in dev”Alchemy.remote() opts a resource out of local emulation — “run this live
even in dev”:
import * as Alchemy from "alchemy";
const worker = yield* Worker("Api", { ... }).pipe(Alchemy.remote());During alchemy deploy the pin is a no-op — everything is live anyway.
Opt out a whole scope
Section titled “Opt out a whole scope”yield* Effect.gen(function* () { const api = yield* Worker("Api", { ... }); const site = yield* Worker("Site", { ... });}).pipe(Alchemy.remote());The policy is ambient and captured at registration (like adopt()), so
everything inside the scope runs live. Resources with no local emulation
(a Hyperdrive, a Vectorize index) already run live in dev — remote() on
them is a no-op.
Queues support remote() in both directions: a local Worker binding a
live queue produces real messages, and a local queue() handler drains
the live queue through a pull consumer — with the usual local batching,
retry, and dead-letter semantics.
Switching is a replacement
Section titled “Switching is a replacement”When a resource moves between local and live — you deploy after a dev
session, or add/remove remote() — it’s planned as a replacement: the
new instance is created live, and the local one (the workerd instance, the
dev process) is torn down.
Custom port
Section titled “Custom port”export default Cloudflare.Worker("Worker", { main: import.meta.url, dev: { port: 3000, }, // ...});Dev vs Deploy
Section titled “Dev vs Deploy”alchemy dev |
alchemy deploy |
|
|---|---|---|
| KV, R2, D1, Queues | Local simulators (remote() for live) |
Deployed to cloud |
| Everything else | Deployed to cloud | Deployed to cloud |
| Application code | Runs locally in workerd | Deployed to cloud |
| File watching | Hot reload on change | Manual redeploy |
| Debugging | Attach local debugger | Tail logs |
| Speed | Milliseconds | Seconds to minutes |
To automate the deploy side, see the CI guide.
Where next
Section titled “Where next”- CI — deploy the same stack from GitHub Actions with PR previews.
- Stages — how
dev_$USER,pr-42, andprodstay isolated. - Dev servers — run any framework dev server as a
Command.Devresource in the dev loop. - Local Providers — build the local implementation of a resource.