Orchestrating Fly.io Deployments via GitHub Actions

The integration of Fly.io with GitHub Actions transforms the software delivery lifecycle from a manual, error-prone process into a streamlined, automated pipeline. By leveraging the synergy between a Git-based version control system and a global platform for running applications, developers can implement Continuous Deployment (CD) patterns that ensure code moves from a commit to a production-ready state with minimal friction. This architecture relies on the orchestration of GitHub's virtual runners, the Fly.io GraphQL API, and a specialized set of configuration files that define the application's infrastructure as code. Whether the objective is a straightforward production push, the creation of ephemeral review environments for pull requests, or the manual pushing of images to a private registry, the ecosystem provides the necessary primitives to maintain high velocity without sacrificing stability.

Continuous Deployment Workflow Architecture

The primary mechanism for automating deployments to Fly.io involves the use of GitHub Actions, which triggers a sequence of events upon specific Git hooks, such as a push to the main branch. This process is governed by a combination of the fly.toml configuration file and a GitHub workflow YAML file, typically named fly.yml.

The technical foundation of this workflow requires a series of preparatory steps to synchronize the local development environment with the cloud infrastructure. The process begins with the initiation of the application via the fly launch command. When executing fly launch --no-deploy, the developer creates the necessary application entity on the Fly.io platform and generates a fly.toml file without immediately deploying the code. This allows for the precise tweaking of settings, such as the geographic region where the application will reside, ensuring that the infrastructure is optimized for the target user base before the first automated deployment occurs.

The administrative layer of this integration is the security handshake. GitHub Actions requires an authenticated bridge to interact with the Fly.io API. This is achieved by generating a deploy token using the command fly tokens create deploy -x 999999h. This token acts as a long-lived credential that grants the GitHub runner the authority to manage the specific application. For the security of the pipeline, this token must never be committed to the version control system in plain text. Instead, it is stored within the GitHub repository's "Secrets and variables" under the "Actions" section as a secret named FLY_API_TOKEN.

The real-world impact of this setup is the elimination of "deployment dread." By automating the build and deploy cycle, developers move away from manual CLI commands and toward a declarative model. The contextual link here is the fly.toml file; while it is often excluded via .gitignore in standard examples to prevent environment leakage, it must be committed to the repository for GitHub Actions to function. The action uses this file as the blueprint for the deployment, mapping the software's requirements to the Fly.io infrastructure.

The actual execution within the GitHub Action involves a specific sequence of steps:

  • The actions/checkout@v4 action is invoked to clone the repository into the virtual machine's workspace.
  • The superfly/flyctl-actions/setup-flyctl@master action is utilized to install the flyctl command-line tool on the runner.
  • A shell command flyctl deploy --remote-only is executed, which instructs Fly.io to build the image on their remote builders rather than on the GitHub runner, optimizing for speed and reducing resource constraints.
  • The FLY_API_TOKEN is passed as an environment variable, enabling the flyctl wrapper to authorize access to the Fly.io GraphQL API.

Ephemeral Review App Environments

Review apps represent a sophisticated implementation of the "preview" pattern, where every pull request (PR) triggers the creation of a temporary, isolated instance of the application. This allows stakeholders to test features and bug fixes in a live environment before they are merged into the main branch.

The technical implementation of review apps differs from standard CD in its authentication and trigger mechanism. While a standard deploy token works for a single app, review apps often require organizational-level permissions because they dynamically create new apps. This is achieved by using the command fly tokens org <ORG NAME>, which generates a token valid for all applications within a specific Fly.io organization. This token is likewise stored as FLY_API_TOKEN in the GitHub repository secrets.

The workflow for review apps is typically defined in .github/workflows/fly-review.yml. When a PR event is triggered, the superfly/fly-pr-review-apps action is invoked. By default, these apps are assigned a predictable URL following the pattern https://pr-<PR number>-<repository owner>-<repository name>.fly.dev.

The impact of this system is a significant reduction in the "merge-and-pray" cycle. Developers can verify that their changes work in a real-world cloud environment, including networking and resource constraints, before the code ever touches the production branch. This is particularly critical for complex features that may interact unexpectedly with the platform's infrastructure.

To further refine the behavior of review apps, the superfly/fly-pr-review-apps action provides several configuration inputs:

  • Custom Naming: The name input can be used to specify a unique identifier, such as my-app-name-pr-${{ github.event.number }}.
  • Configuration Overrides: Since review apps typically do not require the same resource specifications (CPU, RAM) as production apps, a separate configuration file can be used. By passing config: fly.review.toml in the with block, the action will ignore the production fly.toml and apply the settings defined in the review-specific file.

The contextual integration of these review apps can also extend to auxiliary resources. While the basic workflow spins up a standalone app, the system can be customized to deploy associated data stores, such as Redis or Memcached, ensuring that the preview environment is a faithful representation of the production stack.

Advanced Image Management and Registry Interaction

Beyond the standard fly deploy flow, there are scenarios where developers need direct control over the container image lifecycle. This involves interacting with the Fly Registry to push images manually, which allows for more granular control over versioning and the ability to notify other services when a new image is available.

The technical process for pushing to the Fly Registry involves the use of Docker within the GitHub Action runner. Because the superfly/flyctl-actions/setup-flyctl action focuses on the flyctl tool, the developer must manually bridge the authentication between flyctl and the Docker daemon. This is achieved through the command flyctl auth docker, which configures the local Docker environment to use the Fly.io registry credentials.

A detailed implementation for a registry-push workflow follows this logic:

  • The workflow is triggered by a push to the master branch.
  • A build number is generated using the Git revision count via export BUILD_NUMBER=$(git rev-list --count HEAD).
  • The flyctl tool is set up using the superfly/flyctl-actions/setup-flyctl@master action.
  • The Docker authentication is established using flyctl auth docker.
  • The image is pushed to the registry using the command docker push registry.fly.io/<app name>:master-${BUILD_NUMBER}.

The real-world consequence of this approach is the ability to implement "blue-green" or "canary" deployment patterns more effectively. By pushing a tagged image to the registry first, the developer creates a permanent artifact that can be promoted to different environments without needing to rebuild the code, ensuring that the exact same binary is tested in staging and deployed in production.

Technical Specifications and Configuration Summary

The following tables provide a structured overview of the components and commands required for various Fly.io GitHub Action implementations.

Authentication and Token Management

Token Type Command to Generate Scope Primary Use Case
Deploy Token fly tokens create deploy -x 999999h Specific Application Production CD Pipelines
Org Token fly tokens org <ORG NAME> Entire Organization Review Apps / Dynamic App Creation

Workflow Component Comparison

Feature Standard CD (fly.yml) Review Apps (fly-review.yml) Registry Push
Trigger Push to Main/Master Pull Request (PR) Push to Master
Required Secret FLY_API_TOKEN FLY_API_TOKEN FLY_API_TOKEN
Primary Action superfly/flyctl-actions superfly/fly-pr-review-apps docker push
Config File fly.toml fly.review.toml (optional) N/A (Manual)
Deployment Target Production App Ephemeral PR App Fly Registry

Essential Command Reference

Command Purpose Context
fly launch --no-deploy Initializes app and fly.toml Local Setup
flyctl deploy --remote-only Deploys app using remote builders GitHub Action
flyctl auth docker Configures Docker for Fly Registry Registry Workflow
git rev-list --count HEAD Generates a unique build number Registry Workflow

Critical Implementation Details and Troubleshooting

The transition from a local environment to a GitHub Action involves several technical nuances that can lead to deployment failure if overlooked.

The most prominent issue is the handling of the fly.toml file. In many developer guides, fly.toml is listed in .gitignore to prevent the accidental commit of environment-specific configurations. However, for a GitHub Action to function, the fly.toml must be present in the repository. This is because the flyctl deploy command looks for this file to determine the application's configuration, such as the internal port and the HTTP profile. Without this file, the action will fail to identify the target application settings.

Another critical consideration is the use of the concurrency key within the GitHub workflow. Because deployments to Fly.io involve updating a global state, having multiple actions running simultaneously for the same application can lead to race conditions or deployment conflicts. By defining a custom group name in the concurrency key, developers can ensure that only one deployment action runs at a time for a specific group, providing a linear and predictable deployment history.

Furthermore, the distinction between flyctl and the superfly/flyctl-actions wrapper is important. The wrapper simplifies the installation of the CLI tool on the Ubuntu-latest runner, but the underlying commands remain the same. If a developer needs to perform operations not covered by the wrapper, they can still execute raw flyctl commands as long as the setup action has been completed and the FLY_API_TOKEN is present in the environment.

Conclusion

The integration of Fly.io and GitHub Actions represents a sophisticated fusion of infrastructure-as-code and continuous delivery. By utilizing fly.toml for environment definition and FLY_API_TOKEN for secure authentication, developers can transition from manual deployments to a fully automated pipeline. The ability to deploy production environments via standard CD, spin up ephemeral review apps for every pull request, and manage container images directly through the Fly Registry provides a comprehensive toolkit for modern software engineering.

The strategic advantage of this setup lies in its scalability. The use of remote builders removes the burden of image construction from the GitHub runner, while the organizational tokens enable the dynamic creation of infrastructure. This architecture not only accelerates the development cycle but also increases the reliability of the software by ensuring that every change is vetted in an environment that mirrors production. Ultimately, the synergy between these tools allows for a high-frequency deployment cadence where the focus remains on feature delivery rather than the mechanics of the deployment process.

Sources

  1. Continuous Deployment with Fly.io and GitHub Actions
  2. Git Branch Preview Environments on Github
  3. How to push to fly registry via github action

Related Posts