podfly 0.8.0 copy "podfly: ^0.8.0" to clipboard
podfly: ^0.8.0 copied to clipboard

Deploy Serverpod apps on Fly, Railway, DigitalOcean, Render, Cloud Run, AWS, Azure, and Hetzner via existing cloud CLIs. Thin orchestrator (not a host): podfly.yaml, Docker quirks, Flutter web packagi [...]

podfly #

podfly — fueling Serverpod launches

Deploy Serverpod on real cloud infrastructure without memorizing each provider’s CLI, config, and quirks.

podfly is a thin orchestrator: it shells out to existing tools (fly, railway, wrangler, vercel, netlify, gh, neonctl, …), generates the right config, and encodes battle-tested defaults (Flutter web packaging, scale-to-zero, DB wiring). It is not a new host — it makes the hosts you already use boring to ship to.

serverpod create …     →  Dockerfile + monorepo (Serverpod)
podfly deploy          →  provider CLIs + configs + quirks (podfly)
pub.dev pub package
Repo github.com/127thousand/podfly

Serverpod Cloud vs podfly #

The Serverpod project’s managed offering is Serverpod Cloud — API, web, Insights, and the rest of the product surface on infrastructure built for Serverpod.

podfly is the path when you want to keep your own infra (Fly, Railway, and similar) and still avoid hand-rolling every CLI and config quirk. Use one or the other; they solve different problems.


Install #

dart pub global activate podfly

Ensure ~/.pub-cache/bin (or your platform’s pub cache bin) is on PATH. Upgrade with the same command.

Contributors / unreleased:

dart pub global activate --source git https://github.com/127thousand/podfly.git
# or: dart pub global activate --source path /path/to/podfly
Tool When
Flutter / Dart Always
Host CLI (fly, railway, doctl, …) Only for the host: you chose (wizard asks)
Railway CLI host: railway (often ~/.railway/bin)
doctl + Docker host: digitalocean (DOCR registry required)
wrangler Flutter web · web_host: cloudflare
vercel Flutter web · web_host: vercel
netlify-cli Flutter web · web_host: netlify
gh + git Flutter web · web_host: github_pages
neonctl database.neon.provision: true

podfly doctor checks these, can install missing CLIs (TTY or PODFLY_AUTO=1), and can open login flows on a TTY.

CI: see doc/ci.md (FLY_API_TOKEN / RAILWAY_TOKEN / CDN tokens + --yes --no-login).


Quick start #

Value prop: create a Serverpod project, then one command ships it. You do not hand-write fly.toml / railway.toml / app specs first — podfly generates them when missing.

serverpod create my_app --mini -f   # Serverpod: monorepo + Dockerfile
cd my_app
fly auth login                      # once per machine (or set FLY_API_TOKEN in CI)
podfly deploy --yes --smoke         # creates podfly.yaml + fly.toml if needed, deploys, smokes

That’s the happy path. No prior podfly.yaml, no prior host config — only a normal Serverpod tree and a logged-in host CLI (or CI token).

Do I need fly.toml? #

Situation What happens
Fresh project podfly deploy writes a starter fly.toml (or railway.toml, etc.) then deploys
File already exists podfly leaves your settings alone (only patches app = when the Fly app name changes)
Committed in an example / prod repo Optional lock-in for reviewable, deterministic CI — not a prerequisite to use podfly

Same idea for podfly.yaml: created on first deploy (--yes = non-interactive defaults). Commit both when you care about stable names and settings in git; delete them and redeploy to regenerate starters.

Examples by cloud (separate repo, monorepo leaves):
github.com/127thousand/podfly_examples — e.g. fly/api_only, render/api_postgres.
Package pointer: example/mobile_api_only (Fly API-only).

What deploy automates (Fly path today):

  1. Doctor tools + auth
  2. Init podfly.yaml if missing (--yes = non-interactive)
  3. Detect web vs API-only (e.g. mobile without web/)
  4. Ensure API app exists (needed before Postgres attach)
  5. Database ensure (fly_postgres / railway_postgres / neon / none)
  6. Write fly.toml / railway.toml if missing; Serverpod-style Dockerfile only if missing
  7. Patch production publicHost
  8. Build/deploy + optional smoke
podfly deploy --dry-run
podfly deploy --api
podfly deploy --host railway --api --yes --smoke
podfly deploy --web
podfly doctor
podfly init
podfly smoke

What you get today #

Mode UI API
🔀 split 🟠 Cloudflare Pages (CDN) + API host 🟣 Fly / 🚂 Railway / 🌊 DO
🧱 monolith UI with the API host (or DO native web app) 🟣 Fly / 🚂 Railway / 🌊 DO
📱 API-only — (web.enabled: false; usually mode: monolith) 🟣 Fly / 🚂 Railway / 🌊 DO

mode: fly is still accepted as a legacy alias for monolith.

Database When
🚫 none Stateless
💾 sqlite Single machine + volume (Fly)
🟣 fly_postgres Classic Serverpod on Fly (attach → Serverpod config)
🚂 railway_postgres Railway Postgres plugin (host: railway)
🌊 digitalocean_postgres DO Managed Postgres (host: digitalocean)
🟢 neon Serverless PG

Insights and full managed Serverpod ops: Serverpod Cloud (not podfly).

Serverpod version compatibility #

podfly is a host orchestrator — it does not depend on the serverpod package. Compatibility is about project layout, Dockerfile, and config files.

Serverpod Status Notes
4.x (incl. current beta) Primary / tested Examples, fallback Dockerfile template, and docs target 4.x
3.4.x Smoke-tested See below
Older than 3.4 Untested Likely fine if the project already has a working Serverpod Dockerfile

Verified on Serverpod 3.4.11 (real Fly deploys, 2026-07-20):

Project Database Result
serverpod create … --mini none API deploy + smoke POST /greeting/hello → 200
serverpod create … --template server fly_postgres (create + attach + patch production.yaml / passwords.yaml) API deploy + same smoke → 200

What made 3.x work: podfly used the project’s own Dockerfile (dart:3.8 + dart compile exe) and conventional *_server / config paths.

What is still 4-oriented (avoid relying on these for 3.x):

  • Fallback Dockerfile if yours is missing (templates/Dockerfile.serverpod is Serverpod 4-style: Dart 3.10 + dart build cli)
  • Examples and CI sample pin Serverpod 4 / Dart 3.10

Rule of thumb: keep the Dockerfile from serverpod create for your major version; run podfly deploy --api --yes --smoke. Do not delete the Dockerfile and expect the generated 4-style template to build a 3.x app.


Provider roadmap #

podfly status = first-class in this tool.
Fit = how well that host matches Serverpod’s process model, independent of whether podfly implements the deploy yet.

Topology keys #

Topology Meaning When it fits
📱 API-only One public API port. Mobile or other clients. Any container/PaaS that runs a single process + port.
🔀 Split Static Flutter web on a CDN; API on an app host. Best of both: CDN for multi‑MB WASM; API can scale to zero.
🧱 All-in-one API + static web on one machine (multi-port / multi-role). Hosts that allow multi-port Machines or a reverse proxy.

Serverpod Insights is not covered by podfly. For Insights and the full managed product surface, use Serverpod Cloud.

App hosts #

Provider CLI podfly 📱 API-only 🔀 Split UI+API 🧱 All-in-one Notes
💜 Serverpod Cloud Serverpod Cloud Serverpod’s managed host (not via podfly)
🟣 Fly.io fly / flyctl Default podfly path; multi-port Machines OK
🚂 Railway railway 🟡 Separate API + static web services
🟠 Cloudflare Pages wrangler ✅ UI ✅ UI Static Flutter web only (web_host: cloudflare); not the API
Vercel vercel ✅ UI ✅ UI Static Flutter web (web_host: vercel); not the API
🟢 Netlify netlify ✅ UI ✅ UI Static Flutter web (web_host: netlify); not the API
🐙 GitHub Pages gh ✅ UI ✅ UI Static Flutter web (web_host: github_pages); not the API
🟦 Render render 🟡 🟡 Git + Docker; monorepo rootDir; render_postgres
☁️ Google Cloud Run gcloud 🟡 ✅* Cheap serverless; *monolith = nginx + Serverpod one container (see gcp/realtime_monolith)
📦 AWS App Runner aws 🟡 🟡 Notes: no WebSockets (managed Envoy 403); not free scale-to-zero
📦 AWS ECS + ALB aws_ecs Fargate + ALB; WebSockets work (unlike App Runner)
🔷 Azure Container Apps az 🟡 ✅* Notes: Docker→ACR→env/app; scale-to-zero; *monolith = nginx image
Hetzner Cloud hcloud 🟡 ✅* Notes: VPS + Docker/SSH + Caddy HTTPS; bind or create; *no scale-to-zero
🌊 DigitalOcean App Platform doctl 🟡 DOCR images + App Spec; web = separate app

Fit legend: ✅ natural · 🟡 possible with constraints · ❌ poor fit · 🗺️ podfly not implemented yet · — N/A

Hosted Postgres #

Provider CLI / API podfly Notes
🚫 None Stateless APIs
🟢 Neon neonctl Serverless PG; pairs with sleeping APIs
🟣 Fly Postgres fly postgres Private network; often bills when API is stopped
🚂 Railway Postgres Railway CLI database.provider: railway_postgres
💾 SQLite (+ Fly volume) fly volumes Single-machine only
Supabase CLI / URL 🗺️ Managed PG
🟦 Render Postgres render postgres database.provider: render_postgres
📦 AWS RDS aws 🗺️ Enterprise default
☁️ Google Cloud SQL gcloud 🗺️ BYO: set cloud_run.cloud_sql_instances + unix socket in production.yaml
🔷 Azure Database for PostgreSQL az 🗺️ Azure default
🌊 DigitalOcean Managed Postgres doctl digitalocean_postgres + app firewall

podfly legend: ✅ supported today · 🗺️ planned

Want another provider? Open an issue — preference is excellent DX or clouds most teams already pay for.

Redis / shared state (later) #

Most small apps do not need Redis (Postgres is enough). When you run multiple instances and need shared cache or PubSub (live streams across machines), Serverpod can use Redis.

Provider podfly Notes
🔺 Upstash 🗺️ later Serverless Redis + TLS; Serverpod’s own Cloud docs use it as the third-party example; great fit with scale-to-zero hosts
Host-managed Redis Fly/Railway/etc. Redis plugins — manual config today

Parked as an extra, not core cheap path: optional redis / Upstash wiring (env + production.yaml patch) after Cloud Run and the main host set settle.


Example podfly.yaml (split, no database) #

host: fly
mode: split   # or monolith — UI with API host, no Pages
name: sacred-draw
server: tarot_draw_server
flutter: tarot_draw_flutter

fly:
  app: sacred-draw
  region: iad
  config: fly.toml
  scale_to_zero: true
  ha: false

cloudflare:
  project: sacred-draw
  branch: main

database:
  provider: none

web:
  enabled: true
  server_url_define: SERVER_URL
  api_url: https://sacred-draw.fly.dev/
  patch_bootstrap: true
  write_headers: true

smoke:
  api:
    method: POST
    path: /tarot/draw
    body: '{}'
    expect_status: 200
  web:
    path: /
    expect_status: 200

Railway API-only sketch:

host: railway
mode: monolith
name: my-api
server: my_app_server
web:
  enabled: false
database:
  provider: railway_postgres
  railway_postgres:
    create: true

DigitalOcean full stack sketch:

host: digitalocean
mode: monolith
name: my-app
server: my_app_server
flutter: my_app_flutter
digitalocean:
  app: my-app
  region: nyc
database:
  provider: digitalocean_postgres
  digitalocean_postgres:
    create: true
    region: nyc1
web:
  enabled: true

Full field list: doc/podfly.yaml.md.


Documentation #

Doc Contents
User guide Flow, flags, automation, troubleshooting
CI / GitHub Actions Tokens, example workflows, dry-run on PR
Caching & Flutter web WASM, service worker, _headers
Database Providers + detection
Config reference podfly.yaml fields
AGENTS.md Rules for coding agents
llms.txt LLM / doc index
Skill Grok skill (/podfly)
CHANGELOG Release history
Design specs Architecture decisions

License #

MIT

0
likes
130
points
301
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

Deploy Serverpod apps on Fly, Railway, DigitalOcean, Render, Cloud Run, AWS, Azure, and Hetzner via existing cloud CLIs. Thin orchestrator (not a host): podfly.yaml, Docker quirks, Flutter web packaging, Postgres wiring, smoke checks.

Repository (GitHub)
View/report issues

Topics

#serverpod #deploy #cli #fly #railway

License

MIT (license)

Dependencies

args, io, path, yaml

More

Packages that depend on podfly