Skip to content

Deploy a Phoenix application to a VPS

Short guide on how to deploy a Phoenix app to Linode/DigitalOcean by hand.

3 min read

Prerequisites

First, we need a Phoenix app.

The application we'll be deploying is the one from:
Building a JSON API in Elixir with Phoenix .

I'll show the steps needed to have this app available at: http://lobotuerto.com:4000

We'll go with Ubuntu for the OS.
Also, we should have a domain already configured on that machine.

So, when trying something like this, it should respond as expected:

Shell command
curl -H "Content-Type: application/json" -X POST \
-d '{"email":"asd@asd.com","password":"qwerty"}' \
http://lobotuerto.com:4000/api/users/sign_in \
-c cookies.txt -b cookies.txt -i

OK, SSH into your VPS then follow the steps below.

Configure the firewall

Shell command
sudo ufw enable
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https

Install PostgreSQL on the VPS

Follow this guide: How to install PostgreSQL in Linux .

Make sure to create a DB user for your app ---it's on the guide.
In this example the DB user will be called deployer and will have a password deployer123.

Install dependencies for Erlang and Elixir

Shell command
sudo apt install build-essential autoconf m4 libncurses5-dev libwxgtk3.0-dev \
libgl1-mesa-dev libglu1-mesa-dev libpng-dev libssh-dev unixodbc-dev

Create a user for deployment

Shell command
sudo adduser deployer

Install Erlang and Elixir

Impersonate the user with:

Shell command
sudo -i -u deployer

This is done so the deployer user have access to the Elixir executable.

Follow this guide: How to install Elixir in Linux .

Clone your app repo

Or transfer the code using scp.

Shell command
git clone https://github.com/lobo-tuerto/my-phoenix-json-api

Install the app dependencies

Shell command
cd my-phoenix-json-api
mix deps.get

Adjust the config files

Comment out this code in config/prod.exs:

config/prod.exs
-config :my_app, MyAppWeb.Endpoint,
- url: [host: "example.com", port: 80],
- cache_static_manifest: "priv/static/cache_manifest.json"
+
+# config :my_app, MyAppWeb.Endpoint,
+# url: [host: "example.com", port: 80],
+# cache_static_manifest: "priv/static/cache_manifest.json"

Uncomment this code from config/prod.secret.exs:

config/prod.secret.exs
-# config :my_app, MyAppWeb.Endpoint, server: true
+config :my_app, MyAppWeb.Endpoint, server: true

Setup the DB

Execute the following three commands:

Shell command
SECRET_KEY_BASE="`mix phx.gen.secret`" \
DATABASE_URL="ecto://deployer:deployer123@localhost/myapp_prod" \
MIX_ENV=prod mix ecto.create
SECRET_KEY_BASE="`mix phx.gen.secret`" \
DATABASE_URL="ecto://deployer:deployer123@localhost/myapp_prod" \
MIX_ENV=prod mix ecto.migrate
SECRET_KEY_BASE="`mix phx.gen.secret`" \
DATABASE_URL="ecto://deployer:deployer123@localhost/myapp_prod" \
MIX_ENV=prod mix release

Let's create a couple of users to try it out.

Shell command
_build/prod/rel/my_app/bin/my_app start_iex
iex> MyApp.Account.create_user(%{email: "asd@asd.com", password: "qwerty"})
iex> MyApp.Account.create_user(%{email: "some@email.com", password: "some password"})

This could have been done using the priv/repo/seeds.exs file too, but I wanted to showcase how to open an IEx session on the production app.

Now, without exiting that IEx session, go ahead and try this:

Shell command
curl -H "Content-Type: application/json" -X POST \
-d '{"email":"asd@asd.com","password":"qwerty"}' \
http://lobotuerto.com:4000/api/users/sign_in \
-c cookies.txt -b cookies.txt -i

You should see:

Terminal output
HTTP/1.1 200 OK
cache-control: max-age=0, private, must-revalidate
content-length: 48
content-type: application/json; charset=utf-8
date: Mon, 29 Jul 2019 22:15:13 GMT
server: Cowboy
x-request-id: FbYAie3B0hrQ5-gAAAPF
set-cookie: _my_app_key=SFMyNTY.RgcVe-7fJ-NqSO5DQL; path=/; HttpOnly
{"data":{"user":{"email":"asd@asd.com","id":1}}}

Awesome! 🎉

Further reading on releases and deployments

More posts connected by shared tags.