Deploy / Fly

Fly

Same image as Railway, with one setting that decides whether the loop survives.

app = "sales-index"

[build]
  dockerfile = "Dockerfile"

[http_service]
  internal_port = 8080
  # A consumer is not a request/response service. Both lines are load-bearing.
  auto_stop_machines = false
  min_machines_running = 1

  [[http_service.checks]]
    path = "/health"
    interval = "30s"
    timeout = "5s"

Scale-to-zero kills the loop

Fly stops machines with no inbound HTTP traffic by default. A consumer receives none — it makes outbound calls — so the defaults suspend it minutes after deploy and the sweep silently stops. auto_stop_machines = false and min_machines_running = 1 are not optional here.

fly launch --no-deploy   # writes fly.toml; replace it with the block above
fly secrets set DATABASE_URL="postgres://…" SL_API_KEY="sk-sl_…"
fly deploy

Secrets land as environment variables in the machine.

Any reachable Postgres works — attach a Fly-managed instance, or point DATABASE_URL at Neon, Supabase, or RDS. Keep it in the same region as the app; the loop commits a transaction per batch, so round-trip latency sets the backfill ceiling.

fly deploy replaces machines one at a time with SIGTERM first. The shutdown handler commits the in-flight batch inside the grace period; the replacement resumes from that cursor.