In the previous articles, we have been building the Prater chat app. It is a time to deploy it now.
There is a bunch of different ways to deploy Phoenix applications.
I would like to start from the simplest approach and deploy the app to Heroku.
For those who are not familiar with Heroku. It is a cloud-based Platform as a Service.
It allows deploying web applications for many different programming languages.
Once you configure the deployment for your app, every new deploy would be triggered by simply run git push heroku master
.
That is why it’s so easy to use Heroku, especially for demo and prototype apps.
Let’s get started.
In order to configure the deployment process to Heroku, we would need to have several tools in our pocket.
Requirements
- Git - I suppose you already have it
- Heroku account - You can create it here if you don’t have it.
- Heroku CLI
- The Phoenix app - The app we want to deploy. I will use the Prater app for that.
Once you have everything ready, make sure you are signed in, by running
→ heroku login
in your terminal window.
Also, navigate to the directory with your project you want to deploy.
Create Heroku app
Now we have everything we need to start.
At first, we need to create a Heroku application. We do it by running the following command.
→ heroku apps:create prater-app --buildpack "https://github.com/HashNuke/heroku-buildpack-elixir.git"
We can optionally pass the name of the application. I am using “prater-app” for that. If you won’t provide the name, the Heroku will assign a random name to your application.
We should specify the buildpack for the application.
Buildpacks are basically a set of scripts which prepares your application and helps to deploy it to Heroku. There is no official Elixir buildpack at the moment. So we should use a custom one.
As soon as we have static assets in our application, we need to use an additional buildpack in order to compile them.
→ heroku buildpacks:add https://github.com/gjaldon/heroku-buildpack-phoenix-static.git
Now, almost every application uses the external dependencies in order to work. One of those dependencies is most likely a database. We have it as well.
In order to work with dependencies, Heroku provides the Add-ons tooling. Where we can install those dependencies as Add-ons to our Heroku applications.
We need an add-on for PostgreSQL database and we are going to use free “Hobby Dev” plan.
→ heroku addons:create heroku-postgresql:hobby-dev
The “Hobby Dev” plan has a limit for the number of DB connections set to 20. We need to stay below that limit.
So let’s define POOL_SIZE
environment variable for later use.
→ heroku config:set POOL_SIZE=18
The Phoenix application also requires us to provide the “secret key base” token in order to securely verify the integrity of signed cookies.
We don’t need to think of ways to create that string, because the phx.gen.secret
helps us to generate that.
Let’s define an environment variable for that:
→ mix phx.gen.secret
→ heroku config:set SECRET_KEY_BASE="`mix phx.gen.secret`"
Configure config files
Our Heroku application is almost ready. Now we need to update our config files to make our Phoenix app work after deploy.
First, we need to update config/prod.exs
file.
For our Endpoint
configuration we need to update the url
to contain: scheme
as “https”, host
to be the host of your Heroku app (mine is prater-app.herokuapp.com
) and port
to be 443
.
Then we need to create force_ssl
with [rewrite_on: [:x_forwarded_proto]]
and fetch secret_key_base
from the environment variable.
Here is how that section should look:
config :prater, PraterWeb.Endpoint,
load_from_system_env: true,
url: [scheme: "https", host: "prater-app.herokuapp.com", port: 443],
cache_static_manifest: "priv/static/cache_manifest.json",
force_ssl: [rewrite_on: [:x_forwarded_proto]],
secret_key_base: Map.fetch!(System.get_env(), "SECRET_KEY_BASE")
Then we need to create a new section for our Repo
configuration:
config :prater, Prater.Repo,
adapter: Ecto.Adapters.Postgres,
url: System.get_env("DATABASE_URL"),
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
ssl: true
Heroku defines DATABASE_URL
environment variable for us which holds the connection to the database. So here we just use it.
And we fetch the POOL_SIZE
we have defined earlier.
The last piece in that file, we need to remove import_config "prod.secret.exs"
at the bottom. We don’t need it anymore.
Now we are moving to channels/user_socket.ex
file.
Here we need to set the timeout for idle Phoenix connections. We need to do that to prevent Heroku killing them after 55 seconds.
transport :websocket, Phoenix.Transports.WebSocket,
timeout: 45_000
Next, we need to create a Procfile
in the project root directory with the following content:
web: MIX_ENV=prod mix phx.server
As the last step, we can configure Erlang and Elixir versions. That can be done in the elixir_buildpack.config
file.
Let’s create one with the following content:
erlang_version=20.0
elixir_version=1.6.0
The Buildpack we use, provides a bunch of other options as well.
Now everything is ready for deploy. After the update of the configs, we need to create a new commit with those changes.
Deploy the app
To trigger out first deploy we can run:
→ git push heroku master
or, if you’re experimenting in a separate git branch, as I do, you need to run
→ git push heroku <your-branch-name>:master
you can go back to git push heroku master
once you finish your work and merge the stuff into the “master” branch.
If everything went fine, at the end of the output you should see something similar to that:
remote: https://prater-app.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
No errors, now we need to migrate the database:
→ heroku run "POOL_SIZE=2 mix ecto.migrate"
If you have the following error, when trying to run the migrations:
[error] GenServer #PID<0.227.0> terminating
** (RuntimeError) Connect raised a ArgumentError error. The exception details are hidden, as
they may contain sensitive data such as database credentials.
(postgrex) lib/postgrex/utils.ex:67: anonymous fn/1 in Postgrex.Utils.parse_version/1
You may need to update “Postgrex” dependency to fix that issue
→ mix deps.update postgrex
Then commit your changes, push them to the Heroku and try to run Ecto migration again.
If everything went fine, try to open the app in the browser and check if everything works.
Wrapping up
We have learned how to deploy Phoenix applications to Heroku. The reason why I choose the Heroku as the first example is to try something simple before move on.
Heroku might be not a best chose for hosting Elixir applications. It comes with the bunch of limitations. So you need to take them into account if you decide to use Heroku for your apps in production.
But Heroku is a good example just to try it out.
See you next time.