1---
2title: Stacks
3description: A Stack is a collection of Resources deployed together as a unit.
4---
5
6import StackOutputsTerminal from "../../../components/marketing-islands/StackOutputsTerminal.tsx";
7
8A **Stack** is the top-level unit of deployment in Alchemy. It groups
9resources together, wires up providers, and tracks state across
10deploys.
11
12## Defining a Stack
13
14A Stack is just an Effect that you `export default` from a TypeScript
15file:
16
17```typescript
18import * as Alchemy from "alchemy";
19import * as Cloudflare from "alchemy/Cloudflare";
20import * as Effect from "effect/Effect";
21
22export default Alchemy.Stack(
23 "MyApp",
24 { providers: Cloudflare.providers(), state: Cloudflare.state() },
25 Effect.gen(function* () {
26 const bucket = yield* Cloudflare.R2.Bucket("Bucket");
27 return { bucketName: bucket.bucketName };
28 }),
29);
30```
31
32`Alchemy.Stack` takes three arguments:
33
341. **Name** — identifies this stack in state storage
352. **Options** — `providers` and `state`, both required (see
36 [State Store](/state-store) for the available state layers)
373. **Effect** — a generator that declares resources and returns outputs
38
39:::note[`alchemy.run.ts` is a convention]
40The CLI looks for `alchemy.run.ts` by default, but **any TypeScript
41file with a default-exported Stack works**. Pass the path as the
42first positional argument:
43
44```sh
45alchemy deploy --config infra/my-stack.ts
46alchemy destroy --config stacks/github.ts
47```
48
49See the [CLI reference](/cli/deploy) for the full invocation
50syntax.
51:::
52
53## Multiple providers
54
55A stack is not limited to one cloud. Providers are Effect Layers, so
56to deploy resources from several providers in a single stack, merge
57their layers with `Layer.mergeAll`:
58
59```typescript
60import * as Alchemy from "alchemy";
61import * as AWS from "alchemy/AWS";
62import * as Cloudflare from "alchemy/Cloudflare";
63import * as Neon from "alchemy/Neon";
64import * as Effect from "effect/Effect";
65import * as Layer from "effect/Layer";
66
67export default Alchemy.Stack(
68 "MyApp",
69 {
70 providers: Layer.mergeAll(
71 Cloudflare.providers(),
72 AWS.providers(),
73 Neon.providers(),
74 ),
75 state: Cloudflare.state(),
76 },
77 Effect.gen(function* () {
78 const db = yield* Neon.Project("Db");
79 const queue = yield* AWS.SQS.Queue("Jobs");
80 const bucket = yield* Cloudflare.R2.Bucket("Uploads");
81 return { projectId: db.projectId };
82 }),
83);
84```
85
86The type system tracks which providers the stack's resources need —
87declaring an AWS resource without `AWS.providers()` in the merged
88layer is a compile-time error.
89
90`state` is independent of `providers`: pick one state layer no
91matter how many clouds the stack deploys to.
92
93See [Providers](/infrastructure-as-code/provider#multiple-providers)
94for more on provider layers.
95
96## Stack outputs
97
98The value returned from the generator becomes the **stack output**.
99After a deploy, outputs are printed to the console and available
100programmatically in tests.
101
102```typescript
103Effect.gen(function* () {
104 const bucket = yield* Cloudflare.R2.Bucket("Bucket");
105 const worker = yield* Worker;
106
107 return {
108 bucketName: bucket.bucketName,
109 url: worker.url,
110 };
111});
112```
113
114The CLI renders them after every successful deploy:
115
116<StackOutputsTerminal client:visible />
117
118## Stages
119
120Every deploy targets a **stage** — an isolated instance of the stack
121like `dev_sam`, `staging`, or `prod`. The stage defaults to
122`dev_$USER`, so each developer gets their own environment
123automatically. State and physical names are namespaced by stage.
124
125```sh
126# Each stage = its own state file + its own physical names.
127$ alchemy deploy --stage dev_sam # -> myapp-dev_sam-photos-a3f1
128$ alchemy deploy --stage pr-147 # -> myapp-pr_147-photos-9b2c
129$ alchemy deploy --stage prod # -> myapp-prod-photos-7d4e
130
131# Three independent deployments. Destroying one
132# never touches the others.
133```
134
135For naming patterns, isolation, and per-stage configuration see
136[Stages](/environments/stages). Credentials per environment are managed
137via [Profiles](/environments/profiles).
138
139## Accessing the Stack
140
141Inside a resource or layer, you can access the current Stack's
142metadata via the `Stack` service:
143
144```typescript
145import { Stack } from "alchemy/Stack";
146import * as Console from "effect/Console";
147
148Effect.gen(function* () {
149 const stack = yield* Stack;
150 yield* Console.log(stack.name); // "MyApp"
151 yield* Console.log(stack.stage); // "dev_sam"
152
153 const queue = yield* SQS.Queue("Jobs").pipe(
154 RemovalPolicy.retain(stack.stage === "prod"),
155 );
156});
157```
158
159### `Stack.useSync` for module-scope resources
160
161When you declare a resource at module scope (outside any
162`Effect.gen`), there is no `yield*` available to read the `Stack`
163service. But a Resource's props can themselves be an Effect —
164`Stack.useSync` builds one that computes props from the current
165stack, useful for parameterizing names by stage:
166
167```typescript
168import * as Axiom from "alchemy/Axiom";
169import { Stack } from "alchemy/Stack";
170
171export const Traces = Axiom.Dataset(
172 "Traces",
173 Stack.useSync(({ stage }) => ({
174 name: `${stage}-traces`,
175 kind: "otel:traces:v1" as const,
176 description: `OTEL traces for stage '${stage}'`,
177 retentionDays: 30,
178 })),
179);
180```
181
182The function runs once at plan time, after the stage is resolved.
183Use it anywhere a resource accepts a `props` object.
184
185## Cross-stack references
186
187A typed `Alchemy.Stack` handle lets one stack read another's
188persisted outputs at plan time:
189
190```typescript
191// backend/src/Stack.ts — typed handle shared across packages
192export class Backend extends Alchemy.Stack<
193 Backend,
194 { url: string }
195>()("Backend") {}
196```
197
198```typescript
199// frontend/alchemy.run.ts — reads the matching stage of Backend
200import { Backend } from "backend";
201
202export default Alchemy.Stack(
203 "Frontend",
204 {
205 providers: Cloudflare.providers(),
206 state: Cloudflare.state(),
207 },
208 Effect.gen(function* () {
209 const backend = yield* Backend;
210
211 const website = yield* Cloudflare.Website.Vite("Website", {
212 env: { VITE_API_URL: backend.url },
213 });
214
215 return { url: website.url.as<string>() };
216 }),
217);
218```
219
220`yield* Backend` defaults to the **current stage** — `sam`
221frontend reads `sam` backend, `pr-42` reads `pr-42`. Use
222`Backend.stage.<name>` to pin to a specific stage instead.
223
224See [References](/infrastructure-as-code/references) for the underlying
225operators and [Monorepo](/project-structure/monorepo) for the
226end-to-end walkthrough (package layout, schema sharing, deploy
227ordering).
228
229A stack is just the container — the things it deploys are
230[Resources](/infrastructure-as-code/resource).
231
232## Where next
233
234- [Resources](/infrastructure-as-code/resource) — the named entities a stack deploys.
235- [Stages](/environments/stages) — naming, isolation, per-stage configuration.
236- [Monorepo](/project-structure/monorepo) — one stack or many across packages.