Subscribe to access all episodes. View plans →

#3: Deploying Elixir with Heroku

Published January 30, 2017

Elixir 1.3.4

Phoenix 1.2.1

Episode source code on GitHub


Here we have the blog we built in episodes one and two. Now let’s take the next step and deploy it. There are a lot of hosting options out there. But in this episode we’re going to focus on deploying with Heroku .

First you’ll need to have an account with Heroku and have both Git and the Heroku Command Line Interface or CLI installed.

Let’s get started.

We’ll head to our command line and from the directory of our app, we’ll use $ git init to initialize our git repo. Then we’ll add all the files from our project to Git:

$ git add .

Now we’ll need to commit them. We’ll use $ git commit to record our changes:

$ git commit -m 'initial commit'

With that done, we’ll want to create an application on Heroku. We’ll use ‘heroku create’ and then pass it a build pack, which is our way of telling heroku that we are using an elixir app.

$ heroku create --buildpack "https://github.com/HashNuke/heroku-buildpack-elixir.git"

Heroku sets the buildpack and then gives us a URL. In our case, the url is rocky-depths-40660.herokuapp.com. This is the url we’ll be able to access our application on.

We also need to add the Phoenix static build pack to our app in order to compile our static assets.

$ heroku buildpacks:add https://github.com/gjaldon/heroku-buildpack-phoenix-static.git

Now let’s add our production database. We’ll use Heroku’s Postgres addon with the hobby-dev plan.

$ heroku addons:create heroku-postgresql:hobby-dev

And with that added we can configure our app for Heroku.

We’ll use Heroku environment variables to set our application specific configuration.

Let’s start with the pool size for our database.

According to the Phoenix docs, we want this to be just under the available connections.

Our hobby-dev database allows 20 connections, so let’s set this to 18.

$ heroku config:set POOL_SIZE=18

Great, now let’s set a secret key base for our application to use.

Phoenix provides a task to make creating one easy.

Let’s run $ mix phoenix.gen.secret.

And then set this to the value of a ‘SECRET_KEY_BASE’ environment variable.

$ heroku config:set SECRET_KEY_BASE="abcde123456"

Now we can open ‘prod.exs’. We’ll go to the config for our Endpoint. And on the URL line, we’ll add ‘scheme’ with ‘HTTPS’ as the value since our default URL on Heroku will also be HTTPS.

Then we’ll update the host to match the one Heroku gave us for our app - yours will be different.

And we’ll update the port the 443.

Let’s force all requests to use SSL, so we’ll add the ‘force_ssl’ option.

Now we need to tell our application to use the ‘SECRET_KEY_BASE’ we set. We’ll use System.get_env/1 and then the name of the environment variable we want to return - in this case ‘SECRET_KEY_BASE’.

I’ll add our Teacher.Repo config off screen.

Let’s walk through it.

We’ll set our database adapter to PostgreSQL. Our database URL was setup when we added the Heroku Postgres database. It’s stored in the ‘DATABASE_URL’ environment variable.

Then we’ll use the ‘POOL_SIZE’ environment variable we created, defaulting to 10 if it’s not found.

And we’ll set SSL to true.

Since we are using Heorku environment variables, we don’t need to import ‘prod.secret.exs’. Let’s go down the to bottom of the file and remove import_config "prod.secret.exs".

config/prod.exs

config :teacher, Teacher.Endpoint,
  http: [port: {:system, "PORT"}],
  url: [scheme: "https", host: "rocky-depths-40660.herokuapp.com", port: 443],
  force_ssl: [rewrite_on: [:x_forwarded_proto]],
  cache_static_manifest: "priv/static/manifest.json",
  secret_key_base: System.get_env("SECRET_KEY_BASE")

config :teacher, Teacher.Repo,
  adapter: Ecto.Adapters.Postgres,
  url: System.get_env("DATABASE_URL"),
  pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
  ssl: true
  ...
  #import_config "prod.secret.exs"

We’ll also want to handle idle connections. Opening ‘user_socket.ex’ we’ll set a timeout of 45 seconds that will allow Phoenix to close idle connections before they reach Heroku’s 55 second timeout.

web/channels/user_socket.ex

defmodule Teacher.UserSocket do
  use Phoenix.Socket
  ...
  transport :websocket, Phoenix.Transports.WebSocket,
  timeout: 45_000
  ..
end

Now we can create a ‘Procfile’ for our Heroku deploy. In it we’ll tell Heroku to start our Phoenix server.

Procfile

web: MIX_ENV=prod mix phoenix.server

With that done, let’s head over the command line and add commit our updates.

$ git add config/prod.exs Procfile web/channels/user_socket.ex

$ git commit -m "updates for heroku"

Now we can deploy to heroku with: $ git push heroku master.

And it looks like our app was deployed successfully.

Now we just need to run the migrations. We’ll need to specify 2 as the ‘POOL_SIZE’ to prevent echo from trying to open more connections than are available.

$ heroku run "POOL_SIZE=2 mix ecto.migrate"

Now we can open our app in the browser. Perfect - we see the familiar ‘Welcome to Phoenix’ template.

Let’s check out our posts by creating one. And great our post was created. It looks like our app is up and running in production.

If we inspect the logs from our deploy, we see that the Elixir buildpack defaults to Erlang 18 and Elixir 1.3.4.

If we wanted to use a different version of Elixir or Erlang, how would we do that?

Going back to our app, let’s create a file named ‘elixir_buildpack.config’. And in it we can specify the Erlang and Elixir versions:

elixir_buildpack.config

# Erlang version
erlang_version=19.0

# Elixir version
elixir_version=1.4.0

We would then save this file, commit it, and on our next deploy and it would use Erlang 19 and Elixir 1.4.

For a full list of customizations be sure to check out the Heroku Buildpack for Elixir project.

© 2024 HEXMONSTER LLC