How to create Elixir Releases. And some tips for Phoenix, Ecto, and umbrella applications. A Quick Refresher and a Bit of History Releases have long been around in the Erlang/OTP world. I have previously written about . Here is a little refresher: Releases are self-contained bundles of compiled Elixir/Erlang that can easily be deployed on e machines. what Releases are and why they’re great in more detail code remot Naturally, most tools for creating Releases have so far come from the Erlang world. Erlang ships with and , but their usage is rather complicated. So the community built as an easier-to-use alternative that has now become the de-facto standard for creating Releases from Erlang projects. Since those tools are all made to work with Erlang, they work with Elixir projects but leave much to be desired. systools reltool [relx](https://github.com/erlware/relx) can In 2014, Paul aka set out to make Elixir Releases easier and published — built on top of with some Elixir-specific features. Due to limitations of , was inflexible and never able to leverage the full potential of modular Elixir applications. So Paul decided to create a new Release manager, written from scratch in pure Elixir — and so was born. bitwalker exrm relx relx exrm Distillery Step-by-step guide In this section of the article, you’ll learn how to add Distillery to a project and build your first release step-by-step. Let’s go! I have created a small application that prints out the current time every second. We can use it to build a Release: git clone cd distillery-demo https://github.com/wmnnd/distillery-demo.git Installing Distillery & Creating the First Release Before you can create your first Release, you need to install Distillery. Add to the list of dependencies in your project’s and run . {:distillery, "~> 1.5"} mix.exs mix deps.get Next, we initialize Distillery by calling . This command creates a configuration file . You could also manually create the file but it’s more convenient like this. mix release.init rel/config.exs We will look at the contents of the configuration file in a bit. For now it’s enough to build our first Release. In order to do that, run . Distillery will now create an output that looks something like this: mix release ==> Assembling release..==> Building release clock:0.1.0 using environment dev==> You have set dev_mode to true, skipping archival phase==> Release successfully built!You can run it in one of the following ways:Interactive: _build/dev/rel/clock/bin/clock consoleForeground: _build/dev/rel/clock/bin/clock foregroundDaemon: _build/dev/rel/clock/bin/clock start Congratulations, you have just created your first Release! Run it with . As expected, the application now prints out the current time in one-second intervals. You can terminate the application by hitting . _build/dev/rel/clock/bin/clock foreground Ctrl+C Note: There is currently a bug in Distillery that might crash the Release when starting it. In this case, run **mix clean && mix compile** and try running the application again. In the call to start the application, you passed the command at the end. This is the . There are also other boot commands for starting an application: launches an interactive iex session with your application and creates a daemon process in the background. foreground boot command console start Release Profiles and Environments Let’s take a step back. You’re probably already familiar with Elixir’s build tool Mix. Mix supports multiple : , and . Depending on the current environment, Mix can, for example, determine which dependencies or configuration files to include. environments dev prod test You might have noticed that when we created the Release, Distillery printed out this line: . Distillery also has but they are different from Mix environments. Building release clock:0.1.0 **using environment dev** environments — Distillery uses environments to allow for multiple configurations of the Release building process. Conveniently — or confusingly — Distillery environments are by default the same as the Mix environments. Let’s take a look at the auto-generated configuration file in order to better understand how profiles work: rel/config.exs use Mix.Releases.Config,default_release: :default,default_environment: Mix.env() We can see that is set to . This means that Distillery will match our Mix environment. You can override default environment with the switch. Here are some examples of how this could work: default_environment Mix.env() --env mix release#Mix environment and Distillery environment are both dev MIX_ENV=prod mix release#Mix environment and Distillery environment are both prod MIX_ENV=prod mix release --env=dev#Mix environment is , Distillery environment is prod dev The configuration file also specifies that is . This is because you can actually define more than just one Release for your project. With the setting, Distillery automatically picks the Release defined in . If you want to override the default Release, you can specify this with the flag. default_release :default :default first rel/config.exs --name You can pick a specific profile with the flag. The combination of an environment with a specific Release name is called a . profile --profile=name:env In the next section we’re going to take a look at how we can use multiple environments to configure Distillery. Release Build Configuration Our Distillery configuration includes two environments ( and ) on top of one Release named like our project ( ): dev prod clock environment :dev doset dev_mode: trueset include_erts: falseset cookie: :"W?cN_`G<>ayUI&ku{<$3w7J<^nUBRBu[F[…]"end environment :prod doset include_erts: trueset include_src: falseset cookie: "^_`fz{dk|`w.n3Z%T,n=F>ezazFk.1ci5}[…]"end release :clock doset version: current_version(:clock)set applications: [:runtime_tools]end Here is what the options included in the configuration file do: : If , bytecode, assets and other files are not copied to the Release folder. Instead, Distillery creates symlinks. This makes creating the Release faster and is great for testing purposes. dev_mode: true/false true : If , Distillery includes the Erlang Runtime ERTS. This is not necessary on your development machine but recommended for making self-contained Releases.If you want to deploy your Release to a machine with a different operating system or processor architecture, you can specify the path to a cross-compiled version of ERTS instead of . include_erts: true/false true true/false : Distillery can set the . The auto-generated configuration includes random Erlang cookies. Later in this post we’ll look into a better way to configure the magic cookie. cookie: $STRING Erlang cookie Don’t include a hard-coded cookie if you want to commit your configuration to a Git repository! : This option is available for with the Erlang Release tool . If , Erlang source code from your project and dependencies is included in the Release. Elixir code is never included, so most of the time, you can ignore this option. include_src: true/false backwards compatibility relx true : Sets the version of the Release. You can use the function to extract the version from your project’s . version: $VERSION_STRING current_version/1 mix.exs : Here you can list additional applications that you want to include. By default, Distillery includes which is part of the Erlang standard library and enables certain debugging features. applications: [:app_name] :runtime_tools Lifecycle Scripts aka Hooks It is possible to add shell scripts to your Release to run at certain points of your application’s lifecycle. These scripts are also referred to as and can be configured in your . Simply add them to an or section like this: hooks rel/config.exs env release environment :prod doset pre_start_hook: "rel/hooks/pre_start.sh"end Notice that the paths are relative to your project directory and not relative to the directory. The most interesting hook is certainly , but there is also , , , and . You can currently only configure one script per hook with Distillery although Erlang would theoretically allow for more. rel pre_start post_start pre/post_configure pre/post_stop pre/post_upgrade Umbrella Apps Distillery is also great when working with Umbrella apps! There are really only two things you need to pay attention to: Include the names of your child applications in the list of the Release configuration. applications You can choose to take the Release version number from any of the child applications by using . current_version(:my_child_app) As mentioned before, it’s also possible to define more than one Release in your and thus specify multiple Release profiles. This can be useful if you want to have Releases that only include certain child applications from your umbrella project. rel/config.exs Runtime Configuration With Releases Configuring Elixir applications — especially when it comes to third-party libraries, can be a tricky affair. Ecto core-developer Michał recently about the confusing status quo. voiced his concern The Problem with Mix Configuration The first thing you need to remember is that configuration in Mix configuration files is evaluated at compile-time. This is not a problem when you run your application from source code. But when Distillery builds a Release, all configuration files are evaluated at build-time. If you have anything like in a Mix.Config file, you’re out of luck. will take on the value of the environment variable from your build system. When you launch the application on another machine, the value won’t be updated. foo: System.get_env("BAR_VAR") foo BAR_VAR Fortunately, there are ways around this. The easiest one is a little trick employed by Distillery: A Simple Solution: REPLACE_OS_VARS Distillery has a special way of injecting environment variables into Mix configuration. Simply use a string like in your config file and you’re good to go. Distillery will replace it with the corresponding environment variable value when your application starts. All you need to do for this to work is setting the environment variable when launching your release. This makes it very easy to write configuration like this: "${BAR_VAR}" REPLACE_OS_VARS=true config :my_app,foo: "${BAR_VAR}" Use in your to set the cookie from an environment variable. This method is great for initializing the Erlang cookie. set cookie: "${ERLANG_COOKIE}" rel/config.exs doesn’t come without problems. Most importantly, this method only works with strings. If you try something like , you’ll get an error message because the compiler evaluates the expression before Distillery gets a chance to replace the special string. REPLACE_OS_VARS String.to_integer("${BAR}") Fortunately, many libraries now support a more flexible approach to configuration. Ecto Ecto has introduced a new way of dynamically configuring application dependencies. Their solution: A callback! You can dynamically configure Ecto with the callback. Ecto.Repo.init/2 This callback takes and the as arguments. We can safely ignore (which is either or , depending on the context of the call). is the configuration Keyword list from your Mix configuration. This is convenient because it allows you to set default values in your Mix configuration and then override them with environment variables. type config type :supervisor :dry_run config The callback has to return or . Here is a simple example with type conversion: {:ok, config} :ignore Instead of writing your own code for parsing environment variables, you might find a third-party application such as or useful. I haven’t tried them yet but they seem to have some nice convenience features. Atmo Confex Phoenix Creating a Release from a Phoenix application is easy. Before you create a production release, make sure your assets are built and digested: ./node_modules/brunch/bin/brunch b -pMIX_ENV=prod mix phx.digest With version 1.3, Phoenix has also adapted the Ecto’s callback-style configuration method. This makes it very easy to flexibly configure : Phoenix.Endpoint Upgrading Releases No (pretty) complete guide to Elixir Releases would be (pretty) complete without mentioning one of their coolest features: Hot updates. You probably know that Erlang and Elixir are compiled to bytecode which is then executed by the . And thanks to BEAM, we can upgrade an application without ever stopping it. BEAM virtual machine Creating an Upgrade Release Release upgrades internally use files to determine which modules should be upgraded and how. Fortunately for us, Distillery creates them automatically when we pass it the parameter. Otherwise, Distillery wouldn’t be able to determine which modules need updating. .appup --upgrade Make sure the previous Release is still available in your **/_build** folder when building an upgrade. Creating a Production Release Only Releases that include ERTS can use hot upgrading. So, let’s start by creating a production Release of our application: clock MIX_ENV=prod mix deps.getMIX_ENV=prod mix release #In Terminal 1 Now open up a second terminal window and launch the Release: #In Terminal 2cd distillery-clock-demo_build/prod/rel/clock/bin/clock foreground Again, you should see the current time printed out every second. Creating an Upgrade Release Let’s create a new version of . Since it prints out its current version number together with the current time, all you need to do to get a different output is change the in . Currently it’s set to . Let’s change it to . clock version mix.exs 0.1.0 0.2.0 Then, create an upgrade Release by passing the flag to . --upgrade mix.release MIX_ENV=prod mix release --upgrade #In Terminal 1 We’ll Do It Live! After the Release upgrade has been created, the Clock application in the second terminal hasn’t changed. Yet. Still in the first terminal, run the following command: #In Terminal 1_build/prod/rel/clock/bin/clock upgrade 0.2.0 In the second terminal, the Clock output will now change and start to also print out the current date. How neat! Updating an application wile it’s running! Are You a Master of Elixir Releases Yet? I hope that after reading this guide, you now have a better understanding of Elixir Releases . I certainly do after writing it! If you think I missed an aspect of Elixir Releases that should be part of this (pretty) complete guide, please let me know and I will try to add it! Upcoming articles will cover deployment strategies and building Docker images. This article is part of an ongoing series about developing and deploying Elixir applications to production. In this series I am sharing my experience from creating DBLSQD , a release and update server written in Elixir. Check it out, there is a 60-day no-strings-attached free demo: _DBLSQD is a release + update server for desktop and mobile apps. Powerful SDKs for Electron & Qt. Compatible with Sparkle, Squirrel & others._www.dblsqd.com Publish & Distribute Software — DBLSQD