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
sed -i'' -e 's/use Mix.Config/import Config/g' 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
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
- 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!
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
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
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
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).
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
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
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!