GitHub
GitHub is our system of record for code and for how that code reaches staging, UAT, and production. We do not treat deploys as a side channel—sacred branches drive GitHub Actions to Fly.
Why we use GitHub Actions
Section titled “Why we use GitHub Actions”We deploy APIs through GitHub Actions so every release is traceable: which commit, which workflow run, which person merged, and which environment received it.
That matters for engineering judgment and for compliance (SOC 2 and audits). Deploying from a laptop, or flipping “auto-deploy” on in another tool without the same history, makes it much harder to answer “what changed in production?”
GitHub is the deployment process to Fly. Fly runs the containers; GitHub decides when and from which branch they update.
What we do not do
- Do not turn on Fly’s own auto-deploy on the app unless platform / tech lead has reviewed it with you.
- Do not run
flyctl deployto the production org from a developer machine—that bypasses the controls we rely on.
Sacred branches and naming
Section titled “Sacred branches and naming”We protect a small set of sacred branches. Each one maps to exactly one Fly config file and one workflow—the branch name is the file name. No special “production” filenames.
| Sacred branch | Fly config | Deploy workflow | Fly org tier |
|---|---|---|---|
staging | fly.staging.toml | fly-deploy-staging.yml | Staging |
uat | fly.uat.toml | fly-deploy-uat.yml | UAT |
main | fly.main.toml | fly-deploy-main.yml | Production |
main goes to the production Fly org, but the config file stays fly.main.toml—never fly.production.toml or fly.prod.toml.
Never use: fly.production.toml, fly.prod.toml, deploy-production.yml, or other one-off names that break the pattern.
Some repos only have production today (fly.main.toml only). Do not invent fake staging/UAT files until those tiers exist. Full branch rules: Branches.
Staging
Push to staging → staging workflow → staging Fly org
UAT
Push to uat → UAT workflow → UAT Fly org
Production
Push to main → main workflow → production Fly org
Each workflow runs on push to that branch only and deploys with:
flyctl deploy --config fly.<tier>.toml
Use GitHub environments and the right FLY_API_TOKEN per tier so a staging workflow cannot accidentally use a production token.
One Dockerfile per repo
Section titled “One Dockerfile per repo”Every API repo has one shared Dockerfile. CI builds that image; Fly runs it. What changes between staging, UAT, and production is fly.staging.toml, fly.uat.toml, or fly.main.toml—not a separate Dockerfile per environment.
GitHub Actions secrets by component
Add these under Settings → Secrets and variables → Actions in the repo.
Use GitHub environments (staging, uat, production) when a secret must only apply to one tier’s workflow.
Fly.io
Deploy token for the Fly org that matches the sacred branch workflow.
FLY_API_TOKEN— environment secret per tier
Sentry.io
Build-time source maps and release metadata in the deploy pipeline.
SENTRY_AUTH_TOKENSENTRY_ORGSENTRY_PROJECT
Not in GitHub — runtime on Fly
These stay in fly secrets set on each tier’s app, not in Actions secrets:
Fly checklist → Fly environment variables.
MONGODB_URICLOUDFLARE_TUNNEL_TOKENSENTRY_DSN,SENTRY_ENVIRONMENTAUTH0_*— see Auth0
How this fits together
Section titled “How this fits together”Merge to sacred branch → GitHub Action runs → flyctl deploy → Fly app updates- Fly.io — where containers run
- Cloudflare — public hostnames and tunnel routing (set up separately from the workflow)
- Sentry — required observability before production is “fully live”
- Fly checklist — tier promotion checklists