mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-07 03:00:29 +00:00
feat(fly): add fly.toml and full deploy guide
fly.toml points Fly directly at ghcr.io/fabro-sh/fabro:nightly (no builder step), pins internal_port to 32276 since Fly does not inject $PORT, declares a Volume mount at /storage, and disables autostop so the run queue stays live under no HTTP traffic. Replaces the deploy-fly-io.mdx stub with a CLI-first walkthrough covering volume creation, secrets, dev token retrieval, and the single-Machine / single-Volume caveats that apply to Fabro. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
36e51237d4
commit
484a3dbc39
2 changed files with 136 additions and 2 deletions
|
|
@ -1,8 +1,107 @@
|
|||
---
|
||||
title: "Fly.io"
|
||||
description: "Deploy Fabro to Fly.io"
|
||||
description: "Deploy Fabro to Fly.io from the prebuilt GHCR image, with a Fly Volume for state"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
This guide is coming soon. Deployment guides are currently in development.
|
||||
The server interface is in private early access. Contact [bryan@qlty.sh](mailto:bryan@qlty.sh) if you're interested in trying it.
|
||||
</Warning>
|
||||
|
||||
[Fly.io](https://fly.io) can host the Fabro server by pulling the pre-built image published to GHCR. The repo ships a `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`](https://fly.io/docs/flyctl/install/).
|
||||
|
||||
## First-deploy checklist
|
||||
|
||||
### 1. Adopt `fly.toml` in a new app
|
||||
|
||||
```bash
|
||||
git clone https://github.com/fabro-sh/fabro
|
||||
cd fabro
|
||||
fly launch --copy-config --no-deploy
|
||||
```
|
||||
|
||||
`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`:
|
||||
|
||||
```bash
|
||||
fly volumes create storage --size 1 --region <your-primary-region>
|
||||
```
|
||||
|
||||
Grow later with `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:
|
||||
|
||||
```bash
|
||||
fly secrets set \
|
||||
ANTHROPIC_API_KEY=... \
|
||||
SESSION_SECRET=...
|
||||
```
|
||||
|
||||
The [Server Configuration](/administration/server-configuration) reference has the full list; the minimum useful set:
|
||||
|
||||
| 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
|
||||
|
||||
```bash
|
||||
fly deploy
|
||||
```
|
||||
|
||||
Fly pulls `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:
|
||||
|
||||
1. **The dev token** — on first boot, Fabro writes one to `/var/fabro/dev-token` and logs it. Read it via the Machine's shell:
|
||||
|
||||
```bash
|
||||
fly ssh console -C "cat /var/fabro/dev-token"
|
||||
```
|
||||
|
||||
Or tail the startup logs with `fly logs`.
|
||||
|
||||
2. **Point your local CLI at the server** — add the Fly URL to `~/.fabro/settings.toml`:
|
||||
|
||||
```toml title="~/.fabro/settings.toml"
|
||||
[server]
|
||||
target = "https://<your-app>.fly.dev/api/v1"
|
||||
```
|
||||
|
||||
Then commands like `fabro model list --server <url>` will hit your Fly instance.
|
||||
|
||||
See [Running the Fabro Server](/administration/deploy-server) for the full auth and CLI-pointing story.
|
||||
|
||||
## 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
|
||||
|
||||
- **`$PORT` is not injected.** Unlike Railway and Render, Fly does not set a `PORT` environment variable. The Fabro image binds to `$PORT` if set, otherwise `32276` — `fly.toml` pins `internal_port = 32276` so the default works. If you change `internal_port`, also `fly 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](https://fly.io/docs/volumes/snapshots/) or a backup you manage.
|
||||
- **Single Machine.** Don't `fly scale count` above 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 `:nightly` tag is multi-arch, but the arm64 variant is not currently usable — stay on amd64.
|
||||
- **Autostop is disabled.** `fly.toml` sets `auto_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
|
||||
|
||||
<Columns cols={2}>
|
||||
<Card title="Running the Fabro Server" icon="server" href="/administration/deploy-server">
|
||||
Auth, dev tokens, submitting runs, and pointing the CLI at your deployment.
|
||||
</Card>
|
||||
<Card title="Server Configuration" icon="gear" href="/administration/server-configuration">
|
||||
Full `settings.toml` reference — TLS, auth methods, concurrency, and more.
|
||||
</Card>
|
||||
</Columns>
|
||||
|
|
|
|||
35
fly.toml
Normal file
35
fly.toml
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Fly.io deployment config for the Fabro server.
|
||||
#
|
||||
# Pulls the multi-arch image published to GHCR instead of building from
|
||||
# source on every deploy. Persists state to a Fly Volume mounted at
|
||||
# /storage. The Fabro binary binds to $PORT or 32276, so matching
|
||||
# internal_port to 32276 avoids needing to wire PORT as a secret.
|
||||
#
|
||||
# First-time setup:
|
||||
# fly launch --copy-config --no-deploy # adopts this file; sets app name/region
|
||||
# fly volumes create storage --size 1 # required — fly.toml cannot create volumes
|
||||
# fly secrets set ANTHROPIC_API_KEY=... SESSION_SECRET=...
|
||||
# fly deploy
|
||||
|
||||
app = "fabro"
|
||||
primary_region = "ord"
|
||||
|
||||
[build]
|
||||
image = "ghcr.io/fabro-sh/fabro:nightly"
|
||||
|
||||
[[mounts]]
|
||||
source = "storage"
|
||||
destination = "/storage"
|
||||
|
||||
[http_service]
|
||||
internal_port = 32276
|
||||
force_https = true
|
||||
auto_stop_machines = "off"
|
||||
auto_start_machines = false
|
||||
|
||||
[[http_service.checks]]
|
||||
path = "/health"
|
||||
method = "GET"
|
||||
interval = "30s"
|
||||
timeout = "5s"
|
||||
grace_period = "10s"
|
||||
Loading…
Add table
Reference in a new issue