fly-io-deployer

TotalClaw 作者 kn7d5eszdfwftk153ymhdm4qhs83qsqy v1.0.0

在 Fly.io 上部署和操作 Node、Python、Go、Rust、Elixir 和 Docker 应用程序,包括生产级 Fly.toml 创作、Machines API 编排、区域选择(延迟 vs 主权 vs 出口)、Fly Postgres 集群、用于 SQLite 复制的 LiteFS、Upstash Redis 绑定、Tigris 对象存储、持久卷、6PN 的 WireGuard 专用网络、通过 Flyctl 提供的机密、通过自动停止机器进行蓝/绿部署、缩放至零策略、计划扩展、每个 PR 的预览部署、多区域副本、热配置重新加载、机器 SSH、日志传送到 Better Stack/Axiom/Datadog/Logtail 以及积极的成本调整。在“fly.io”、“flyctl”、“flymachines”、“fly.toml”、“fly postgres”、“litefs”、“tigris”、“upstash on Fly”、“fly部署”、“从heroku迁移”、“从渲染迁移”、“从铁路迁移”、“缩放到零”、“飞行区域”、“飞行体积”、“飞行wireguard”上触发, 《6pn》、《飞行的秘密》。

安装 / 下载方式

TotalClaw CLI推荐
totalclaw skills install shop:totalclaw:kn7d5eszdfwftk153ymhdm4qhs83qsqy~fly-io-deployer
cURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/totalclaw%3Akn7d5eszdfwftk153ymhdm4qhs83qsqy~fly-io-deployer/file -o fly-io-deployer.md
## 概述(中文)

在 Fly.io 上部署和操作 Node、Python、Go、Rust、Elixir 和 Docker 应用程序,包括生产级 Fly.toml 创作、Machines API 编排、区域选择(延迟 vs 主权 vs 出口)、Fly Postgres 集群、用于 SQLite 复制的 LiteFS、Upstash Redis 绑定、Tigris 对象存储、持久卷、6PN 的 WireGuard 专用网络、通过 Flyctl 提供的机密、通过自动停止机器进行蓝/绿部署、缩放至零策略、计划扩展、每个 PR 的预览部署、多区域副本、热配置重新加载、机器 SSH、日志传送到 Better Stack/Axiom/Datadog/Logtail 以及积极的成本调整。在“fly.io”、“flyctl”、“flymachines”、“fly.toml”、“fly postgres”、“litefs”、“tigris”、“upstash on Fly”、“fly部署”、“从heroku迁移”、“从渲染迁移”、“从铁路迁移”、“缩放到零”、“飞行区域”、“飞行体积”、“飞行wireguard”上触发, 《6pn》、《飞行的秘密》。

## 原文

# Fly.io Deployer

Plan, ship, and operate apps on Fly.io's Machines platform with the discipline of a senior platform engineer who has migrated production stacks off Heroku, Render, Railway, and AWS. Produces a deployable `fly.toml`, a region plan, a stateful-services plan (Postgres / LiteFS / Redis / Tigris), a CI/CD pipeline, and a cost model — all sized for the actual traffic shape, not the marketing demo.

## Usage

Invoke when starting a new Fly app, migrating onto Fly, debugging a sick deploy, planning multi-region rollout, or cutting the bill. Equally useful for greenfield ("we want to ship a Rust API to fly") and rescue work ("our Render bill tripled, get us off in 2 weeks").

**Basic invocation:**
> Deploy this Node + Postgres app to Fly.io
> Migrate our Heroku stack (web + worker + Postgres + Redis) onto Fly
> Cut our $1,400/mo Fly bill in half without dropping regions

**With context:**
> Here's the Dockerfile and the Heroku Procfile — produce fly.toml + a migration runbook
> We need EU + US Postgres replicas with read-your-writes from web nodes
> Auto-stop machines but keep p95 cold-start under 800ms for the web app

The agent emits a `fly.toml`, optional `Dockerfile`, `litefs.yml`, `flyctl` migration scripts, GitHub Actions for deploys + preview environments, secret rotation script, and a one-page cost model.

## Inputs Required

- **App shape** — runtime (Node/Python/Go/Rust/Elixir/Bun/Deno/Docker), framework (Next.js/Rails/Django/FastAPI/Phoenix/Actix), entrypoint
- **Stateful needs** — Postgres? Redis? S3-compatible object storage? File system? SQLite?
- **Traffic profile** — req/s peak, geographic distribution, p95 latency target, daily/weekly seasonality
- **Compliance constraints** — data residency (EU-only? US-only? FRA mandatory?), HIPAA/PCI scope
- **Origin platform** (if migrating) — Heroku / Render / Railway / Vercel / AWS / DigitalOcean
- **Budget ceiling** — monthly USD cap matters when picking machine sizes and replica counts

## Workflow

1. Read the app and classify it: stateless web, stateful web (sessions on disk), worker, scheduled job, ws server, RPC, or full-stack monolith
2. Pick primary region from latency to majority of users + sovereignty (`fly platform regions` enumerates current set)
3. Decide replicas: single-region multi-machine vs multi-region active-active vs primary+read-replicas
4. Choose stateful services: Fly Postgres cluster, LiteFS+SQLite, external Supabase/Neon, Upstash Redis, Tigris/R2/S3
5. Author `fly.toml` (anatomy section below); generate `Dockerfile` if missing
6. Wire secrets via `flyctl secrets set` (never bake into image)
7. Create the app + provision volumes + provision Postgres + attach
8. First deploy with `--strategy=immediate` to a single machine; verify health
9. Scale to target shape with `fly scale count` + `fly machine clone --region`
10. Wire CI (deploy on main, preview app per PR)
11. Wire log shipping (Vector → Better Stack/Axiom/Datadog) and metrics (Fly Prometheus + Grafana)
12. Configure auto-stop / auto-start for cost; tune min_machines_running
13. Document rollback (`fly releases list` + `fly deploy --image <prev-sha>`)

## fly.toml Anatomy

Every field, what it does, and the most common mistake.

```toml
app = "myapp-prod"                       # globally unique; -prod / -staging / -pr-<n>
primary_region = "fra"                   # closest to majority users; influences PG primary
kill_signal = "SIGINT"                   # SIGTERM default; SIGINT for Node/Python graceful
kill_timeout = "30s"                     # must exceed your slowest in-flight request
swap_size_mb = 512                       # ENABLE — saves OOM kills on tight machines

[build]
  dockerfile = "Dockerfile"              # explicit > auto-detect; nixpacks/buildpacks fragile
  # build_target = "runtime"             # multi-stage final stage
  # build_args = { NODE_ENV = "production" }

[deploy]
  strategy = "rolling"                   # rolling | bluegreen | canary | immediate
  max_unavailable = 0.33                 # rolling: fraction down at once
  release_command = "npm run db:migrate" # one-shot machine before traffic shifts
  wait_timeout = "5m"                    # hard ceiling on deploy duration

[env]
  PORT = "8080"                          # match internal_port below
  NODE_ENV = "production"
  LOG_FORMAT = "json"                    # required for proper log shipping
  # Never put secrets here — use `flyctl secrets set`

[experimental]
  auto_rollback = true                   # roll back on health-check failure

[[mounts]]
  source = "data"                        # name a volume created via `fly volumes create`
  destination = "/data"
  initial_size = "10gb"
  auto_extend_size_threshold = 80        # %, auto-extends volume
  auto_extend_size_increment = "5gb"
  auto_extend_size_limit = "100gb"
  snapshot_retention = 7                 # days; default is 5

[[services]]
  internal_port = 8080
  protocol = "tcp"
  auto_stop_machines = "stop"            # stop | suspend | off; suspend = warm pause
  auto_start_machines = true
  min_machines_running = 1               # 0 only if cold start is acceptable
  processes = ["app"]                    # gates which process group serves this port

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

  [[services.ports]]
    port = 443
    handlers = ["tls", "http"]
    [services.ports.tls_options]
      alpn = ["h2", "http/1.1"]
      versions = ["TLSv1.2", "TLSv1.3"]

  [services.concurrency]
    type = "connections"                 # or "requests" for HTTP-aware
    soft_limit = 200                     # start scaling up
    hard_limit = 250                     # refuse new conns

  [[services.tcp_checks]]
    interval = "15s"
    timeout = "2s"
    grace_period = "10s"                 # extends to first-deploy boot

  [[services.http_checks]]
    interval = "10s"
    timeout = "2s"
    grace_period = "30s"
    method = "GET"
    path = "/healthz"
    protocol = "http"
    tls_skip_verify = false
    [services.http_checks.headers]
      X-Health = "fly"

[[vm]]
  size = "shared-cpu-1x"                 # smallest; fine for low-traffic
  memory = "512mb"
  cpus = 1
  cpu_kind = "shared"                    # shared | performance
  # gpu_kind = "a10"                     # only if doing GPU inference
  processes = ["app"]

[processes]
  app    = "node server.js"
  worker = "node worker.js"
  cron   = "node cron.js"

[[statics]]
  guest_path = "/app/public"             # served from machine, off-CPU
  url_prefix = "/static/"

[metrics]
  port = 9091
  path = "/metrics"                      # Fly's Prometheus scrapes this
```

**Common mistakes:**
- `internal_port` does not match `PORT` env → connection refused, healthchecks 502
- `min_machines_running = 0` on a stateful service → first user gets a 30s cold start
- No `release_command` → migrations race the rolling deploy and break readers
- `kill_timeout` shorter than slowest request → 502s on every deploy
- `auto_stop_machines = "stop"` with attached volume but stateful in-RAM cache → cache cold every wake
- `processes` declared but no `[[services]] processes = [...]` filter → worker exposes HTTP

## Region Strategy

Fly has 35+ regions. Picking three is harder than picking one.

**Tiers by latency to global users** (rough p50 from CDN telemetry):

| Tier | Regions | Use case |
|------|---------|----------|
| 1 | `fra` (Frankfurt), `iad` (Ashburn), `sjc` (Sa