fly.toml that points Fly directly at the image — no build step on Fly’s builders, no Dockerfile evaluation — and declares a Volume mount at /storage so your runs, checkpoints, and sessions survive redeploys.
Fly.io is CLI-first; there is no one-click deploy button. The workflow below uses flyctl.
First-deploy checklist
1. Adopt fly.toml in a new app
fly launch --copy-config keeps the repo’s fly.toml instead of generating a new one; --no-deploy lets you finish wiring secrets and volumes before the first deploy. You’ll be prompted for an app name (must be globally unique on Fly) and a primary region — update fly.toml in place if you change the defaults.
2. Create the persistent Volume
Fabro writes all persistent state — run history, checkpoints, sessions, the default token, and JWT keys — under/storage. Fly containers have ephemeral filesystems, so without a Volume that directory is wiped on every redeploy. fly.toml declares the mount but cannot create the volume itself — provision it with flyctl:
fly volumes extend if needed. The volume name (storage) must match [[mounts]].source in fly.toml.
3. Set required environment variables
Fly stores env vars as encrypted secrets:| Variable | Purpose |
|---|---|
ANTHROPIC_API_KEY / OPENAI_API_KEY / GEMINI_API_KEY / … | At least one LLM provider key for the models you’ll run |
FABRO_DEV_TOKEN | Optional — pre-set the dev token instead of reading the one written to /storage on first boot |
SESSION_SECRET | 64-character hex string; required when the web UI is enabled |
GITHUB_APP_CLIENT_SECRET, GITHUB_APP_WEBHOOK_SECRET, GITHUB_APP_PRIVATE_KEY | Only if you enable GitHub OAuth or the GitHub App integration |
4. Deploy
ghcr.io/fabro-sh/fabro:nightly, attaches the volume, and starts the Machine. The health check on /health must pass before traffic is routed.
Accessing your Fabro server
Once the deploy is healthy, Fly exposes a<app>.fly.dev URL (or your custom domain). Two things to grab:
-
The dev token — on first boot, Fabro writes one to
/var/fabro/dev-tokenand logs it. Read it via the Machine’s shell:Or tail the startup logs withfly logs. -
Point your local CLI at the server — add the Fly URL to
~/.fabro/settings.toml:Then commands like~/.fabro/settings.tomlfabro model list --server <url>will hit your Fly instance.
Redeploys and updates
fly deploy re-pulls the GHCR image on every run. fly.toml references the :nightly tag by default, so redeploying picks up the latest nightly automatically. To pin a specific version, edit fly.toml to reference ghcr.io/fabro-sh/fabro:<version> and redeploy. The /storage Volume survives redeploys, so runs and checkpoints persist.
Caveats
$PORTis not injected. Unlike Railway and Render, Fly does not set aPORTenvironment variable. The Fabro image binds to$PORTif set, otherwise32276—fly.tomlpinsinternal_port = 32276so the default works. If you changeinternal_port, alsofly secrets set PORT=<n>to match.- Volume is load-bearing and not replicated. Fly’s docs recommend at least two Volumes per app for redundancy, but Fabro’s server is single-replica by design — one Machine owns
/storage. Treat this like a traditional VPS: hardware failure means restoring from Fly’s volume snapshots or a backup you manage. - Single Machine. Don’t
fly scale countabove 1 — a second Machine can’t mount the same Volume, and the server assumes a single writer. - Architecture. Fly Machines run x86_64 (amd64) by default. The
:nightlytag is multi-arch, but the arm64 variant is not currently usable — stay on amd64. - Autostop is disabled.
fly.tomlsetsauto_stop_machines = "off"so the Machine stays up for the run queue. Leaving autostop enabled would pause Fabro when there’s no HTTP traffic, stalling any in-flight runs.
Next steps
Running the Fabro Server
Auth, dev tokens, submitting runs, and pointing the CLI at your deployment.
Server Configuration
Full
settings.toml reference — TLS, auth methods, concurrency, and more.