monoton

Deploying a Phoenix application I

You finished writing a fancy Phoenix application. Now you’re asking yourself: Is there a better way than copying over your working directory to the server and running mix phx.server? Yes, there is!

In this post we’ll learn how to deploy a Phoenix application to our production server with Distillery. In general, there are multiple ways to deploy your application: Some are based on Docker, some rely on git hooks and others are completely automated. For now we’ll focus on a basic, semi-automatic deployment mechanism. Once you get used to it you can extend your deployment pipeline by fully automizing your deployment using edeliver.

While this guide will fit most apps, your mileage may vary. Especially applications that use many (native) libraries are difficult to deal with in Distillery. Feel free to consult the excellent documentation if you want to learn more about its intricacies.

For better reproducibility, here are the versions I’ve used:

Create your project

At first, we’ll create our sample application DeployApp. If you already wrote a fancy Phoenix app you can skip this step, of course.

$ mix phx.new deploy_app --no-ecto

Install Distillery

Installing Distillery is pretty straightforward. Just add it to the dependencies in your mix.exs:

defp deps do
  [...,
   {:distillery, "~> 1.4", runtime: false}]
end

Pull it, compile it.

$ mix do deps.get, compile

Initialize Distillery

Fortunately, Distillery provides a task that generates the relevant configuration files for us. Simply run:

$ mix release.init

Our project now contains one more directory called rel. This contains all the files relevant for building releases, most importantly config.exs.

Configure Phoenix

When coding you are probably using mix phx.server to serve your application. It automatically boots up the Cowboy server as soon as your Endpoint supervision tree starts. In order to have the Endpoint start the web server automatically you have to uncomment the following line in your config/prod.exs:

config :deploy_app, DeployAppWeb.Endpoint, server: true

Install Erlang on server

Follow the instructions at the official Erlang Solutions site to download and install the erlang package.

Get the ERTS

Every time you are using Elixir or Erlang, ERTS is used. ERTS stands for Erlang Run-Time System (Application) and provides the functionality to run the Erlang system. You can check the version of your local ERTS by opening an erl or iex shell.

If you build a release with Distillery’s default config it will include your local ERTS in the build. Most distros and Erlang versions ship different runtimes. Since the ERTS on our production server likely differs from our local ERTS we have to do some cross-compiling. To do that, the first step is to get the ERTS of the server.

On the server, we copy the whole Erlang system folder to our ~, create a tar archive and then use scp to fetch it. On the average Ubuntu distro you can do it this way:

$ cd ~
$ cp -r /usr/lib/erlang prod_erlang
$ tar -cvf prod_erlang.tar.gz prod_erlang

Locally, get the prod_erlang.tar.gz via scp and extract it.

$ scp user@server:~/prod_erlang.tar.gz ~/deploy_app/rel
$ cd deploy_app/rel
$ tar -xvf prod_erlang.tar.gz
$ rm prod_erlang.tar.gz

Configure Distillery

Now we are going to make some changes to the aforementioned rel/config.exs. Open it and try to understand the general structure. Since we want to build a production release, we’re going to make some changes to the corresponding environment block.

Currently, Distillery bundles your local ERTS with release because include_erts is set to true. To use our production runtime, we have to specify two paths:

environment :prod do
  set include_erts: "rel/prod_erlang"
  set include_system_libs: "rel/prod_erlang/lib"
  ...
end

Note: With this configuration we’re only cross-compiling the BEAM. If you use any NIF libraries you have to setup a real cross-compilation toolchain.

Read the next post

Congrats for already getting this far! With the current configuration you could already build a release. However there are still some things to setup and consider. In the next post in this series we’ll learn about configuring Phoenix to use a reverse proxy and finally build a real release.