Fly.Machine reference
Machine
Section titled “Machine”Source:
src/Fly/Machine.ts
A Fly.Machine is a Firecracker VM running one image or a named container group.
Prefer a Service when the program is Effect. A Service is effectful, supports bindings,
and scales with count. Alchemy builds and pushes the image. Use Fly.Machine when you already
have an image.
Machine: Prefer a Service
Section titled “Machine: Prefer a Service”Declare a Service when you own the program. Alchemy bundles
main, builds linux/amd64, and pushes to registry.fly.io.
export default class Api extends Fly.Service<Api>()( "Api", { app: Site, main: import.meta.url, region: "iad", count: 3, port: 3000 }, Effect.gen(function* () { return { fetch: Effect.succeed(HttpServerResponse.text("hello")), }; }),) {}Machine: Launch a Machine
Section titled “Machine: Launch a Machine”The parent is an App. Pin a region and an image. Guest
defaults to shared-cpu 1× / 256 MB. Rolling updates image in place;
blue/green prepares replacement Machines.
const web = yield* Fly.Machine("Web", { app: Site, region: "iad", image: "nginx:alpine",});Machine: Run named containers
Section titled “Machine: Run named containers”Each replica contains the entire group. Container checks and dependencies
control Pilot startup; configure Machine or service checks for deployment
readiness. Rolling updates can restart the entire group when one image
changes. Blue/green requires every named image to use an immutable
repository@sha256: digest and cannot attach volumes. Alchemy replaces
the entire group and applies readiness policy before retiring predecessors.
const preview = yield* Fly.Machine("Preview", { app: Site, containers: [ { name: "api", image: apiImage, healthChecks: [{ http: { port: 3000, path: "/health" } }] }, { name: "worker", image: workerImage, dependsOn: [{ name: "api", condition: "healthy" }] }, ],});Machine: A stable name
Section titled “Machine: A stable name”Machine names are unique per App. Omit name and Alchemy generates
one from the stack, stage, and logical ID.
const web = yield* Fly.Machine("Web", { app: Site, name: "web", region: "iad", image: "nginx:alpine",});Machine: Region
Section titled “Machine: Region”Fly Machines live in a region. Default is iad. See
Regions for the list of codes.
const web = yield* Fly.Machine("Web", { app: Site, region: "ewr", image: "nginx:alpine",});Machine: Guest size
Section titled “Machine: Guest size”guest is CPU kind, CPU count, and memory. Default is shared-cpu,
1 CPU, 256 MB. Rolling updates guest sizing in place; blue/green replaces
the Machines.
const web = yield* Fly.Machine("Web", { app: Site, region: "iad", image: "nginx:alpine", guest: { cpuKind: "shared", cpus: 1, memoryMb: 256 },});Machine: GPU
Section titled “Machine: GPU”Set gpuKind and gpus on guest when the Machine should have a
GPU.
const worker = yield* Fly.Machine("Worker", { app: Site, region: "iad", image: "my-gpu-image:tag", guest: { cpuKind: "performance", cpus: 2, memoryMb: 4096, gpuKind: "a10", gpus: 1, },});Machine: Environment variables
Section titled “Machine: Environment variables”env is merged onto the Machine. Fly also injects App
Secret values as env vars unless the Machine skips secrets.
const worker = yield* Fly.Machine("Worker", { app: Site, region: "iad", image: "my-image:tag", env: { LOG_LEVEL: "info" },});Machine: Publish a proxy service
Section titled “Machine: Publish a proxy service”services publishes ports on Fly’s proxy. {app}.fly.dev over IPv4
still needs an IpAssignment on the parent App. url is
https://{appName}.fly.dev when a proxy service is configured.
Handlers are http, tls, pg_tls, and similar. Set forceHttps
to redirect HTTP to HTTPS. Use startPort / endPort for a
published range.
Omit services (or pass []) for a process that should not be
reachable from the internet.
const web = yield* Fly.Machine("Web", { app: Site, region: "iad", image: "nginx:alpine", services: [ { protocol: "tcp", internalPort: 80, ports: [ { port: 80, handlers: ["http"], forceHttps: true }, { port: 443, handlers: ["tls", "http"] }, ], }, ],});Machine: Service health checks
Section titled “Machine: Service health checks”Add HTTP or TCP checks to a service. Fly uses their results to
determine whether the service is ready to receive traffic. With rolling
updates, reconcile waits for each started replica’s checks before updating
the next replica. Missing or non-passing results are polled within
deploy.healthTimeout (60 seconds by default), then fail deployment with
Fly.ReplicaChecksNotPassing. Later replicas remain unchanged; earlier
updates are not rolled back. A single rolling replica can be unavailable.
Blue/green checks replacements before retiring the old set, with
representative/floor readiness for idle capacity.
const web = yield* Fly.Machine("Web", { app: Site, region: "iad", image: "nginx:alpine", services: [ { protocol: "tcp", internalPort: 80, ports: [{ port: 80, handlers: ["http"] }], checks: [ { type: "http", port: 80, method: "GET", path: "/", protocol: "http", interval: "15s", timeout: "2s", gracePeriod: "30s", headers: [{ name: "X-Health-Check", values: ["alchemy"] }], }, ], }, ],});Machine: Autostart and autostop
Section titled “Machine: Autostart and autostop”autostart starts the Machine when a request arrives. autostop is
"off", "stop", "suspend", or a boolean. minMachinesRunning
keeps that many Machines up for the service.
Autostop only affects Machines that already exist. Set count or declare
more resources to size the pool. Stop boots a new process on autostart;
suspend may resume memory or fall back to a cold start. Suspension is not
SIGTERM shutdown and does not run ordinary shutdown finalizers.
Blue/green supports both policies: it checks a representative and the required running floor while preserving idle nonrepresentatives. It restores the requested idle policy before retiring predecessors and checks any new instance created by restoration. A replacement does not inherit its suspended predecessor’s process memory.
const web = yield* Fly.Machine("Web", { app: Site, region: "iad", image: "nginx:alpine", services: [ { protocol: "tcp", internalPort: 80, autostart: true, autostop: "stop", minMachinesRunning: 0, ports: [{ port: 80, handlers: ["http"] }], }, ],});Machine: Scale up
Section titled “Machine: Scale up”Each Machine resource runs one VM by default. Set count to manage
several replicas together, or declare separate resources for Machines
with different configuration. Fly’s proxy load-balances published
services across them.
Both Machine and Service support count. Default rolling updates
replicas sequentially; blue/green prepares replacements before retirement.
Separate resources do not share an update order or App-wide lease lock.
const web1 = yield* Fly.Machine("Web1", { app: Site, region: "iad", image: "nginx:alpine", services: [ { protocol: "tcp", internalPort: 80, ports: [{ port: 80, handlers: ["http"] }], }, ],});
const web2 = yield* Fly.Machine("Web2", { app: Site, region: "iad", image: "nginx:alpine", services: [ { protocol: "tcp", internalPort: 80, ports: [{ port: 80, handlers: ["http"] }], }, ],});Machine: Scale down
Section titled “Machine: Scale down”Remove a Machine from the stack. The next deploy deletes it.
const web1 = yield* Fly.Machine("Web1", { app: Site, region: "iad", image: "nginx:alpine",});
const web2 = yield* Fly.Machine("Web2", { app: Site, region: "iad", image: "nginx:alpine",});Machine: Attach a disk
Section titled “Machine: Attach a disk”Pass disks as mounts. Alchemy creates a Volume in the Machine’s
app and region. A Volume attaches to one Machine. There is no
standalone Volume resource.
sizeGb can grow in place. Shrinking is not supported. Encryption,
filesystem type, snapshotId, and sourceVolumeId are create-only.
See MountVolume for the full disk spec. From a Service,
prefer MountVolume so the path is part of the binding graph.
const box = yield* Fly.Machine("Box", { app: Site, region: "iad", image: "postgres:16", mounts: [{ path: "/data", sizeGb: 10 }],});Machine: Init
Section titled “Machine: Init”init overrides cmd, entrypoint, exec, swap, and TTY. Updates
in place.
const box = yield* Fly.Machine("Box", { app: Site, region: "iad", image: "postgres:16", init: { cmd: ["postgres", "-c", "shared_buffers=256MB"] },});Machine: Restart policy
Section titled “Machine: Restart policy”restart.policy is "no", "always", "on-failure", or
"spot-price". maxRetries applies when the policy is
"on-failure". Updates in place.
const worker = yield* Fly.Machine("Worker", { app: Site, region: "iad", image: "my-image:tag", restart: { policy: "always" },});Machine: Destroy on exit
Section titled “Machine: Destroy on exit”autoDestroy: true tears the Machine down when its main process
exits. Default is false.
const job = yield* Fly.Machine("Job", { app: Site, region: "iad", image: "my-job:tag", autoDestroy: true, restart: { policy: "no" },});Machine: Skip launch
Section titled “Machine: Skip launch”skipLaunch: true creates or updates the config without starting
the Machine. Default is false. Reconcile otherwise waits until
the Machine is started, and until service checks are passing when
the Machine has them.
const web = yield* Fly.Machine("Web", { app: Site, region: "iad", image: "nginx:alpine", skipLaunch: true,});Machine: Metadata
Section titled “Machine: Metadata”User keys on metadata merge with Alchemy ownership keys
(alchemy.stack, alchemy.stage, alchemy.id, alchemy.type,
alchemy.replica). Those ownership keys are always written so
list() can find owned Machines. Fly Apps have no labels.
const web = yield* Fly.Machine("Web", { app: Site, region: "iad", image: "nginx:alpine", metadata: { role: "edge" },});Machine: Secrets version
Section titled “Machine: Secrets version”minSecretsVersion requires at least that App-secrets version, not an
immutable snapshot. Machine leases do not serialize vault writers.
After rotating a Secret, declare the required floor or another
explicit rollout input; an out-of-band change alone does not watch/redeploy.
const web = yield* Fly.Machine("Web", { app: Site, region: "iad", image: "nginx:alpine", minSecretsVersion: 2,});Machine: Blue/green deployments
Section titled “Machine: Blue/green deployments”Prepare a healthy replacement set before retiring the current Machines. Physical IDs and names change; the App URL remains stable. Volumes, auto-destroy, and skipLaunch are incompatible. Autostop preserves idle nonrepresentatives while a representative passes checks. Raw images must handle their own shutdown signal and stop accepting background work.
Retirement honors each predecessor’s own signal and timeout. Native target leases do not exclude every simultaneous first deployment or snapshot; serialize CI for the same resource. See the deployment guide for idle capacity, secret floors, recovery, and the limits of live-tested parity.
const worker = yield* Fly.Machine("Worker", { app: Site, image: "registry.example.com/worker:v2", deploy: { strategy: "bluegreen", healthTimeout: "60 seconds" }, shutdown: { signal: "SIGTERM", timeout: "30 seconds" }, checks: { ready: { type: "http", port: 3000, path: "/healthz", interval: "5s", timeout: "2s" }, },});Every published service needs its own service check. Before promotion, a failed candidate leaves the old generation serving. After possible promotion, potentially serving replacements are preserved rather than blindly deleted. Retry an interrupted deploy to finish promotion or retirement; destroy discovers unfinished owned generations. See the deployment guide for recovery and limits.