Skip to content

Machine

Source: src/Fly/Machine.ts

A Fly.Machine is a Firecracker VM running a container image.

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. image updates in place.

const web = yield* Fly.Machine("Web", {
app: Site,
region: "iad",
image: "nginx:alpine",
});

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. Guest updates in place.

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

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 stops Machines that already exist. It does not mint new ones. Yield more Machine resources to size the pool.

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 is one VM. Yield another Machine to add capacity. Fly’s proxy load-balances published services across them.

A Service still scales with count. That is one program, many Machines.

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.

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 waits until the Machine has seen at least that App secrets version. Use it after rotating a Secret if the process must start with the new value.

const web = yield* Fly.Machine("Web", {
app: Site,
region: "iad",
image: "nginx:alpine",
minSecretsVersion: 2,
});