| 1 | --- |
| 2 | title: Command |
| 3 | description: Alchemy's cloud-agnostic primitives for local processes in the deploy graph — memoized builds, one-off commands, and dev servers. |
| 4 | --- |
| 5 | |
| 6 | The Command provider puts local child processes into the Stack's |
| 7 | dependency graph: `Build` for a command that produces an output |
| 8 | asset, `Exec` for a command run purely for its side effects, and |
| 9 | `Dev` for a long-lived dev server. There is no setup — no |
| 10 | credentials, nothing to configure. `Command.providers()` is already |
| 11 | merged into both `AWS.providers()` and `Cloudflare.providers()`, so |
| 12 | big-hub users have it for free; a standalone Stack registers it with |
| 13 | `{ providers: Command.providers(), state: Alchemy.localState() }`. |
| 14 | Import from `alchemy/Command`. |
| 15 | |
| 16 | ## Resources |
| 17 | |
| 18 | `Build` runs a command that must produce an output asset (`outdir`) |
| 19 | and tracks it in state: |
| 20 | |
| 21 | ```typescript |
| 22 | const build = yield* Command.Build("vite-build", { |
| 23 | command: "npm run build", |
| 24 | cwd: "./frontend", |
| 25 | outdir: "dist", |
| 26 | }); |
| 27 | yield* Console.log(build.outdir); // path to the dist directory, relative to process.cwd() |
| 28 | yield* Console.log(build.hash.output); // hash of the output files (when memo is enabled) |
| 29 | ``` |
| 30 | |
| 31 | Inputs are content-hashed by default, so an unchanged project skips |
| 32 | the rebuild entirely — see [Memoization](/command/memoization) for |
| 33 | how the hash is computed and how to scope it. |
| 34 | |
| 35 | `Exec` runs a command purely for its side effects: |
| 36 | |
| 37 | ```typescript |
| 38 | yield* Command.Exec("codegen", { |
| 39 | command: "npm run codegen", |
| 40 | cwd: "./packages/api", |
| 41 | }); |
| 42 | |
| 43 | yield* Command.Exec("migrate", { |
| 44 | command: "npm run db:migrate", |
| 45 | env: { |
| 46 | DATABASE_URL: Redacted.make("postgres://..."), |
| 47 | }, |
| 48 | }); |
| 49 | ``` |
| 50 | |
| 51 | There is no output contract: the resource succeeds iff the command |
| 52 | exits with code `0`, is skipped when its inputs are unchanged, and |
| 53 | re-runs when the `command`, `env`, or memoized files change — e.g. a |
| 54 | recreated database's connection URL with identical files. Deleting |
| 55 | an `Exec` never reverses its side effects. |
| 56 | |
| 57 | `Dev` runs a long-lived process scoped to the stack instance: |
| 58 | |
| 59 | ```typescript |
| 60 | const dev = yield* Command.Dev("Frontend", { |
| 61 | command: "npm run dev", |
| 62 | }); |
| 63 | yield* Console.log(dev.url); // e.g. "http://localhost:5173" |
| 64 | ``` |
| 65 | |
| 66 | `Dev` only runs during `alchemy dev` — during `alchemy deploy` it is |
| 67 | a no-op. See [Dev servers](/command/dev-servers) for the process |
| 68 | lifecycle, restarts, and URL extraction. |
| 69 | |
| 70 | :::caution |
| 71 | Command strings are whitespace-split into a binary and arguments — |
| 72 | there is no quoting support unless you set `shell`. `Build`'s delete |
| 73 | removes the output directory (`Exec`'s delete does nothing). |
| 74 | `Redacted` env values are unwrapped at spawn time and kept out of |
| 75 | logs and state. |
| 76 | ::: |
| 77 | |
| 78 | Pre-beta.58 `Build.Command` / `Build.DevServer` state converges |
| 79 | automatically to `Command.Build` / `Command.Dev` on the next deploy |
| 80 | ([beta.58](/blog/2026-06-24-beta-58)). |
| 81 | |
| 82 | ## Compose with your cloud |
| 83 | |
| 84 | The static-site guides for |
| 85 | [Cloudflare](/cloudflare/frontend/static-site) and |
| 86 | [AWS](/aws/frontend/static-site) create a `Command.Build` (and, in |
| 87 | dev mode, a `Command.Dev`) under the hood, and |
| 88 | [`alchemy dev`](/cli/dev) is the loop that hosts `Dev` processes. |
| 89 | |
| 90 | ## Reference |
| 91 | |
| 92 | [Build](/providers/command/build) · |
| 93 | [Dev](/providers/command/dev) · |
| 94 | [Exec](/providers/command/exec) |