Realtime Stats

Readers: 0

Likes: 0

Let's D4Y

I believe there is a strong case to be made for shipping your app / idea as soon as possible as this pushes you to think about creating something small that is functional and finished.

So today I want to cover a topic that has been discussed a lot in the community and there seems to be a tendency in Elixir land that they are hard. Let's discuss deployments! The fact that it is often categorised as difficult or hard seems to scare people away and confuses a lot of people. I'm here to tell you that deployments are not hard, once you know your options.

Releases - yes you should use them!

There are basically two ways to run your elixir app in production. One is using mix and the other is using releases. I want to focus today on using releases as using a build tool to deploy and run your app seems silly... And I think demistifying all the fuss around releases will make sure we get more comfortable using them.

We'll take 3 steps in learning about deployment. First we'll package and run our release locally to test out how releases work, next we'll take one of the easiest platforms I've seen to deploy our app, render.com, and lastly we'll go the other deep end and run our app in a kubernetes cluster!

In this post we'll cover steps 1 and 2 and I'm going to leave the kubernetes way for the next blog post as that needs some more indepth explaination. You might think that I'm nuts going the kubernetes route, but I wanted to play around with it and see if it is really that hard to grasp... tl;dr => It really isn't nowadays due to all the managed kubernetes services that handle a lot of the hard stuff behind the scenes.

Baking and running a release locally

After all the hard work that has been done on distillery by Paul Schoenfelder (a.k.a. bitwalker), Elixir has embraced releases as part of core. So no more excuses not to use releases! And ofcourse Phoenix has gone along with this trend and is enabling deployment with releases out of the box.

So let's see how easy it is to get this up and running. A lot of guides out there omit the database part, but I'll add that here so we know how to enable that as well. For production we'll set a DATABASE_URL environment variable as that is most convenient to use when deploying. We will also set the SECRET_KEY_BASE as an environment variable. Locally we'll test our release in development mode, because setting a local release to production mode requires all kinds of extra settings that we really don't need to test this out.

If you want to try this out while reading this, you can create a fresh Phoenix app and follow along:

mix archive.install hex phx_new 1.4.11
mix phx.new test_deployment

Now let's take this step by step:

  • setup a releases.exs file
  mv config/prod.secret.exs config/releases.exs
  • change use Mix.Config to import Config in releases.exs
  sed -i'' -e 's/use Mix.Config/import Config/g' config/releases.exs
  • set server: true in config/releases.exs
  sed -i'' -e 's/^#.*config :test_deployment, TestDeploymentWeb.Endpoint, server: true/config :test_deployment, TestDeploymentWeb.Endpoint, server: true/g' config/releases.exs
  • remove import_config prod.secret.exs from config/prod.exs
  sed -i'' -e 's/import_config "prod.secret.exs"//g' config/prod.exs
  • create our database locally (so we can actually test it with a database). Note that we create the development db with this command
  mix ecto.create
  • set environment variables
  export SECRET_KEY_BASE=$(mix phx.gen.secret)
  export DATABASE_URL=ecto://postgres:postgres@localhost/test_deployment_dev
  • prepare our app
  # fetch dependencies
  mix deps.get --only prod
  MIX_ENV=prod mix compile

  # compile assets
  npm run deploy --prefix ./assets
  mix phx.digest
  • build our release
  MIX_ENV=prod mix release
  • run it!
  _build/prod/rel/test_deployment/bin/test_deployment start

After that visit http://localhost:4000 and see your fresh release shine!

Deploying a release to render.com

Now let's see what it takes to put this live on render.com. If you are following along, make sure you have the repo you want to use on gitlab or github so you can pull that in easily.

Creating an account on render.com is easy and deploying and running your app is charged by the second, so you can safely follow along this turorial and shut the app down once done so you have an idea of the workflow involved in getting your app live on render.com. Note that signing up with github or gitlab gives you the extra benefit of being able to select the repo to deploy right from render, Which makes it even easier!

So go to https://dashboard.render.com/select-repo?type=web and select the repository you want to deploy. After that fill in the neccessary details. So choose a descriptive name and set the environment to Elixir. You'll notice that the default settings to deploy here are mix phx.digest and mix phx.server. In our case we don't want to use that, because we are using releases. To use releases in render we have to change these two settings.

We'll set the build command to: build.sh, which we will create in our repo shortly. The start command will be the command we used when testing the release locally. In our case this would be: _build/prod/rel/test_deployment/bin/test_deployment start. There are two more settings needed in the advanced setting. First one is the SECRET_KEY_BASE environment variable(you can just generate one with mix phx.gen.secret and paste that in here) and the other one is the database url. We'll need to create the database first for this.

Setting up the database

To set up a database for our app go to https://dashboard.render.com/new/database and create a database with the settings you prefer. After creating the database you can go into the settings page and copy the External connection string. This string should go into the advanced setting of your app as DATABASE_URL (exactly as we did when testing it locally).

Build script

The build script gives us all the power to customise building our app when deploying. In our case this means entering the commands that will be run to prepare our app for deployment.

This is the content of our build.sh file. As you can see we are basically adding the steps we used when creating the release on our local machine.

#!/usr/bin/env elixir

# Initial setup
mix deps.get --only prod
MIX_ENV=prod mix compile

# Compile assets
npm install --prefix ./assets
npm run deploy --prefix ./assets
mix phx.digest

# Remove the existing release directory and build the release
rm -rf "_build"
MIX_ENV=prod mix release

# for auto DB migration upon deploy
MIX_ENV=prod mix ecto.migrate

After creating the build.sh file, make sure to make it executable:

chmod a+x build.sh

And..... deploy!

Once we have the database up and running and the environment variables setup we can proceed to deploy. And the beauty here is that once you push to master with the build.sh included it will automagically trigger a deploy on render.com

Custom domain

Once you have your app deployed, you'll get a url generated by render to access your app. However it is fairly easy to add your custom domain in your app settings.

Alright, hope this was helpful and I certainly hope that this illustrates how straightforward it is to use releases to deploy Elixir / Phoenix apps. As promised in the next post I'll cover a deployment setup that is a bit more complex using kubernetes. That is actually also the setup that I've used to deploy realworldphoenix.com, so more good stuff is coming in 2020!

Happy New Year to all of you and may 2020 be the year that Elixir shines for us all!

Until next time!