LangENKO

Fly.io

`mandu deploy --target=fly` emits a `fly.toml` + `Dockerfile` pair. With `--execute` + `flyctl ≥ 0.1.0` installed, Mandu invokes `fly deploy` on your behalf.

since v0.24
On this page

Fly.io

mandu deploy --target=fly emits a fly.toml and the same multi-stage Dockerfile that ships with --target=docker. With --execute, Mandu invokes fly deploy on your behalf — with the provider token read from Bun.secrets so it never appears on argv or in logs.

# Emit artifacts only
mandu deploy --target=fly

# Set up the API token
mandu deploy --target=fly --set-secret FLY_API_TOKEN=fo1_...

# First-time initialization
fly launch --copy-config

# Full deploy (requires flyctl + token)
mandu deploy --target=fly --execute

Prerequisites

# Install flyctl (macOS / Linux)
curl -L https://fly.io/install.sh | sh

# Windows
iwr https://fly.io/install.ps1 -useb | iex

# Log in (once)
fly auth login

Minimum version: flyctl 0.1.0. Older versions exit with CLI_E206 and a clear upgrade hint.

Emitted files

.
├── Dockerfile
├── .dockerignore
└── fly.toml

The fly.toml

# fly.toml
app = "my-mandu-app"                  # derived from package.json name
primary_region = "iad"                # override via flyctl login / --region

[build]
  # Uses the same Dockerfile as --target=docker

[env]
  PORT = "3333"
  NODE_ENV = "production"

[[services]]
  protocol = "tcp"
  internal_port = 3333
  auto_stop_machines = true
  auto_start_machines = true
  min_machines_running = 0

  [[services.ports]]
    port = 80
    handlers = ["http"]
    force_https = true

  [[services.ports]]
    port = 443
    handlers = ["tls", "http"]

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

[[vm]]
  cpu_kind = "shared"
  cpus = 1
  memory_mb = 512

auto_stop_machines = true scales a Mandu app to zero when idle — cold-start budget is ~850ms on Fly's shared-CPU tier, comfortable for a default deploy.

First deploy

Fly expects a one-time fly launch to create the app slot:

# If you already have fly.toml from `mandu deploy`:
fly launch --copy-config --no-deploy

# Then deploy via Mandu
mandu deploy --target=fly --execute

Alternatively you can skip Mandu's --execute and run fly deploy directly — the fly.toml and Dockerfile are standard.

Required secrets

Name Purpose Store
FLY_API_TOKEN flyctl auth for non-interactive deploys Bun.secrets → OS keychain

Configure per-app secrets (DB URL, session secrets) with flyctl, not mandu deploy --set-secret:

# App-visible env vars at runtime
fly secrets set DATABASE_URL="postgres://..." -a my-mandu-app
fly secrets set SESSION_SECRET="$(openssl rand -hex 32)" -a my-mandu-app

fly secrets set stores values inside Fly's control plane, encrypted at rest. mandu deploy --set-secret is only for Mandu-side secrets (the FLY_API_TOKEN used to authenticate the deploy command itself).

Multi-region

Add regions with flyctl:

fly regions add fra sin   # Frankfurt + Singapore
fly scale count 3         # 3 machines across regions

Mandu's fly.toml uses the flyctl-configured primary_region. If you need declarative multi-region pinning, edit fly.toml directly — the adapter preserves hand edits on subsequent mandu deploy runs.

Health checks

Fly auto-wires health checks against internal_port. The emitted fly.toml uses a TCP check by default. For HTTP, add:

  [[services.http_checks]]
    interval = "10s"
    timeout = "2s"
    method = "get"
    path = "/healthz"

Mandu exposes /healthz by default.

Logs + monitoring

# Tail live logs
fly logs -a my-mandu-app

# Open the metrics dashboard
fly dashboard metrics

Common errors

CLI_E205: required provider CLI flyctl is missing — install with curl -L https://fly.io/install.sh | sh.

CLI_E206: flyctl 0.0.x is older than required minimum 0.1.0 — run fly version update.

CLI_E207: required secret FLY_API_TOKEN is not present — run mandu deploy --target=fly --set-secret FLY_API_TOKEN=fo1_....

Deploy succeeds but 503 on request — check fly logs. Usually a missing app secret (DATABASE_URL, SESSION_SECRET). Set with fly secrets set.

🤖 Agent Prompt

🤖 Agent Prompt — Fly.io
Apply the guidance from the Mandu docs page at https://mandujs.com/docs/deploy/fly to my project.

Summary of the page:
Fly adapter: uses the same non-root Dockerfile as Docker adapter, plus a fly.toml tuned for Mandu (port 3333, auto-stop, auto-start). Secret: `FLY_API_TOKEN` — read from OS keychain, never passed on argv. `--execute` requires flyctl.

Required invariants — must hold after your changes:
- Uses the same multi-stage Dockerfile as `--target=docker`
- Required secret: `FLY_API_TOKEN` — stored in Bun.secrets / OS keychain
- Minimum flyctl version: 0.1.0
- `internal_port = 3333`, `auto_stop_machines = true`, `auto_start_machines = true` by default
- Multi-region: default primary region from flyctl login — override with `primary_region`

Then:
1. Make the change in my codebase consistent with the page.
2. Run `bun run guard` and `bun run check` to verify nothing
   in src/ or app/ breaks Mandu's invariants.
3. Show me the diff and any guard violations.

For Agents

{
  "schema": "mandu.deploy.fly/v0.24",
  "command": "mandu deploy --target=fly",
  "artifacts": ["Dockerfile", "fly.toml", ".dockerignore"],
  "provider_cli": { "binary": "flyctl", "min_version": "0.1.0" },
  "secrets_mandu_side": ["FLY_API_TOKEN"],
  "secrets_app_side": "via `fly secrets set`, NOT `mandu deploy --set-secret`",
  "fly_toml_defaults": {
    "internal_port": 3333,
    "auto_stop_machines": true,
    "auto_start_machines": true,
    "min_machines_running": 0
  },
  "rules": [
    "Run `fly launch --copy-config --no-deploy` once before first `--execute`",
    "App-runtime secrets go via `fly secrets set`, NOT `--set-secret`",
    "`--execute` requires flyctl installed and authenticated"
  ]
}

For Agents

AI hint

Fly adapter: uses the same non-root Dockerfile as Docker adapter, plus a fly.toml tuned for Mandu (port 3333, auto-stop, auto-start). Secret: `FLY_API_TOKEN` — read from OS keychain, never passed on argv. `--execute` requires flyctl.

Invariants
  • Uses the same multi-stage Dockerfile as `--target=docker`
  • Required secret: `FLY_API_TOKEN` — stored in Bun.secrets / OS keychain
  • Minimum flyctl version: 0.1.0
  • `internal_port = 3333`, `auto_stop_machines = true`, `auto_start_machines = true` by default
  • Multi-region: default primary region from flyctl login — override with `primary_region`
Guard scope
deploy