Elixir v1.9 will ship with releases support and in this blog post we want to show how we have used this exciting new feature on the Hex.pm project.

Installing Elixir master

(Update: This section is no longer relevant since v1.9 is already out!)

Since Elixir v1.9 is not out yet, we need to use the development version. Locally, my preferred approach is to use the Elixir plugin for the asdf-vm version manager.

Here’s a couple of ways we may use asdf to install recent development versions:

# install latest master $ asdf install elixir master $ asdf local elixir master # or, install particular revision: $ asdf install elixir ref:b8b7e5a $ asdf local elixir ref:b8b7e5a

Per “Deployment” section of mix release documentation:

A release is built on a host, a machine which contains Erlang, Elixir, and any other dependencies needed to compile your application. A release is then deployed to a target, potentially the same machine as the host, but usually separate, and often there are many targets (either multiple instances, or the release is deployed to heterogeneous environments).

We deploy Hex.pm using Docker containers and we needed to change our Dockerfile. If you’re deploying using buildpacks (e.g. to Heroku or Gigalixir), it should be as simple as setting elixir_version=master in your elixir_buildpack.config .

Setting up releases

Elixir 1.9 ships with two new Mix tasks to work with releases:

mix release.init – generates sample files for releases

– generates sample files for releases mix release – builds the release

The sample files generated by mix release.init are optional, if they are not present in your project then the release will be built with default options.

On Hex.pm, previously we were building releases using Distillery and to work with Elixir releases we needed to make a few small tweaks. Here are the main ones:

add :releases section to mix.exs – this is an optional step but since we don’t deploy on Windows, we only need to generate executable files for UNIX-like systems

section to – this is an optional step but since we don’t deploy on Windows, we only need to generate executable files for UNIX-like systems replace rel/vm.args with rel/vm.args.eex

with replace rel/hooks/pre_configure with rel/env.sh.eex

with add config/releases.exs for runtime configuration of the release

for runtime configuration of the release remove Distillery dependency (remember to mix deps.unlock it!)

See the “Replace Distillery with Elixir releases” PR on Hex.pm repo for more details.

We now have a few files that deal with configuring our app/release, let’s take a step back and see what they can do:

config/prod.exs – provides build-time application configuration

– provides build-time application configuration config/releases.exs – provides runtime application configuration. We’re using the new Config module and the System.fetch_env!/1 function, also introduced in Elixir v1.9.0, to conveniently return the environment variable if set, or raise an error.

– provides runtime application configuration. We’re using the new module and the function, also introduced in Elixir v1.9.0, to conveniently return the environment variable if set, or raise an error. rel/vm.args.eex – provides a static mechanism for configuring the Erlang Virtual Machine and other runtime flags. For now, we use the defaults but if down the line we’d tune the VM, we’d set the options here.

– provides a static mechanism for configuring the Erlang Virtual Machine and other runtime flags. For now, we use the defaults but if down the line we’d tune the VM, we’d set the options here. rel/env.sh.eex – provides a dynamic mechanism for setting up the VM, runtime flags, and environment variables.

RELEASE_NODE and RELEASE_COOKIE variables are used by the release script, see “Environment variables” section in the documentation for all recognized variables. The POD_A_RECORD variable we have there is specific to our deployment environment on Hex.pm, we deploy it to Google Kubernetes Engine.

See “Application configuration” and “vm.args and env.sh (env.bat)” sections for more information.

Finally, we use the mix release task to actually assemble the release:

$ mix release * assembling hexpm-0.0.1 on MIX_ENV=dev * using config/releases.exs to configure the release at runtime * creating _build/dev/rel/hexpm/releases/0.0.1/vm.args * creating _build/dev/rel/hexpm/releases/0.0.1/env.sh Release created at _build/dev/rel/hexpm! # To start your system _build/dev/rel/hexpm/bin/hexpm start Once the release is running: # To connect to it remotely _build/dev/rel/hexpm/bin/hexpm remote # To stop it gracefully (you may also send SIGINT/SIGTERM) _build/dev/rel/hexpm/bin/hexpm stop To list all commands: _build/dev/rel/hexpm/bin/hexpm

Running the release

The generated release script ( bin/hexpm ) has many commands:

$ _build/dev/rel/hexpm/bin/hexpm Usage: hexpm COMMAND [ARGS] The known commands are: start Starts the system start_iex Starts the system with IEx attached daemon Starts the system as a daemon daemon_iex Starts the system as a daemon with IEx attached eval "EXPR" Executes the given expression on a new, non-booted system rpc "EXPR" Executes the given expression remotely on the running system remote Connects to the running system via a remote shell restart Restarts the running system via a remote command stop Stops the running system via a remote command pid Prints the OS PID of the running system via a remote command version Prints the release name and version to be booted

In our Hex.pm deployment we have used two of these commands for now:

bin/hexpm start – we use it as the start command to be run in our Docker container

– we use it as the start command to be run in our Docker container bin/hexpm eval – we use it to run DB migrations and other maintenance scripts. For migrations, the command is: bin/hexpm eval 'Hexpm.ReleaseTasks.migrate()' .

Summary

In this blog post we’ve walked through using Elixir releases on an existing project, Hex.pm. We’ve installed the development version of Elixir, configured the release, and adjusted our deployment setup to use it. Hex.pm was previously using Distillery, and with minimal changes we were able to update it to use built-in releases support.

Overall, I’m very happy about this change. We’ve ended up with about the same amount of configuration code, but I think it’s a little bit better structured and more obvious.

I especially like new conventions around configuration. Where previously we used workarounds like config :app, {:system, "ENV_VAR"} and "${ENV_VAR}" (and REPLACE_OS_VARS=true ), we now have a clear distinction between build-time and runtime configuration. mix release documentation does a really good job of explaining configuration aspects in particular but also the whole release process in general.

Building the release is now faster too, on my machine ~2.5s now vs ~5.5s before. Granted, it’s probably the least concern but it’s a nice cherry on top nonetheless.

As of this writing, Hex.pm is already deployed using Elixir releases. Now your turn – try out releases on your project! (And if something goes wrong, submit an issue!)