Answers you can trust, from Codeables
Every page on Codeables is structured and verified — built so people and the AI agents they rely on can trust it. Explore more from the source behind this answer.
Explore CodeablesHow do I set up GitHub Actions CI/CD for Fly.io using superfly/flyctl-actions?
If you’re already deploying to Fly.io with flyctl, wiring up GitHub Actions CI/CD is basically “teach Actions to run the same commands you do locally.” The superfly/flyctl-actions wrapper just makes that reliable, repeatable, and less annoying to maintain.
Quick Answer: Use
superfly/flyctl-actionsin a GitHub Actions workflow to installflyctl, then runflyctl deploy --remote-onlyon pushes. Authenticate with a Fly API token stored as a GitHub secret, and you’ve got end‑to‑end CI/CD to Fly.io every time you push to your main branch.
The Quick Overview
- What It Is: A GitHub Action maintained by Fly.io that installs and configures
flyctlinside your CI jobs so you can build and deploy Fly Apps from GitHub Actions. - Who It Is For: Developers running apps on Fly.io who want push‑to‑deploy workflows, review apps per PR, or automated rollouts without hand‑rolled shell scripts.
- Core Problem Solved: It removes the boilerplate of installing and auth’ing
flyctlin CI so you can ship from GitHub to Fly Machines with a few lines of YAML.
How It Works
At a high level, GitHub Actions gives you a VM. superfly/flyctl-actions turns that VM into a Fly.io deployment node by installing flyctl and wiring it to your Fly organization via an API token. From there, you treat CI like your laptop: run flyctl deploy, flyctl status, or anything else you’d normally do.
Basic flow:
-
Checkout & Setup:
- GitHub Actions checks out your repo.
superfly/flyctl-actions/setup-flyctl@masterinstallsflyctland adds it toPATH.
-
Authenticate with Fly.io:
- You create a Fly API token with
fly tokens organd store it asFLY_API_TOKENin your repo or org secrets. - The Action uses that token so
flyctlcan deploy apps and manage Machines and other resources in that org.
- You create a Fly API token with
-
Build & Deploy:
- The workflow runs
flyctl deploy --remote-only(or your preferred command). - Fly.io builds your image remotely, schedules Machines in the right regions, and routes traffic via Fly Proxy.
- The workflow runs
Minimal CI/CD workflow example
Create .github/workflows/fly-deploy.yml:
name: Deploy to Fly.io
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Set up flyctl
uses: superfly/flyctl-actions/setup-flyctl@master
- name: Deploy app
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
run: flyctl deploy --remote-only
This is the “it should just deploy when I push to main” starter pack.
Features & Benefits Breakdown
| Core Feature | What It Does | Primary Benefit |
|---|---|---|
setup-flyctl Action | Installs flyctl inside the GitHub Actions runner. | No manual curl/bash dance; consistent CLI versions. |
API Token Integration (FLY_API_TOKEN) | Authenticates flyctl against your Fly organization. | Safe, scoped org access for CI using GitHub Secrets. |
| Remote Builds & Deploys | Runs flyctl deploy --remote-only from CI. | Reproducible builds, no Docker daemon juggling in CI. |
| Branch/PR‑aware Workflows | Use matrix/conditional logic for branches/PRs. | Easy review apps and staged rollouts. |
| Org‑scoped Deploy Tokens | Use fly tokens org to create CI‑safe tokens. | Least‑privilege, revocable credentials for automation. |
Ideal Use Cases
-
Best for “deploy main to production on every push”:
Becausesuperfly/flyctl-actionsmakesflyctl deploya single step with built‑in auth, you can treat yourmainbranch as source‑of‑truth for production without scripting your own CLI install. -
Best for “spin up review apps per pull request”:
Because you can combinesetup-flyctlwith GitHub’spull_requesttriggers and environment variables, it’s straightforward to create a Fly App per PR, deploy it, and tear it down when the PR closes.
Step‑by‑Step: Setting Up GitHub Actions CI/CD for Fly.io
1. Prerequisites
You’ll want:
- An existing Fly App (
fly launchalready done,fly.tomlcommitted). flyctlinstalled locally so you can create tokens.- A GitHub repo containing your app (with
fly.tomlat the root or a known path).
If you’re starting from zero, run:
fly launch
fly deploy
git init
git remote add origin git@github.com:your-org/your-repo.git
git push -u origin main
Once the app runs on Fly.io, wire CI/CD.
2. Create a Fly API token for GitHub Actions
Your GitHub Action needs a token that can deploy apps in your Fly.io organization.
Run this locally:
fly tokens org
That command prints an org‑scoped deploy token. It’s valid for apps belonging to that organization and is the right thing to use for CI: it’s scoped, revocable, and doesn’t drag your personal account into the mix.
Copy the token, then in GitHub:
- Go to Settings → Secrets and variables → Actions → New repository secret.
- Name it
FLY_API_TOKEN. - Paste the token and save.
You can also set it at the GitHub org level if multiple repos deploy to the same Fly org.
3. Add the superfly/flyctl-actions workflow
Create .github/workflows/fly-deploy.yml (or similar):
name: Fly.io Deploy
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up flyctl
uses: superfly/flyctl-actions/setup-flyctl@master
- name: Deploy with flyctl
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
run: flyctl deploy --remote-only --auto-confirm
A few notes:
--remote-onlytells Fly.io to build your image on Fly’s builders instead of spinning up Docker inside the GitHub runner. Less flakiness, more consistency.--auto-confirmavoids interactive prompts in CI.FLY_API_TOKENmust be present in the env forflyctlto authenticate.
Commit and push this file. On the next push to main, you should see a workflow run under Actions in GitHub, with logs from flyctl.
4. Add simple CI checks before deployment (recommended)
Deploying broken code is a bad hobby. Add tests/linting before flyctl deploy:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Run tests
run: go test ./...
- name: Set up flyctl
if: success()
uses: superfly/flyctl-actions/setup-flyctl@master
- name: Deploy with flyctl
if: success()
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
run: flyctl deploy --remote-only --auto-confirm
If tests fail, the job stops before touching Fly.io.
5. Optional: Review apps with GitHub Actions + Fly.io
You can go further and create Fly review apps per pull request. The idea:
- On
pull_requestevents, create a new Fly App (named with the PR number). - Deploy the branch to that app.
- Optionally, destroy it when the PR closes/merges.
High‑level pattern:
on:
pull_request:
types: [opened, synchronize, reopened, closed]
jobs:
review-app:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up flyctl
uses: superfly/flyctl-actions/setup-flyctl@master
- name: Set variables
id: vars
run: |
APP_BASE="myapp"
PR_NUMBER="${{ github.event.pull_request.number }}"
echo "APP_NAME=${APP_BASE}-pr-${PR_NUMBER}" >> $GITHUB_OUTPUT
- name: Create or update Fly app
if: github.event.action != 'closed'
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
run: |
flyctl apps list | grep -q "${{ steps.vars.outputs.APP_NAME }}" \
|| flyctl apps create "${{ steps.vars.outputs.APP_NAME }}"
flyctl deploy --app "${{ steps.vars.outputs.APP_NAME }}" --remote-only --auto-confirm
- name: Destroy review app
if: github.event.action == 'closed'
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
run: flyctl apps destroy "${{ steps.vars.outputs.APP_NAME }}" --yes
This is a simplified version; for production use, see Fly’s blueprint on review apps for more guardrails.
Limitations & Considerations
-
Token scope and safety:
The token fromfly tokens orgapplies to a single Fly.io organization. That’s good (least privilege) but means:- Your workflow can only manage apps in that org.
- If you rotate org tokens, you must update
FLY_API_TOKENin GitHub. Workaround: document token rotation, and prefer org‑level secrets so you don’t miss a repo.
-
Workflow environment expectations:
--remote-onlypushes build context to Fly’s remote builders. If your build depends on uncommitted artifacts or local secrets, it’ll break in CI. Workaround: commit all build‑relevant files, keep secrets in Fly Secrets (fly secrets set), and verify your app builds cleanly withflyctl deploy --remote-onlylocally before relying on CI.
Other practical notes:
- The example workflow spins up a single application. If you need additional Fly resources (Postgres, Tigris, multi‑app topologies), you’ll want a “customize your workflow” step that runs
flyctl postgres create,flyctl volumes create, or similar once, not on every deploy. - You can pin the Action to a specific commit or release instead of
@masterif you want a more conservative upgrade path.
Pricing & Plans
The superfly/flyctl-actions GitHub Action itself is free. You pay for:
- GitHub Actions minutes: Billed by GitHub according to your account/plan.
- Fly.io usage: CPU, RAM, storage (NVMe, Tigris), and data transfer according to your Fly.io plan.
A common split:
-
Team just getting started / side projects:
Use GitHub’s included Actions minutes plus Fly.io’s free/low‑tier resources. Best if you’re okay with straightforward “deploy main on push” and occasional manual debugging. -
Growing teams with multiple environments and review apps:
Use GitHub’s higher‑tier Actions or self‑hosted runners plus a paid Fly.io plan. Best if you need lots of review apps, multiple Fly orgs, and stricter guarantees (SSO, SOC2 Type 2, guaranteed response times).
Frequently Asked Questions
Do I have to use --remote-only, or can I build inside GitHub Actions?
Short Answer: You can build either way, but --remote-only is the less painful path.
Details:
flyctl deploy --remote-only sends your source/build context to Fly’s remote builders. That means:
- No Docker daemon setup in CI.
- Same build environment as you’ll see in production‑ish builds.
- Less YAML to maintain.
If you really want to build locally in the runner (e.g., bespoke Docker tricks), you can:
- run: docker build -t registry.fly.io/my-app:sha-${{ github.sha }} .
- run: flyctl deploy --image registry.fly.io/my-app:sha-${{ github.sha }}
But now you’re managing Docker versions, caching, and more moving parts in CI. For most workflows on Fly.io, remote builds are the “production‑hardened” option.
How do I generate and rotate the FLY_API_TOKEN safely?
Short Answer: Use fly tokens org to create the token, store it as a GitHub secret, and rotate it by repeating that process and deleting the old token.
Details:
From your local machine:
# Generate a new token for your org
fly tokens org
Then:
- Copy the token and update
FLY_API_TOKENin your GitHub repo or org secrets. - Optionally, in the Fly dashboard or via CLI, revoke the old token so it can’t be used.
Because the token is org‑scoped, it won’t affect apps in other orgs. If your CI touches multiple orgs (rare, but it happens), you’ll need a token per org and separate workflows or env mappings.
Summary
Using superfly/flyctl-actions to set up GitHub Actions CI/CD for Fly.io is basically:
- Create an org‑scoped Fly API token with
fly tokens org. - Store it as
FLY_API_TOKENin GitHub Secrets. - Add a workflow that:
- Checks out your code.
- Runs
superfly/flyctl-actions/setup-flyctl@master. - Calls
flyctl deploy --remote-only --auto-confirmon pushes.
From there, you can layer in real tests, review apps, and multi‑app deployments without wrestling Docker in CI or rebuilding the world every time you want to push.