| 1 | --- |
| 2 | title: Machines |
| 3 | description: A Firecracker VM running a container image. |
| 4 | --- |
| 5 | |
| 6 | A [`Fly.Machine`](/providers/fly/machine) is a Firecracker VM |
| 7 | running a container image. Use it when you already have an image. |
| 8 | |
| 9 | ## Launch a Machine |
| 10 | |
| 11 | A Machine runs in a [`Fly.App`](/fly/compute/apps) you declare: |
| 12 | |
| 13 | ```typescript |
| 14 | // src/app.ts |
| 15 | export const Site = Fly.App("Site"); |
| 16 | ``` |
| 17 | |
| 18 | Pin a [region](/fly/compute/regions), an image, and a guest: |
| 19 | |
| 20 | ```typescript |
| 21 | const web = yield* Fly.Machine("Web", { |
| 22 | app: Site, |
| 23 | region: "iad", |
| 24 | image: "nginx:alpine", |
| 25 | guest: { cpuKind: "shared", cpus: 1, memoryMb: 256 }, |
| 26 | services: [ |
| 27 | { |
| 28 | protocol: "tcp", |
| 29 | internalPort: 80, |
| 30 | ports: [ |
| 31 | { port: 80, handlers: ["http"], forceHttps: true }, |
| 32 | { port: 443, handlers: ["tls", "http"] }, |
| 33 | ], |
| 34 | }, |
| 35 | ], |
| 36 | }); |
| 37 | ``` |
| 38 | |
| 39 | `region` replaces on change. `image` updates in place by default; the |
| 40 | opt-in blue/green strategy prepares replacement Machines instead. Guest |
| 41 | defaults to shared-cpu 1× / 256 MB. Port 443 with `tls` is what |
| 42 | makes `https://{app}.fly.dev` answer. Omit `services` (or pass |
| 43 | `[]`) for a worker that should not be reachable from the internet. |
| 44 | |
| 45 | The App needs an |
| 46 | [`IpAssignment`](/fly/networking#addresses-for-a-shared-app) before |
| 47 | `{app}.fly.dev` answers over IPv4. For a public endpoint without |
| 48 | managing Apps and addresses, use a [Service](/fly/compute/services). |
| 49 | |
| 50 | ## Run named containers |
| 51 | |
| 52 | Use `containers` for a group that shares one Machine. Each named container has its own image and can set `cmd`, `entrypoint`, `env`, startup `dependsOn`, and Pilot `healthChecks`. Use `cmd` and `entrypoint` to override startup; container `exec` overrides are not supported. Existing single-image `init.exec` is unchanged. Check intervals and timeouts are numeric seconds. A dependency condition can be `started`, `healthy`, or `exited_successfully`. |
| 53 | |
| 54 | ```typescript |
| 55 | const preview = yield* Fly.Machine("Preview", { |
| 56 | app: Site, |
| 57 | region: "iad", |
| 58 | guest: { cpus: 1, memoryMb: 1024 }, |
| 59 | containers: [ |
| 60 | { name: "api", image: apiImage, healthChecks: [{ http: { port: 3000, path: "/health" } }] }, |
| 61 | { name: "worker", image: workerImage, dependsOn: [{ name: "api", condition: "healthy" }] }, |
| 62 | ], |
| 63 | }); |
| 64 | ``` |
| 65 | |
| 66 | `count` duplicates the entire group. A rolling change to one container can restart the whole group; attached volumes stay with their Machine. Machine `env` and bindings remain shared, while container `env` is passed to Fly as a per-container override. Configure Machine or service readiness checks for every critical component before treating a deploy as ready: Pilot container checks and dependencies control startup, not Alchemy's rollout readiness. Blue/green also supports named containers when every image is an immutable |
| 67 | `repository@sha256:` digest and no volumes are attached. It replaces the |
| 68 | whole group and can overlap old and new workers during promotion. |
| 69 | |
| 70 | If a rolling update is interrupted, Fly may have accepted the update even |
| 71 | though the CLI reported a failure. Wait for the in-progress Machine update to |
| 72 | settle, then rerun the same desired deployment. Reconciliation reuses the |
| 73 | owned Machine and its attached volumes. |
| 74 | |
| 75 | ## Scale up |
| 76 | |
| 77 | A Machine resource runs one VM by default. Set `count` to manage |
| 78 | several replicas together, or declare separate resources for Machines |
| 79 | with different configuration. Fly's proxy load-balances published |
| 80 | `services` across them. |
| 81 | |
| 82 | ```typescript |
| 83 | const web1 = yield* Fly.Machine("Web1", { |
| 84 | app: Site, |
| 85 | region: "iad", |
| 86 | image: "nginx:alpine", |
| 87 | services: [ |
| 88 | { |
| 89 | protocol: "tcp", |
| 90 | internalPort: 80, |
| 91 | ports: [ |
| 92 | { port: 80, handlers: ["http"], forceHttps: true }, |
| 93 | { port: 443, handlers: ["tls", "http"] }, |
| 94 | ], |
| 95 | }, |
| 96 | ], |
| 97 | }); |
| 98 | |
| 99 | const web2 = yield* Fly.Machine("Web2", { |
| 100 | app: Site, |
| 101 | region: "iad", |
| 102 | image: "nginx:alpine", |
| 103 | services: [ |
| 104 | { |
| 105 | protocol: "tcp", |
| 106 | internalPort: 80, |
| 107 | ports: [ |
| 108 | { port: 80, handlers: ["http"], forceHttps: true }, |
| 109 | { port: 443, handlers: ["tls", "http"] }, |
| 110 | ], |
| 111 | }, |
| 112 | ], |
| 113 | }); |
| 114 | ``` |
| 115 | |
| 116 | Both `Fly.Machine` and [`Service`](/fly/compute/services) support |
| 117 | `count`. With the default rolling strategy, replicas within one resource |
| 118 | update sequentially; separate resources do not share that ordering. |
| 119 | Blue/green prepares the desired replacement topology before old retirement. |
| 120 | |
| 121 | `count` is provisioned capacity, not always-running capacity. With |
| 122 | `autostop: "stop"` or `"suspend"`, replicas may be idle. `autostart` allows |
| 123 | requests to wake them, and `minMachinesRunning` controls Fly's running floor. |
| 124 | Blue/green checks a running representative plus the required floor without |
| 125 | permanently waking all idle replicas. A replacement does not inherit suspended |
| 126 | RAM. See [idle-capacity semantics](/fly/compute/deployments#keep-idle-capacity-with-stop-or-suspend). |
| 127 | |
| 128 | ## Scale down |
| 129 | |
| 130 | Remove a Machine from the stack. The next deploy deletes it. |
| 131 | |
| 132 | ```diff lang="typescript" |
| 133 | const web1 = yield* Fly.Machine("Web1", { |
| 134 | app: Site, |
| 135 | region: "iad", |
| 136 | image: "nginx:alpine", |
| 137 | }); |
| 138 | - |
| 139 | - const web2 = yield* Fly.Machine("Web2", { |
| 140 | - app: Site, |
| 141 | - region: "iad", |
| 142 | - image: "nginx:alpine", |
| 143 | - }); |
| 144 | ``` |
| 145 | |
| 146 | ## Env, restart, metadata |
| 147 | |
| 148 | ```typescript |
| 149 | const worker = yield* Fly.Machine("Worker", { |
| 150 | app: Site, |
| 151 | region: "iad", |
| 152 | image: "my-image:tag", |
| 153 | env: { LOG_LEVEL: "info" }, |
| 154 | restart: { policy: "always" }, |
| 155 | }); |
| 156 | ``` |
| 157 | |
| 158 | Alchemy stamps `alchemy.stack` / `alchemy.stage` / `alchemy.id` / |
| 159 | `alchemy.type` onto `config.metadata` so `list()` can find owned |
| 160 | Machines. Fly injects App [secrets](/fly/data/secrets) as env vars |
| 161 | on the Machine. `minSecretsVersion` requires at least that secret version; |
| 162 | it is a floor, not an immutable snapshot. Machine leases do not serialize |
| 163 | standalone `Fly.Secret`, other Services, or runtime vault writers. A later |
| 164 | writer can advance the shared App vault. Set an explicit deployment input |
| 165 | or version floor when an out-of-band rotation requires a new rollout; |
| 166 | secret changes are not automatically watched. |
| 167 | |
| 168 | ## Mounts |
| 169 | |
| 170 | Pass disks as `mounts`. Alchemy creates a Volume in the Machine's |
| 171 | app and region. A Volume attaches to one Machine. |
| 172 | |
| 173 | ```typescript |
| 174 | const box = yield* Fly.Machine("Box", { |
| 175 | app: Site, |
| 176 | region: "iad", |
| 177 | image: "nginx:alpine", |
| 178 | mounts: [{ path: "/data", sizeGb: 1 }], |
| 179 | }); |
| 180 | ``` |
| 181 | |
| 182 | From a Service, prefer [`MountVolume`](/fly/data/volumes) so the |
| 183 | path is part of the binding graph. |
| 184 | |
| 185 | ## Lifecycle |
| 186 | |
| 187 | For replicas required to run, reconcile waits until the Machine is |
| 188 | `started` (`waitMachine`, bounded). It then waits for configured readiness |
| 189 | checks. With default rolling updates, this happens before the next replica. |
| 190 | The default readiness budget is 60 seconds; set `deploy.healthTimeout` |
| 191 | for a longer bounded wait. A failed wait stops the rollout with |
| 192 | `Fly.ReplicaChecksNotPassing` and |
| 193 | the last observed check names, statuses, and output. Later replicas |
| 194 | remain unchanged; earlier updates are not automatically rolled back. |
| 195 | The default rolling strategy updates in place, so a single replica can be |
| 196 | unavailable while it updates. Set `deploy: { strategy: "bluegreen" }` to |
| 197 | [prepare checked replacement Machines before retirement](/fly/compute/deployments). |
| 198 | |
| 199 | For rolling, `skipLaunch: true` creates or updates config without starting |
| 200 | or checking it. By default, delete force-destroys and waits until gone. |
| 201 | Setting `shutdown` or enabling blue/green instead cordons reachable |
| 202 | predecessors, signals each with its own persisted shutdown policy, waits |
| 203 | for it to stop, and verifies deletion. A new generation's shorter timeout |
| 204 | must not shorten the old process's grace window. |
| 205 | |
| 206 | A raw image must handle signals, stop accepting work, and drain requests or jobs. |
| 207 | These props do not install the Effect runtime in it. A genuinely unreachable |
| 208 | host may require force-deleting an owned stateless predecessor once its |
| 209 | replacement is ready. This cannot prove the old work drained. See the |
| 210 | [deployment and recovery limits](/fly/compute/deployments). |
| 211 | |
| 212 | Alchemy uses native Machine leases. It reports contention rather than stealing |
| 213 | a lease. These leases do not lock the whole App or serialize every first |
| 214 | deployment. Serialize CI deployments to the same resource. LocalState is not |
| 215 | a cross-process lock, and App deletion has no Machine-lease precondition. |
| 216 | |
| 217 | `autoDestroy: true` removes a Machine when its process exits. Blue/green requires |
| 218 | a persistent process and rejects this option, explicit `skipLaunch`, attached |
| 219 | volumes, and missing readiness checks. It reports unsupported settings rather |
| 220 | than silently switching to rolling. Stop and suspend are supported through the |
| 221 | [idle-capacity rules](/fly/compute/deployments#keep-idle-capacity-with-stop-or-suspend). |
| 222 | |
| 223 | ## Service or Sprite |
| 224 | |
| 225 | Prefer a [`Service`](/fly/compute/services) when the program is |
| 226 | Effect. A Service is effectful, supports bindings, and scales with |
| 227 | `count`. It owns its App and addresses, and `api.url` is its public |
| 228 | endpoint. Alchemy builds and pushes the image. Prefer a |
| 229 | [`Sprite`](/fly/compute/sprites) when the sandbox can hibernate — |
| 230 | no App, no Docker image. |
| 231 | |
| 232 | ```typescript |
| 233 | export default class Api extends Fly.Service<Api>()( |
| 234 | "Api", |
| 235 | { main: import.meta.url, region: "iad", count: 3, port: 3000 }, |
| 236 | Effect.gen(function* () { |
| 237 | return { |
| 238 | fetch: Effect.succeed(HttpServerResponse.text("hello")), |
| 239 | }; |
| 240 | }), |
| 241 | ) {} |
| 242 | ``` |
| 243 | |
| 244 | ## Where next |
| 245 | |
| 246 | - [Blue/green deployments](/fly/compute/deployments) — readiness, graceful shutdown, and recovery. |
| 247 | |
| 248 | - [Services](/fly/compute/services) — bundle an Effect program into |
| 249 | a Machine. |
| 250 | - [Regions](/fly/compute/regions) — where a Machine lives. |
| 251 | - [Volumes](/fly/data/volumes) — persistent disks. |
| 252 | - [`Machine` reference](/providers/fly/machine) — every prop and |
| 253 | attribute. |