Search docs

Jump between documentation pages.

Fly.io

Fly runs your container as one or more Machines. Use the Node adapter, ship a Dockerfile, and let auto_stop_machines scale you to zero when idle.

When to choose Fly

  • You want multiple regions cheaply, with anycast routing for free.
  • You want a single image that also runs on ECS or Kubernetes elsewhere.
  • You want raw TCP without a serverless workaround.

Server entrypoint

Use the Node adapter and bind to the Fly-provided PORT:

ts
// src/server.ts
import { serve } from "@daloyjs/core/node";
import { app } from "./app.js";

serve(app, {
  port: Number(process.env.PORT ?? 3000),
  hostname: "0.0.0.0",
});

fly.toml

auto_stop_machines takes a string ("off", "stop", or "suspend") and not a boolean.

toml
# fly.toml
app = "my-daloy-api"
primary_region = "fra"

[build]

[http_service]
  internal_port = 3000
  force_https = true
  auto_stop_machines = "stop"
  auto_start_machines = true
  min_machines_running = 0

  [http_service.concurrency]
    type = "requests"
    soft_limit = 200
    hard_limit = 250

[[http_service.checks]]
  interval = "10s"
  timeout = "2s"
  grace_period = "5s"
  method = "GET"
  path = "/healthz"

[[vm]]
  size = "shared-cpu-1x"
  memory = "256mb"

Dockerfile

docker
FROM node:24-slim AS build
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build

FROM gcr.io/distroless/nodejs24-debian12
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
USER nonroot
EXPOSE 3000
CMD ["dist/server.js"]

Deploy

bash
brew install flyctl
fly launch --no-deploy
fly secrets set SESSION_SECRET=...
fly deploy

Gotchas

  • Set shutdownTimeoutMson the Node adapter to a value smaller than Fly's grace period so in-flight requests drain before the machine is killed.
  • Make sure /healthz is cheap. The lifecycle plugin ships a ready-made one.

See also