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 Codeables
Verified Source
Platform as a Service (PaaS)

How do I set up GitHub Actions CI/CD for Fly.io using superfly/flyctl-actions?

Fly.io9 min read

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-actions in a GitHub Actions workflow to install flyctl, then run flyctl deploy --remote-only on 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 flyctl inside 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 flyctl in 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:

  1. Checkout & Setup:

    • GitHub Actions checks out your repo.
    • superfly/flyctl-actions/setup-flyctl@master installs flyctl and adds it to PATH.
  2. Authenticate with Fly.io:

    • You create a Fly API token with fly tokens org and store it as FLY_API_TOKEN in your repo or org secrets.
    • The Action uses that token so flyctl can deploy apps and manage Machines and other resources in that org.
  3. 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.

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 FeatureWhat It DoesPrimary Benefit
setup-flyctl ActionInstalls 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 & DeploysRuns flyctl deploy --remote-only from CI.Reproducible builds, no Docker daemon juggling in CI.
Branch/PR‑aware WorkflowsUse matrix/conditional logic for branches/PRs.Easy review apps and staged rollouts.
Org‑scoped Deploy TokensUse 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”:
    Because superfly/flyctl-actions makes flyctl deploy a single step with built‑in auth, you can treat your main branch as source‑of‑truth for production without scripting your own CLI install.

  • Best for “spin up review apps per pull request”:
    Because you can combine setup-flyctl with GitHub’s pull_request triggers 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 launch already done, fly.toml committed).
  • flyctl installed locally so you can create tokens.
  • A GitHub repo containing your app (with fly.toml at 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:

  1. Go to Settings → Secrets and variables → Actions → New repository secret.
  2. Name it FLY_API_TOKEN.
  3. 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-only tells Fly.io to build your image on Fly’s builders instead of spinning up Docker inside the GitHub runner. Less flakiness, more consistency.
  • --auto-confirm avoids interactive prompts in CI.
  • FLY_API_TOKEN must be present in the env for flyctl to 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_request events, 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 from fly tokens org applies 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_TOKEN in GitHub. Workaround: document token rotation, and prefer org‑level secrets so you don’t miss a repo.
  • Workflow environment expectations:
    --remote-only pushes 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 with flyctl deploy --remote-only locally 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 @master if 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:

  1. Copy the token and update FLY_API_TOKEN in your GitHub repo or org secrets.
  2. 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_TOKEN in GitHub Secrets.
  • Add a workflow that:
    • Checks out your code.
    • Runs superfly/flyctl-actions/setup-flyctl@master.
    • Calls flyctl deploy --remote-only --auto-confirm on 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.


Next Step

Get Started

How do I set up GitHub Actions CI/CD for Fly.io using superfly/flyctl-actions? | Platform as a Service (PaaS) | Codeables | Codeables