Part 4: Stages
In Part 3 you wrote integration tests against a deployed stack. But so far everything has landed in a single environment. In this part you’ll use stages — isolated instances of the same Stack — to give every developer, environment, and (in Part 5) every pull request its own copy of the infrastructure.
The default stage
Section titled “The default stage”You’ve been using stages all along. If you don’t pass --stage,
Alchemy deploys to dev_$USER (e.g. dev_sam):
$ whoamisam
$ bun alchemy deploy# deploys to stage `dev_sam`Each developer on your team automatically gets a personal sandbox without any config. The resolution order is:
--stage <name>flag$STAGEenvironment variabledev_${USER}(ordev_${USERNAME}on Windows)dev_unknownif no user is set
Deploy a second stage
Section titled “Deploy a second stage”Pass --stage to deploy a completely separate instance of the same
program:
bun alchemy deploy --stage stagingPlan: 2 to create + Api (AWS.Lambda.Function) + Bucket (AWS.S3.Bucket) Proceed? ◉ Yes ○ No ✓ Bucket (AWS.S3.Bucket) created ✓ Api (AWS.Lambda.Function) created { url: "https://def456uvw.lambda-url.us-east-1.on.aws", }
Everything is created fresh — a new bucket, a new function, a new
Function URL — because staging has never been deployed before.
Your dev_sam deployment is untouched.
How stages isolate
Section titled “How stages isolate”Each stage gets its own:
- State — the persisted record of what’s deployed, kept
separately per stage in the
AWS.state()S3 bucket - Physical names —
myapp-prod-bucket-7d4evsmyapp-dev_sam-bucket-9b2c; the stage is baked into every generated resource name - Logs and metrics — scoped per deployed function
Because of this, deploying or destroying one stage never touches another:
alchemy deploy --stage prodalchemy deploy --stage pr-42alchemy destroy --stage pr-42 # only removes pr-42 resourcesStage names must match [a-z0-9][-_a-z0-9]*.
Configure per stage
Section titled “Configure per stage”The same program deploys to every stage, but you’ll often want
different knobs per environment — more Lambda memory in prod, say.
Stack.useSync gives module-scope resources a synchronous accessor
to the surrounding Stack context, including the current stage:
import * as AWS from "alchemy/AWS";import * as S3 from "alchemy/AWS/S3";import { Stack } from "alchemy/Stack";import * as Effect from "effect/Effect";// ...
export default class Api extends AWS.Lambda.Function<Api>()( "Api", { main: import.meta.url, url: true }, Stack.useSync((stack) => ({ main: import.meta.url, url: true, memory: stack.stage === "prod" ? 1024 : 512, })), Effect.gen(function* () { /* ... */ }),) {}Now alchemy deploy --stage prod provisions a 1024 MB function while
every other stage stays at 512 MB — one program, per-stage
configuration, no separate config files.
Destroy a stage
Section titled “Destroy a stage”When you’re done with an environment, tear down just that stage:
bun alchemy destroy --stage stagingThe plan lists everything in staging for deletion and leaves every
other stage alone.
Stages in tests
Section titled “Stages in tests”The test harness from Part 3 defaults to a stage named test. You
can override it per file — or per call — which is how multiple CI
runs can test in parallel against the same AWS account without
colliding:
const { test, beforeAll, afterAll, deploy, destroy } = Test.make({ providers: AWS.providers(), state: AWS.state(), stage: process.env.STAGE ?? "test",});See Test harness → stage for the per-call form
(deploy(Stack, { stage: "ci-pr-42" })).
Stage vs profile
Section titled “Stage vs profile”Stages isolate what is deployed (state, physical names).
Profiles isolate how Alchemy authenticates
to AWS. They’re orthogonal — you can pair a prod stage with a
prod profile that points at a separate AWS account, or use the same
credentials across many stages:
alchemy deploy --stage prod --profile prodalchemy deploy --stage pr-42 --profile defaultYou now have:
- A personal
dev_<user>stage that every plainalchemy deploytargets - A
stagingstage deployed (and destroyed) with--stage - Per-stage Lambda memory via
Stack.useSync - Tests pinned to their own stage
In Part 5, you’ll put stages to work in CI:
GitHub Actions deploys prod on pushes to main and an isolated
pr-<n> preview stage for every pull request.