Skip to content

State Store

Alchemy persists resource state between deploys so it can compute diffs — comparing the desired state in your code against the current state of your infrastructure.

Each resource’s state is keyed by its fully qualified name (FQN), which includes the namespace path and logical ID. State is scoped by stack name and stage, so different stacks and environments are fully isolated.

A resource’s persisted state includes:

  • Resource type — e.g. Cloudflare.R2Bucket
  • Input properties — the props you passed when creating it
  • Output attributes — the values returned after creation
  • Instance ID — a unique identifier for this instance
  • Lifecycle statuscreated, updating, deleting, etc.
  • Bindings — data attached by policies and event sources

By default, state is stored on disk in the .alchemy/ directory:

.alchemy/
state/
MyApp/
dev_sam/
Bucket.json
Worker.json

Add .alchemy/ to your .gitignore:

Terminal window
echo ".alchemy/" >> .gitignore

Local state works for solo development. Each developer gets their own state via their default stage (dev_$USER).

For teams and CI, configure a remote state store so all deploys share the same state. Alchemy includes a Cloudflare-backed store:

Alchemy.Stack(
"MyApp",
{
providers: Cloudflare.providers(),
state: Cloudflare.state(),
},
Effect.gen(function* () {
// ...
}),
);

Cloudflare.state() deploys a Worker backed by a Durable Object with embedded SQLite. The first deploy after adding it migrates local state automatically.

State transitions track the lifecycle of each resource:

StatusMeaning
creatingCreate operation in progress
createdResource exists and is healthy
updatingUpdate operation in progress
updatedResource was updated successfully
deletingDelete operation in progress
replacingResource is being replaced (new one created, old pending delete)

These intermediate states (creating, updating, deleting) exist because state is persisted before the cloud operation completes. If the process crashes mid-operation, Alchemy uses the intermediate state to recover on the next deploy.

For tests, Alchemy provides an in-memory state store so tests don’t touch the filesystem:

import * as TestState from "alchemy/Test/TestState";
// Seed with existing resource state
const state = TestState.state({
Bucket: {
/* ... */
},
});

The test harness (alchemy/Test/Bun and alchemy/Test/Vitest) handles state setup automatically.