Deploy Phoenix to Gigalixir using Elixir Releases

Learn how to deploy your Elixir Phoenix app to Gigalixir using releases. Gigalixir lets you take advantage of many Elixir features not available on other cloud hosting platforms. For this reason, Gigalixir was our Editor’s Choice from our Best Cloud Hosting Providers for Elixir Phoenix article. In this guide, you’ll learn how to prepare your app for releases, create your Gigalixir app, deploy, and automate migrations.

Overview

Here are the topics we’ll cover while deploying the project.

  1. Why Gigalixir?
  2. Create a new Phoenix app
  3. Configure for Releases
  4. Deploy to Gigalixir

Prerequisites

To follow this guide, make sure you have at least the following applications installed on your machine. In the brackets are the versions with which this guide was written.

  • Elixir (1.12.3-otp-24)
  • Erlang (OTP 24.1)
  • Phoenix (1.5.13)
  • Node.js with npm (15.14.0)

Make sure to install at least Elixir 1.9 since we are using releases. We recommend using asdf to manage Erlang and Elixir versions.

If you run into problems, the finished project is available on GitHub as StakNine HelloGigalixir.

Why Gigalixir?

Gigalixir allows you to use all the features Elixir has to offer like distributed clustering, hot upgrades, remote console, and production observer.

Phoenix can handle over two million simultaneous websocket connections on a single node and it’s great for massively real-time apps. Gigalixir doesn’t limit your connection counts or duration, so you can leverage the full power of Phoenix with websockets.

Finally, GenServer state, ETS, and cachex make it possible to create ridiculously high performance, fault-tolerant apps but daily restarts will get in your way. Distributed Phoenix channels don’t need Redis if you have your nodes clustered.

Many other cloud hosting platform limit your ability to use these features. Gigalixir is a great option to use the full capability of Elixir and Phoenix.

Create a New Phoenix App

We are going to create a simple Phoenix project called HelloGigalixir. Use the command line to update your Phoenix version and create a new Phoenix project.

mix archive.install hex phx_new 1.5.13
mix phx.new hello_gigalixir

Change into the project directory, create your database and start your app.

cd hello_gigalixir
mix ecto.create
mix phx.server

Now visit http://localhost:4000 and you should see the Welcome to Phoenix! page.

Welcome to Phoenix! homepage

Add users

Now we want to add users to the project with the phx.gen html task.

mix phx.gen.html Accounts User users name:string \
username:string:unique

Follow the directions to add the resource to the lib/docker_phx_web/router.ex file and migrate the database with mix ecto.migrate.

Now start your server with mix phx.server and visit http://localhost:4000/users on your local machine to see the index of users. Add a new user to test out the database.

Add user to database in your Phoenix app

We’ll use this project to test our database is migrated and our app is deployed properly.

Let’s make the initial commit to your git repository and then configure for releases.

git init
git add .
git commit -m 'initialize repo and add users'

Configure for Elixir Releases

Using Elixir releases to deploy on Gigalixir unlocks many of Elixir’s features, including remote observer. A release consists of your application code, all of its dependencies, plus the whole Erlang Virtual Machine and runtime.

Configure Environment Variables

We are going to update our project to use releases. First set the following.

# generate a really long secret
mix phx.gen.secret

export SECRET_KEY_BASE=REALLY_LONG_SECRET
export DATABASE_URL=postgres://postgres:postgres@localhost:5432/hello_gigalixir_dev

Then load dependencies to compile code and assets.

# Initial setup
mix deps.get --only prod
MIX_ENV=prod mix compile

# Compile assets
npm run deploy --prefix ./assets
mix phx.digest

Runtime configuration

Update the project to use runtime configuration with the steps below. With runtime configuration, you are able to store environment variables in an external configuration system in an actual production setup. We are going to use config/runtime.exs added in Elixir 1.11.

  1. Change config/prod.exs to no longer call import_config "prod.secret.exs" at the bottom.
  2. Rename config/prod.secret.exs environment file to config/runtime.exs.
  3. Change use Mix.Config inside the new config/runtime.exs file to import Config.
  4. Restrict your runtime configuration to the :prod environment
  5. Uncomment or add server: true.
# config/runtime.exs
import Config

if config_env() == :prod do
  # other configuration

  config :hello_gigalixir, HelloGigalixirWeb.Endpoint, server: true

end

Ecto migrations and custom commands

You can’t run mix ecto.migrate using releases because Mix is not available. We need to write a release commands file. Create a new file in your application, lib/hello_gigalixir/release.ex, with the following:

defmodule HelloGigalixir.Release do
  @app :hello_gigalixir

  def migrate do
    for repo <- repos() do
      {:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
    end
  end

  def rollback(repo, version) do
    {:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version))
  end

  defp repos do
    Application.load(@app)
    Application.fetch_env!(@app, :ecto_repos)
  end
end

Deploy Your Elixir Release Locally

Once you’ve completed those sections, you should be able to test your app and start building releases.

Drop the existing database and create a new database, so you can test the release command. Then run the mix command to build the release with the MIX_ENV=prod mix release build script.

mix ecto.drop
mix ecto.create
MIX_ENV=prod mix release

Migrate your database and start your application

_build/prod/rel/hello_gigalixir/bin/hello_gigalixir eval "HelloGigalixir.Release.migrate"
_build/prod/rel/hello_gigalixir/bin/hello_gigalixir start

Now you should be able to visit http://localhost:4000/users on your local machine to see the index of users. You can add a user again to test out the site is working properly.

Listing users after deploying with an Elixir release

Great! Let’s commit our changes to your git repository and then prepare for deployment.

git add .
git commit -m "update for releases"

Deploy to Gigalixir

We’re ready to deploy our app. First we will add deployment configuration. Then we’ll create and deploy our app using the Gigalixir CLI. Finally, we will try out remote console and observer.

Add buildpacks

Now that we’ve used releases to deploy to our local machine, we’re ready to deploy to our remote server. First, we need to create a .buildpack file since we are using runtime configuration file. Runtime configuration can be used with either mix or releases, so you need to tell Gigalixir how you want to start your application based on the last line in the file.

#.buildpack file
https://github.com/HashNuke/heroku-buildpack-elixir
https://github.com/gjaldon/heroku-buildpack-phoenix-static
https://github.com/gigalixir/gigalixir-buildpack-releases.git

Then let’s make sure we add buildpack config to use the same versions of Elixir, Erlang, and Node in both development and production.

echo "elixir_version=1.12.3" > elixir_buildpack.config
echo "erlang_version=24.1" >> elixir_buildpack.config
echo "node_version=15.14.0" > phoenix_static_buildpack.config

Update runtime config

Now we need to make some additional updates to the config for Gigalixir.

  • Set your pool size to 2 since we will be using a free tier database
  • Update HelloGigalixirWeb.Endpoint endpoint and repo config
 # changes to config.runtime.exs 
  config :hello_gigalixir, HelloGigalixir.Repo,
    # ssl: true,
    url: database_url,
    pool_size: String.to_integer(System.get_env("POOL_SIZE") || "2")

  config :hello_gigalixir, HelloGigalixirWeb.Endpoint,
    server: true,
    http: [port: {:system, "PORT"}],
    url: [host: System.get_env("APP_NAME") <> ".gigalixirapp.com", port: 443]

Procfile for migrations on startup

To automatically migrate our database on startups and updates using releases, we need to create a custom Procfile and overlay it into our release tarball. To do this create a file rel/overlays/Procfile with the following.

web: /app/bin/$GIGALIXIR_APP_NAME eval "HelloGigalixir.Release.migrate" && /app/bin/$GIGALIXIR_APP_NAME $GIGALIXIR_COMMAND

Phoenix v1.6 update

If you’re using Phoenix v1.6, it uses esbuild to compile your assets but Gigalixir images come with npm, so we will configure npm directly to deploy our assets. Add a assets/package.json file if you don’t have any with the following:

{
  "scripts": {
    "deploy": "cd .. && mix assets.deploy && rm -f _build/esbuild"
  }
}

Now we’re ready to commit our changes to your git repository and deploy.

git add .
git commit -m 'update for deployment'

Create app and deploy

We’ve configured your Phoenix project for releases and we’re ready to deploy to Gigalixir. If you haven’t already, install the gigalixir CLI and sign up for an Gigalixir account. We are using macOS. If your are using another OS, check out the install options for Windows and Linux.

brew tap gigalixir/brew && brew install gigalixir
gigalixir signup
gigalixir login

Create a Gigalixir app. You will get your app name in the output and have a git remote set for gigalixir.

APP_NAME=$(gigalixir create)

Verify that the app was created, by running gigalixir apps

gigalixir apps

The following command will provision a free database for you and set your DATABASE_URL environment variable appropriately.

gigalixir pg:create --free

Verify your database is up by running

gigalixir pg

Once the database is created, verify your configuration includes a DATABASE_URL with

gigalixir config

Now your are ready to push your code which will initiate the deployment of your production app. You will get the link including your app name in the output.

git push gigalixir main

Gigalixir will deploy your app.

Wait a minute or two for the app to pass health checks. Then you should notice Gigalixir running migrations on startup using the script you added to rel/overlays. You can check the status with

gigalixir ps

Now visit https://app_name.gigalixirapp.com/users and add a user to test your app running in production is deployed properly.

Successfly deployed your Phoenix app to Gigaixir using Elixir releases

Using the Gigalixir Dashboard

Gigalixir provides a dashboard for your app running in production to monitor its health and view logs.

Gigalixir Dashboard

Conclusion

Congratulations! You’ve deployed your Elixir Phoenix project to Gigalixir using releases. In future posts, we plan to cover continuous deployment, custom domains, ssh access, remote observer, and remote console.

Want To Know How To Deploy Phoenix Apps Using A single Command?

This brand-new FREE training reveals the most powerful new way to reduce your deployment time and skyrocket your productivity… and truly see your programming career explode off the charts!