Configuring Distillery

Following up on our last post about generating an Erlang release through Docker, let's talk about how we configure Distillery to generate the actual release. Here again I’m using the example of the scripts I use to deploy Grapevine, to give us a real example to look at.

Production Environment

First let’s take a look at the production environment block.

environment :prod do
  set include_erts: true
  set include_src: false
  set cookie: :crypto.hash(:sha256, System.get_env("COOKIE")) |> Base.encode16() |> String.to_atom()
  set vm_args: "rel/vm.args.eex"

  set config_providers: [
    {Mix.Releases.Config.Providers.Elixir, ["/etc/grapevine.config.exs"]}

Lines 2-3 include ERTS (Erlang Run-Time System) and don't include your application’s source code. We don’t want to the source code as only the compiled BEAM files are necessary for production.

In line 4 we set a custom cookie via an environment variable. This is a must for open source projects like Grapevine. The Erlang cookie is the only way to prevent nodes from connecting to your Erlang node and having full access, outside of the system firewall. I use a script that exports a known cookie that is excluded from Git so it should never be committed. You could also set your cookie as an environment variable for your CI server if you build there. However it gets to production, it’s important that your Erlang cookie isn't committed in the codebase.

On line 5 we set a custom vm.args template, this is how you can alter what distillery generates for vm.args. For example, in my vm.args template I configure Distillery to use -sname for the node name, instead of the default -name, which Grapevine uses for local clustering.

Finally, on lines 7-9, we set a config provider file. Because we deploy to a VPS instead of running inside Docker, we can template a configuration for the generated release. This config lets us tweak a standard release after it has been generated. This file should only contain configuration that would typically be left in the ENV from our rails applications, API secrets, SMTP secrets, etc.


The other important piece to look at is the release block.

release :grapevine do
  set version: current_version(:grapevine)
  set applications: [
    grapevine_telnet: :none

  set commands: [
    migrate: "rel/commands/migrate.sh",
    restarting: "rel/commands/restarting.sh",

On line 2, we set the release version to be the version set in our mix.exs file.

In this example the applications section, lines 3-5, is a bit unusual in that Grapevine includes a dependent application that should not be started. This application runs on another local Erlang node in the cluster. This is fairly unique for this application and you will probably not require these lines. If you do get an error from Distillery saying a set of applications were not included in the generated list, you can also add them here to make sure they get included in the final release.

The last section is a set of custom commands that run bash scripts. I have two for Grapevine, one to perform migrations and one that runs before the server restarts that tells all games connected to the chat network that Grapevine is about to restart.

Distillery Commands

Distillery allows you to set up some very simple scripts that you can then access as commands. Here is an example of the “restarting” script referenced above:

bin/grapevine rpc "Grapevine.restart()"

In this case I call Grapevine.restart/0 which broadcasts a restart event to all connected games.


The scripts above in combination with the last post Deploying with Distillery and Docker should get you a fully built Erlang release that you can deploy however you wish, even by simply SCPing the file to your server and untarring manually.

For a real example that I use to deploy Grapevine, you can find the rel/config.exs file, the rel/vm.args.eex, and a sample grapevine.config.exs on GitHub. You can also watch the video below where I walk through deploying Grapevine.

Header photo by Louis Reed on Unsplash

You've successfully subscribed to SmartLogic Blog
Great! Next, complete checkout for full access to SmartLogic Blog
Welcome back! You've successfully signed in.
Unable to sign you in. Please try again.
Success! Your account is fully activated, you now have access to all content.
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.