Subscribe to access all episodes. View plans →
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.
Denis Klimenko
7 years agoThank you!)
dapperAuteur
7 years agoThank you for putting this together. It’s easy to follow. I could use some help. I’m getting an error when loading environment variables “ (Mix.Config.LoadError) could not load config config/prod.secret.exs remote: * (File.Error) could not read file “/tmp/build_1c35240d95f137d79fb0adc0f4218131/config/prod.secret.exs”: no such file or directory” Have you seen that? The config/prod.secret.exs exists and I followed the instructions to tell it to look in config/prod.exs
Alekx
7 years agoThank you for the kind words.
Is the
import_config "prod.secret.exs"
line removed or commented out in ‘config/prod.exs’?dapperAuteur
7 years agoI made another attempt and got it deployed. thanks for replying to my previous comment and making the video. It’s been a long road to getting an Elixir app deployed. I’m ready to actually put some code into the world.
Alekx
7 years agoThat’s great to hear!
Mohamad Fadhil
3 years agoYou’ll need to add the port settings in
config/prod.exs
file like the following:Otherwise, you’ll get this error in Heroku after deploying the app.
Reference: https://github.com/gjaldon/heroku-buildpack-phoenix-static/issues/73