Deployment Strategies For Elixir Phoenix - Heroku

by on

Modern web technology stacks are constantly trying to get faster, trying to meet the never-ending requirements of the community and cater to the ever-growing user base.

It is hard to choose a technology that strikes a good balance between developer productivity and also is able to provide a good throughput, latency, uptime, extendability etc.

Elixir I feel is, productive for developers but also provides speed and concurrency to build robust software. But I have found deployment methodologies for Elixir - Phoenix applications not to be as mature as the runtime. Deciding on using a language solely based on the speed and productivity is not always ideal, we also have to consider other factors such as:

  • What mechanisms are available for deployment?
  • How easy/intuitive is it to deploy/rollback the applications?
  • What is the cost/effort that goes into deployment?
  • How actively maintained is the library we are planning to use for deployment?
  • How easy is it to change our deployment strategies?

In this blog series, we will cover some of the ways to deploy an Elixir - Phoenix application.

Title Link
Elixir on Heroku
Elixir on Google Cloud Coming Soon
Elixir on DigitalOcean (Using edeliver) Coming Soon
Elixir deployment using nanobox Coming Soon


But first, let us see a quick introduction to Elixir and Phoenix.

Introduction to Elixir - Phoenix

Phoenix is a web development framework written in Elixir (dynamic, and functional language designed for building scalable and maintainable applications.) which implements the MVC design pattern.

Leveraging on the Erlang VM, which is known for running low-latency, distributed and fault-tolerant systems. Phoenix provides the best of both worlds - developer productivity and high application performance.

It also comes pre-built with powerful tools such as channels that makes implementation of real-time features easy, along with pre-compiled templates (views) makes rendering really fast.

Let us start this series by looking at how easy it is to deploy an Elixir application to Heroku

Elixir on Heroku

Heroku is a polygot platform that supports various languages such as Ruby, Scala, Go, Python and Elixir. The main advantage Heroku brings to the table is the simplicity of deployment, in a sense that it allows developers to build, run and scale applications in a similar manner across several languages.

This guide considers that you have a working setup of Elixir on your local machine and have a sample Phoenix application handy.

Heroku requires you to have Git installed. Before we deploy the application to Heroku we need to initialize our Git Repository and commit the files to it.

Initializing a Git Repository

We can initialize git by running the following commands in the root of your project directory.

$ git init
$ git add .
$ git commit -m "Initial commit"

Installing Heroku CLI (Formerly called toolbelt)

You can signup for a Heroku account, if you already have an account handy you can install the Heroku CLI which basically is a tool used to create and manage applications on Heroku via the terminal. For this tutorial we will be working with a free account, this will give us access to one web dyno and one worker dyno, as well as a PostgreSQL and Redis.

Now that we have Heroku CLI is installed we can login to Heroku via the same. To log in to Heroku open a terminal and execute heroku login, enter the email ID and password that you used to signup for Heroku. If the login was successful you will see a confirmation saying Logged in as YOUR-EMAIL-ID.

Creating Elixir Application On Heroku

Let us now create the Phoenix application on Heroku which will run our project. In the root of the project run the following command.

$ heroku create --buildpack ""

This should give you an output similar to the one below.

Creating app... done, ⬢ pure-peak-67829
Setting buildpack to done |

NOTE the name of the Phoenix application that was created in my case was pure-peak-67829. This will vary for you. As Heroku creates a new and unique name for each application that it creates.

--buildpack is a convenient way of packaging framework and/or runtime support. In our case, we are installing the Erlang runtime, Elixir and also fetching the required dependencies.

Adding Phoenix Static Buildpack

NOTE this step is only needed if you have static assets in your projects, for API based Phoenix applications or for the ones that have been created with --no-html --no-brunch this step is not needed.

To add the required buildpack. Paste the following in the terminal.

$ heroku buildpacks:add

If the above command was successful then you should see an output similar to the one below.

Buildpack added. Next release on pure-peak-67829 will use:
Run git push heroku master to create a new release using these buildpacks.

Configuring Phoenix For Heroku

Since Heroku requires us to pass in sensitive information via Environment variables we will have to make some changes to our code before we can actually deploy to Heroku.

First let us change config/prod.secret.exs to load our secret key from Heroku's environment variable and also configure our Repo to load values from Heroku's environment variables.

use Mix.Config

config :demo_heroku_deployment, DemoHerokuDeploymentWeb.Endpoint,
  load_from_system_env: true,
  # Don't forget to replace pure-peak-67829 with the name of your Phoenix application that was created.
  url: [scheme: "https", host: "", port: 80],
  cache_static_manifest: "priv/static/cache_manifest.json",
  secret_key_base: Map.fetch!(System.get_env(), "SECRET_KEY_BASE")

config :demo_heroku_deployment, DemoHerokuDeployment.Repo,
  adapter: Ecto.Adapters.Postgres,
    url: System.get_env("DATABASE_URL"),
    pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
    ssl: true

config :logger, level: :info

Next we have to decrease the timeout for the WebSocket transport in lib/demo_heroku_deployment/channels/user_socket.ex because Heroku has an idle timeout of 55 seconds. And this ensures that all Idle connections are killed before the idle timeout is reached.

defmodule DemoHerokuDeploymentWeb.UserSocket do
  use Phoenix.Socket

  ## Transports
  transport :websocket, Phoenix.Transports.WebSocket,
    timeout: 50_000



The last step of the configuration involves creating a Procfile in the root of the project.

web: MIX_ENV=prod mix phx.server

Setting Up Environment Variables On Heroku

First let us configure the Phoenix application to use Postgres as the database by adding an add-on.

$ heroku addons:create heroku-postgresql:hobby-dev
Creating heroku-postgresql:hobby-dev on ⬢ pure-peak-67829... free
Database has been created and is available
 ! This database is empty. If upgrading, you can transfer
 ! data from another database with pg:copy
Created postgresql-adjacent-12021 as DATABASE_URL
Use heroku addons:docs heroku-postgresql to view documentation

This not only adds Postgres but also sets up the DATABASE_URL which we had created earlier.

Let us now configure the POOL_SIZE Number of connections that we have to the database

$ heroku config:set POOL_SIZE=18
Setting POOL_SIZE and restarting ⬢ pure-peak-67829... done, v5

Let us now set up the SECRET_KEY_BASE (application secret) with :

$ mix phx.gen.secret

$ heroku config:set SECRET_KEY_BASE="frVmI4tvRSLJBSH09i657N5v1UwUfzOEyzD6cSDZIF8Nqhm4KQA6F3yUzrcu74hK"
Setting SECRET_KEY_BASE and restarting ⬢ pure-peak-67829... done, v3
SECRET_KEY_BASE: frVmI4tvRSLJBSH09i657N5v1UwUfzOEyzD6cSDZIF8Nqhm4KQA6F3yUzrcu74hK

Deploying To Heroku

Let us commit all our changes up until now.

$ git add .
$ git commit -am "Preparing app for Heroku"
$ git push heroku master

Once the deploy is done we can setup the database with

$ heroku run "POOL_SIZE=2 mix ecto.migrate"
Running POOL_SIZE=2 mix ecto.migrate on ⬢ pure-peak-67829... up, run.4676 (Free)
12:10:30.567 [info] == Running DemoHerokuDeployment.Repo.Migrations.AddTest.change/0 forward
12:10:30.570 [info] == Migrated in 0.0s

NOTE the above command might fail if you don't have a migration.

And we can see our Phoenix application in the browser by typing.

$ heroku open

Drawbacks of Elixir on Heroku

Heroku has some drawbacks as well, viz:

  1. Heroku limits the number of connections.
  2. Since each dyno has it's own network interface (Router) clustering of applications becomes hard with standard or built-in tools and we will have to resort to Redis to achieve this.
  3. State of the application that is stored in memory will be lost every 24 Hours as each dyno will be restarted once in 24 Hours.
  4. It also does not allow SSH access thus making it impossible to use Elixir's Remote Shell which helps in debugging a lot of production scenarios.

If your requirement needs any of the above then it would be best to deploy your Elixir - Phoenix applications to a cloud platform such as EC2 or Digital Ocean etc. which we will cover in the subsequent parts of this tutorial series.

Published in | Tagged with






Talk to us, that's always a good idea!