Skip to content

Walkthrough

Tip

Before starting, please review the Terminology page to get familiar with the terms used in this guide.

This is a simple guide on how to use Distillery and releases in general. We will cover the following:

  • Adding Distillery to your project
  • Initializing Distillery
  • Configuring your release
  • Building your release
  • Deploying your release
  • Building an upgrade release
  • Deploying an upgrade

Let’s get started!

Adding Distillery to your project

Just add the following to your deps list in mix.exs:

1
2
3
defp deps do
  [{:distillery, "~> 2.0"}]
end

Then run mix do deps.get, compile, and you’re ready to go!

Initializing Distillery

To set up your project for releases, Distillery will create a rel directory in your project root. All files related to releases will be in this directory.

Distillery also creates rel/config.exs, which is the configuration file you will use to configure Distillery and your releases. Depending on your project type, it will create an appropriate default release configuration, along with two environments, :dev and :prod, with typical configuration for both. You can leave this configuration untouched, or modify it as desired. We will look at this file and discuss its contents briefly. Check out Configuration for more information on this file and available settings.

To initialize Distillery, just run mix release.init.

Note

In this walkthrough, we’re making the assumption that this is not an umbrella application, though there are no significant differences. See Umbrella Projects for more details on configuration specific to that setup.

Configuring your release

The default configuration file will look something like the following (comments stripped):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
use Mix.Releases.Config,
    default_release: :default,
    default_environment: Mix.env()

environment :dev do
  set dev_mode: true
  set include_erts: false
end

environment :prod do
  set include_erts: true
  set include_src: false
end

release :myapp do
  set version: current_version(:myapp)
end

Let’s talk about what these settings do from top to bottom.

Tip

See Configuring Distillery if you want to explore settings not covered in this guide.

1
2
3
use Mix.Releases.Config,
    default_release: :default,
    default_environment: Mix.env()

This loads the configuration macros required by Distillery, and sets a few optional global settings: default_release and default_environment. These setting specify which release and which environment to build by default if they are not specified as options to mix release.

If default_release is set to :default, then the first release definition in the file will be used. If default_environment is set to :default, then an “empty” environment will be used, in which case the only settings applied will be those set within a release block. By default, Distillery sets default_environment to be the same as the name of the current Mix environment.

1
2
3
4
environment :dev do
  set dev_mode: true
  set include_erts: false
end

This creates an environment called dev, and configures a few settings which are optimal for quick iteration during development. dev_mode: true will symlink compiled BEAM files into the release directory, instead of copying them, which ends up being significantly faster. include_erts: false tells Distillery to not copy the Erlang Runtime System (ERTS) into the release directory, and instead just use the system-wide ERTS.

1
2
3
4
environment :prod do
  set include_erts: true
  set include_src: false
end

This creates an environment called prod, and configures a few settings which are optimal for a self-contained production release package. include_erts: true bundles the Erlang Runtime System so that the target system does not need to have Erlang or Elixir installed, and include_src: false will ensure that unnecessary source code files are not included in the release package, reducing the final file size of the release.

1
2
3
release :myapp do
  set version: current_version(:myapp)
end

This creates a new release definition, named myapp, and sets the only required setting for a release definition, version to whatever the current version of the myapp application is when mix release is run, by using the current_version/1 macro.

You must have at least one release definition in the config. You do not have to create any environments, though the example config does so for reference.

To recap, an environment is a group of settings which override those of a release when that environment is active, a release is the specific configuration for a group of applications which will be packaged together. In a non-umbrella application, it is usually the case that the release is named/versioned the same as the application being released. In umbrella applications, this may or may not be true, depending on whether the umbrella is released per-app or as one. When building a release, the release configuration is overlaid with the environment configuration, to form what is called a “profile”. In simpler terms, if you have a release named myapp, and an environment called dev, the profile would be myapp:dev.

Building your release

Now that we’ve configured our application we can build it! Just run mix release. There are a few flags to mix release that you may be interested in using right now:

  • --verbose – log detailed information about what Distillery is doing and metadata it discovers. If you encounter issues, you should always turn this on to help troubleshoot.
  • --name=<name> – build the release with the given name, as defined in rel/config.exs
  • --env=<env> – build the release using the given environment, as defined in rel/config.exs
  • --profile=<name:env> – build the given release profile (release + environment)

Tip

See mix help release for more usage information and additional flags.

When you run mix release, you should see something like the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
==> Assembling release..
==> Building release myapp:0.1.0 using environment dev
==> You have set dev_mode to true, skipping archival phase
Release succesfully built!
To start the release you have built, you can use one of the following tasks:

    # start a shell, like 'iex -S mix'
    > _build/dev/rel/myapp/bin/myapp console

    # start in the foreground, like 'mix run --no-halt'
    > _build/dev/rel/myapp/bin/myapp foreground

    # start in the background, must be stopped with the 'stop' command
    > _build/dev/rel/myapp/bin/myapp start

If you started a release elsewhere, and wish to connect to it:

    # connects a local shell to the running node
    > _build/dev/rel/myapp/bin/myapp remote_console

    # connects directly to the running node's console
    > _build/dev/rel/myapp/bin/myapp attach

For a complete listing of commands and their use:

    > _build/dev/rel/myapp/bin/myapp help

At this point, you can run your release as described in the output.

Deploying your release

Warning

When running mix release for deployment to production, you should set MIX_ENV=prod, to avoid pulling in dev dependencies, and to make sure the compiled BEAMs are optimized.

Note

If you are deploying to a different OS or architecture than the build machine, you should either set include_erts: false or include_erts: "path/to/cross/compiled/erts". The former will require that you have Erlang installed on the target machine, and the latter will require that you have built/installed Erlang on the target platform, and copied the contents of the Erlang lib directory somewhere on your build machine, then provided the path to that directory to include_erts.

Let’s assume you’ve built your release like so, and are ready to deploy to production:

1
$ MIX_ENV=prod mix release

The artifact you will want to deploy is the release tarball, which is located at _build/prod/rel/<name>/releases/<version>/<name>.tar.gz.

Deployment might look something like the following:

1
2
3
4
$ mkdir -p /opt/app
$ cp _build/prod/rel/myapp/releases/0.1.0/myapp.tar.gz /opt/app/
$ pushd /opt/app
$ tar -xzf myapp.tar.gz

At this point, the release has been deployed, and it can be run with one of the start commands, e.g. bin/myapp start.

Warning

If you did not include ERTS, you must ensure Erlang is installed on the target system, along with any optional packages containing Erlang libraries required by your application. This Erlang installation must be the same version as the one you built the release with, as the standard library applications are versioned, and your release is built against specific versions.

The following are some alternative approaches to building releases to help make bundling ERTS easy:

  • Build the release in a Docker container running the same OS version and CPU architecture, then copy the tarball out of the container for deployment to the target.
  • Install Erlang in a Docker container running the same OS version and CPU architecture, then copy the Erlang lib directory back to your build machine so that you can cross-compile your release.
  • Same as the above, but use Vagrant

Tip

It is recommended that you use Docker for building your releases. It is simple to set up, and makes automating release builds and tracking against production infrastructure trivial.

The start command of the boot script will automatically handle running your release as a daemon, but it is also common to use systemd, upstart or supervisord, in which case you will want to use the foreground task instead.

Once started, you can connect a shell to the running release with bin/<name> remote_console

Info

If you started the release with the start task, you can stop the release with the stop task.

Info

If you started the release with the foreground task, you can stop the release by sending an interrupt with CTRL+C or kill -s INT to shut it down gracefully.

Building an upgrade release

Note

You do not have to use hot upgrades, you can simply do rolling restarts by running stop, extracting the new release tarball over the top of the old, and running start to boot the release.

So you’ve made some changes and bumped the version of your app, and you want to perform a hot upgrade of the target system. This is pretty simple!

1
$ MIX_ENV=prod mix release --upgrade

You will now have a new tarball in _build/prod/rel/<name>/releases/<upgrade_version>/ which you can use for the next step.

Deploying an upgrade

Warning

This part is very straightforward with an important caveat: Once you have deployed an upgrade, if you make a mistake in, say version X (eg, version X has a bug that is detected in production), you cannot “redeploy” a new release with the same version number (X). If you try to do so, you will discover that your application instance has marked version X as “old” and will refuse to upgrade to that version number. If you find a mistake in your application and you wish to continue with hot upgrades, rollback to the previous version (X -1), cut a new release at version (X + 1), and deploy it. At this time you will be able to upgrade twice in a row X - 1 => X => X + 1 and have a clean production instance.

To deploy a built upgrade release (using our previous example as a base):

1
2
3
4
$ mkdir -p /opt/app/releases/0.2.0
$ cp _build/prod/rel/myapp/releases/0.2.0/myapp.tar.gz /opt/app/releases/0.2.0.tar.gz
$ pushd /opt/app
$ bin/myapp upgrade 0.2.0

If the upgrade is not working properly, you can roll back to a previous version:

1
$ bin/myapp downgrade 0.1.0

Warning

You cannot downgrade to an arbitrarily old version, you must downgrade in the same order in which upgrades are applied, as the appup instructions must be applied in reverse order. In other words, to go from 0.5.0 to 0.3.0, you must first downgrade to 0.4.0, then 0.3.0