Skip to content

Fly.Machine reference

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.

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")),
};
}),
) {}

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",
});

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 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",
});

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",
});

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 },
});

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,
},
});

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" },
});

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"] },
],
},
],
});

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"] }],
},
],
},
],
});

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"] }],
},
],
});

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"] }],
},
],
});

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",
});

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 }],
});

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"] },
});

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" },
});

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" },
});

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,
});

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" },
});

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,
});

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.