Deploying to Fly.io using Dagger and Github
Learn how to streamline your deployment process using Fly.io, GitHub Actions, and Dagger. This guide walks you through setting up automatic deployments a simple todo app, from basic Fly.io CLI usage to creating efficient, reusable deployment pipelines with Dagger.
Starting with Fly.io
fly deploy
. However, we could do better than that and do automatic deployments on every push to the main branch.Setup Github action for Fly.io
bun create elysia app
. You can follow the instructions here.fly
CLI inside your app folder:fly tokens create deploy -x 999999h
- Click Settings.
- From the Secrets and variables dropdown click Actions.
- Click New repository secret.
- Paste the output of the command above in the Secret field.
- Name the secret
FLY_API_TOKEN
. - Click Add secret.
.github/workflows
folder and name it deploy.yaml
:name: Fly Deploy
on:
push:
branches:
- main
jobs:
deploy:
name: Deploy app
runs-on: ubuntu-latest
concurrency: deploy-group
steps:
- uses: actions/checkout@v4
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only
working-directory: ./app
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
main
branch, and it checks out the code from the repository using the checkout
action. Once the code is checked out, it runs the flyctl deploy
command using the superfly/flyctl-actions/setup-flyctl
action.Note
Note that if you're app is in a subfolder, don't forget to set theworking-directory
to the correct path. In my case, I created the app in the./app
folder so I set theworking-directory
field to that value.
Daggerize your scripts
dagger init
), write the function and run it without ever downloading any dependencies or setting up the environment!call
command in the Dager CLI (e.g. dagger call [function_name]
). I am not going to write a walkthrough, because the quickstart in Dagger's docs is great, so check that out if you're interested.Writing a Dagger function for Fly.io
flyctl
CLI. The full source is here, but sinces there's not much to it, I'll paste the functions here as well:// Deploy deploys an app from the src folder to Fly.io
func (m *Flyio) Deploy(ctx context.Context,
// +required
src *Directory,
// +required
token *Secret) (string, error) {
return m.FlyContainer(ctx, token).
WithMountedDirectory("/src", src).
WithWorkdir("/src").
WithExec([]string{"/root/.fly/bin/flyctl", "deploy"}).
Stdout(ctx)
}
// FlyContainer creates a container with the flyctl CLI installed
func (m *Flyio) FlyContainer(ctx context.Context, token *Secret) *Container {
return dag.Container().
From("alpine:3.20.0").
WithExec([]string{"apk", "add", "curl"}).
WithExec([]string{"curl", "-LO", "https://fly.io/install.sh"}).
WithExec([]string{"sh", "install.sh"}).
WithSecretVariable("FLY_API_TOKEN", token)
}
FlyContainer
function creates (and returns) a new Docker container based on alpine:3.20.0
, installs curl
, downlads the script and installs the Fly.io CLI and then mounts an environment variable FLY_API_TOKEN
from the secret passed to the function (Secret is a concept in Dagger that allows you to input things like API keys, passwords, into Dagger functions without exposing them in the logs, for example).Deploy
function uses the FlyContainer
function to create a container, mounts the source directory that was pass-in by the user, and then runs the flyctl deploy
command in that mounted folder and pipes the output to standard out (stdout).$ dagger functions
Name Description
deploy Deploy deploys an app from the src folder to Fly.io
fly-container FlyContainer creates a container with the flyctl CLI installed
deploy
function, you can do something like this:dagger call deploy --src ./projects/htmx-todo/app --token=env:FLYIO_TOKEN
--src
flag is the path to the source code of the app you want to deploy, and the --token
flag is the deployment token. It's prefixed with env
which is telling Dagger to read the value from the environment variable. Other options here are file
, to read the secret value from a file, or cmd
to run a command and use the output as the value.dagger call
or dagger functions
you can point to the location of the Dagger module (e.g. the Github repo for example where I published the module) and it will work the same way as if you had the module locally:$ dagger functions -m github.com/peterj/dagger-modules/flyio@46138d1028f721d7a0cdc03794c64aa063584f46
```console
Name Description
deploy Deploy deploys an app from the src folder to Fly.io
fly-container FlyContainer creates a container with the flyctl CLI installed
Putting it all together
name: Fly Deploy with Dagger
on:
push:
branches:
- main
jobs:
deploy:
name: Deploy app
runs-on: ubuntu-latest
concurrency: deploy-group
steps:
- uses: actions/checkout@v4
- uses: dagger/dagger-for-github@v5
with:
verb: call
module: github.com/peterj/dagger-modules/flyio@46138d1028f721d7a0cdc03794c64aa063584f46
args: deploy --src $PWD --token=env:${{ secrets.FLY_API_TOKEN }}