Introduction

In this post, I’m going to explain how to deploy an Elixir Phoenix application on a VPS. We are going to use Elixir Release Manager (Exrm) for generating releases and all the necessary files to deploy our application.

This is only an example to show how to do it for the first time. I recommend using a newly created application to test your setup.

To run the application on the VPS, we are going to use the HTTP server cowboy written in Erlang.

Here is the list of tasks that need to be done to make the deployment successful:

1
2
3
4
5
6
7
8
Find a VPS to host our application
Configure the VPS for hosting the application
Add exrm to application dependencies
Update the application production configuration file
Compile the assets
Compile the application
Make a release
Deploy the release

Find a VPS

You can use any VPS you want from any provider. The only thing you need to have is an ssh access with sudo. For this example we are going to use a debian based linux distros.

If you are not sure about your server security, I recommend using a provider with a built-in firewall. AWS, Google Cloud or others have built-in firewall on the platform to protect your exposed server.

Only open the ports you need. Do not expose your ssh server to everybody.

Once you have a secured ssh access to your VPS, you are ready for the next task.

Configure the VPS

To host the application on the VPS, you have nothing to do. An exrm release has everything inside to make it run.

Dependencies

We need to add exrm to our list of dependencies in our mix.exs file to be able to generate a release.

defp deps do
  [{:phoenix, "~> 1.1.4"},
   {:postgrex, ">= 0.0.0"},
   {:phoenix_ecto, "~> 2.0"},
   {:phoenix_html, "~> 2.4"},
   {:phoenix_live_reload, "~> 1.0", only: :dev},
   {:gettext, "~> 0.9"},
   {:cowboy, "~> 1.0"},
   {:exrm, "~> 1.0.8"}]
end

We can now do a mix do deps.get, compile to fetch the newly added dependency and compile everything.

Production Configuration File

Ok, now we need to verify our production configuration file and set the http port. By default, the endpoint is going to use an environment variable called PORT on the system.

If you want to use this, the variable needs to be present on the machine you use to make a release. Otherwise, the build is going to fail.

I like to setup my endpoint differently by setting the port directly on the configuration file. This is a personal choice.

The original code should look like this:

config/prod.exs

config :elixir_phoenix_deploy_example, ElixirPhoenixDeployExample.Endpoint,
  http: [port: {:system, "PORT"}],
  url: [host: "example.com", port: 80],
  cache_static_manifest: "priv/static/manifest.json"

I’m going to setup the port to 80 for this example:

config :elixir_phoenix_deploy_example, ElixirPhoenixDeployExample.Endpoint,
  http: [port: 80],
  url: [host: "example.com", port: 80],
  cache_static_manifest: "priv/static/manifest.json"

We also need to uncomment the following line to instruct Phoenix to start the server for all endpoints:

## Using releases
#
# If you are doing OTP releases, you need to instruct Phoenix
# to start the server for all endpoints:
#
    config :phoenix, :serve_endpoints, true
#
# Alternatively, you can configure exactly which server to
# start per endpoint:
#
#     config :elixir_phoenix_deploy_example, ElixirPhoenixDeployExample.Endpoint, server: true

Static Assets Pre-Compile

We need to pre-compile our production static assets before creating our release. You can do this with the following command:

brunch build --production && MIX_ENV=prod mix phoenix.digest

Note: You need to have brunch installed globally for this command to work.

npm install -g brunch

Compiling the Application

It is a good idea to compile the application to see if everything is working correctly. You can compile the application with the following command:

MIX_ENV=prod mix compile

Generating a Release

To generate a release we need to execute the command:

MIX_ENV=prod mix release

That’s it. We have a release ready for deployment. Nice! :)

Note: If you are getting an error and you setup your production configuration file with a PORT environment variable, you need to setup the variable on your machine.

Uploading the Release

Now that we have a release, located at:

rel/<project_name>/releases/<version>/<project_name>.tar.gz

we can easly upload it to our server. On way is by using the scp command like this:

scp rel/<project_name>/releases/<version>/<project_name>.tar.gz <your_ssh_connection>:/home/<server_username>

Installing the Application on the Server

To install the application on the server we need to execute the following commands:

# Connect to your server using ssh then
sudo mkdir -p /srv/app
sudo chown <server_username>:<server_username> /srv/app
cd /srv/app
tar -xfz /home/<server_username>/<app_name>.tar.gz

# Start the application (port 80 needs root privileges)
sudo sh /srv/app/bin/<app_name> start

# Stop the application
sudo sh /srv/app/bin/<app_name> stop

You can find the original documentation about Exrm here: https://hexdocs.pm/exrm

Accessing the Application

You can now access your Elixir Phoenix application on the IP:PORT you have selected. Remember, this is only a start. You have to think about migration, server restart and security too. I usually end up with a bash script that do tasks for me and another script to boot the server on restart of the host.

Anyway, That’s it, Happy Coding! :)