Deploying Phoenix/Elixir App with Gitlab CI/CD on Fly.io

Deploying Phoenix/Elixir App with Gitlab CI/CD on Fly.io

I recently tried deploying my simple Phoenix/Elixir application on Fly.io, but boy did I hit some roadblocks along the way. Some of the resources I was using were outdated but gladly, I was able to deploy my app successfully! I set up GitLab CI/CD to make my life a little easier.

I decided to write down everything I went through in case anyone else is struggling with the same stuff.

Prerequisite

  • Existing Phoenix/Elixir Application you want to deploy or create a new one here

  • Fly.io Account — you can sign up here: You need to add credit card payment information, it is required for us to deploy the application.

Getting Ready to Fly

We need to install flyctl — flyctl is a command-line utility that lets you work with Fly.io. I have homebrew installed so I installed flyctl using this command:

$ brew install flyctl

If you don’t have homebrew you can check other options here.

After installing flyctl, we need to sign in with flyctl. Just run:

$ fly auth login

A browser will open up with the Fly.io sign-in screen, enter your username and password to sign in, once done, you can close the browser tab and return to your command line, ready to use Fly.io.

Launching our App

Now that we have everything fly related on set, let us now launch our app:

$ cd <Your project path>
$ fly launch

After running the fly launch it will ask a few questions to set everything up. Let’s run through it now:

Here you can enter your app name. You can only use numbers, lowercase letters, and dashes.

? App Name (leave blank to use an auto-generated name):

If you did not add another organization to your account, the personal organization will be selected automatically.

? Select organization: Personal (personal)

Next, you'll be prompted to select a region to deploy in. The closest region to you is selected by default. You can use this or change it to another region.

? Select region: ord (Chicago, Illinois (US))

Then, it will ask if you need a Postgresql database. You can reply no for now but what I did is set it up since I will be needing it in the future.

? Would you like to set up a Postgresql database now? (y/N)

Lastly, it will ask you if you would like to deploy now.

? Would you like to deploy now? Yes
==> Building image

Here comes the first issue — when the app is building, it returned an error regarding “alpinejs” package installed in my app. So I need to do some changes to the Dockerfile that was generated automatically.

A fly.toml file was also generated automatically but I didn’t have to do anything to it.

I need to install yarn and run yarn install inside my assets folder so that all dependencies/packages will be installed before deployment. Here’s what I change:

After editing the Dockerfile, we need to deploy our app again, just run:

fly deploy

And our app is now live. When you look at the Fly Dashboard — New apps are created for your project and the database.

Some other fly commands:

To check your app status run:

$ fly status
App
  Name     = just-a-con
  Owner    = personal
  Version  = 1
  Status   = running
  Hostname = just-a-con.fly.dev
  Platform = nomad

Deployment Status
  ID          = dd132cb5-3570-7a09-96f50e1b9ad5
  Version     = v1
  Status      = successful
  Description = Deployment completed successfully
  Instances   = 1 desired, 1 placed, 1 healthy, 0 unhealthy

Instances
ID       PROCESS VERSION REGION DESIRED STATUS  HEALTH CHECKS      RESTARTS CREATED
34fc8 app     1       sin    run     running 1 total, 1 passing 0        1h38m ago

If you want to quickly visit your app, simply run:

$ fly open

This will open a browser on the http version of the site. That will automatically be upgraded to https secured connection (when using the fly.dev domain) to connect to it securely. Add /name to fly open and it'll be appended to the app's path.

To check logs, just run:

$ fly logs

for other commands, you can run:

$ fly --help

Setting up Gitlab CI/CD

To streamline the deployment process, I want to create a separate branch on Gitlab that will facilitate the automatic deployment of any merged changes.

Then, navigate to Settings > Repository > Protected Branches, we need to set that branch as protected.

Please don’t forget this ☝️, I was blocked for hours trying to figure out why my fly access token is not working only to figure out that I only made the variable FLY_ACCESS_TOKENonly available to protected branches. 🤦🏻

Adding Fly access token

Generate a fly access token by running this command:

$ fly auth token

> mgA6NOTLKF9k1pqfGdUzGaupaREAL-AvGmZXn5LhwRrvTOKENQaTc

Then copy the token and go to your Gitlab account. Under Setting > CI/CD > Variables, add a new variable called FLY_ACCESS_TOKEN with the generated access token as the value.

Make sure to select the production branch you created as the Environment scope.

Also, turn on the Protected and Masked switches so that it is not leaked through the logs. then save the new variable.

And for the last step, go back to your Phoenix/Elixir app and create a new file .gitlab-ci.yml , mine looks like this:

default:
  image: elixir:1.12
  before_script:
    - apt-get update -qq && apt-get install -y curl
    - curl -L https://fly.io/install.sh | sh

deploy:
  except:
    - tags
  only:
    - production 
  environment:
    name: "production"
  script:
    - /root/.fly/bin/flyctl deploy

Let's walk through the .gitlab-ci.yml I used to deploy from GitLab to Fly

default:
  image: elixir:1.12

It selects an image to use for the virtual machine — or “Runner” to use GitLab terminology — that will be used to run the CI/CD process

before_script:
    - apt-get update -qq && apt-get install -y curl
    - curl -L https://fly.io/install.sh | sh

This code block installs curl and then uses curl to download flyctl.

only:
    - production

This sets the deployment to only execute under the production branch.

script:
    - /root/.fly/bin/flyctl deploy

And lastly, to deploy the application, it should run flyctl deploy. Notice the /root/.fly/bin/ added to the flyctl deploy command, it is because the GitLab runner is returning this error. Since it's not installed globally, we need to execute it to where it was installed.

Found a solution here.

Finally, commit your changes and push them to the production branch. Then navigate to CI/CD to check if there's a pipeline runner for the deployment.

Update:

Adding Test Stage on GitLab CI/CD

I also have added a test stage in my GitLab runners following this tutorial here. I don’t think I have much to discuss here since the tutorial is pretty straightforward and works.

here’s my updated .gitlab-ci.yml for that:

image: elixir:latest

before_script:
  - mix local.hex --force
  - mix local.rebar --force
  - mix deps.get
  - mix ecto.create
  - mix ecto.migrate

stages:
  - test
  - deploy

test:
  stage: test
  except:
    - tags
  only:
    - branches
  services:
    - postgres:latest
  variables:
    POSTGRES_DB: justacon_ci_test
    POSTGRES_HOST: 'postgres'
    POSTGRES_USER: 'postgres'
    POSTGRES_PASSWORD: 'postgres'
    MIX_ENV: "test"
  script:
    - mix test

production:
  stage: deploy
  except:
    - tags
  only:
    - production 
  environment:
    name: "production"
  before_script:
    - apt-get update -qq && apt-get install -y curl
    - curl -L https://fly.io/install.sh | sh
  script:
    - /root/.fly/bin/flyctl deploy

Now I have 2 pipelines running:

  • test — runs on all branches

  • deploy — runs on the production branch

Hope this works for you, let me know if it does or if you have any questions. Happy Coding!!!