Deploy Early and Often: Deploying Phoenix with Edeliver and Distillery (Part One)

Overview

We will be setting up a Phoenix production environment on Ubuntu 16.04 LTS and deploy a simple Phoenix application using Edeliver and Distillery. We’ll be setting up a Droplet on DigitalOcean.

We will be compiling and deploying on the same server.

What you’ll need:

  • An Elixir environment set up on your computer
  • Any domain or subdomain

Background

When it comes to learning Phoenix, deploying your application to a VPS is probably the most challenging part. I had developed an application with Programming Phoenix as a guide, and I wanted to deploy the finished application to a server. To my chagrin, it was almost impossible to debug the various cryptic error messages that came up. I realised that leaving deployment till last was a bad idea — the application had accumulated enough complexity that I was at a loss as to how to fix it. With so much technical debt, it was impossible to gauge which part of the process had gone wrong. It would have been easy to pinpoint errors had I deployed the application regularly from the beginning.

Whenever you develop a Phoenix application for any real-world use, remember to deploy early and often. This will save you hours of head scratching and hair pulling.

While there are a number of popular articles about deploying Elixir releases with Edeliver and Distillery, I had to piece together several different guides along with the Edelivery documentation in order to deploy successfully. Hopefully, this will serve as a “golden path” guide that would serve the needs of most small Phoenix applications.

Our Phoenix Application

Let’s start by creating a new Phoenix project. (You can skip ahead if you’re familiar with the basics)

mix phoenix.new deploy_phoenix
cd deploy_phoenix

We’ll set up git:

git init
git add .
git commit -m "initial commit"

Set up your database. Open config/dev.exs and config/test.exs and edit the database configuration, then run:

mix ecto.create

Next, run:

mix phoenix.server

Visit localhost:4000 in your browser. You should see the default Phoenix homepage.

Now, add the dependencies edeliver and distillery to mix.exs.

Run mix deps.get to fetch the dependencies.

Our DigitalOcean VPS

Let’s set up our DigitalOcean VPS. (If you aren’t already using DO, you’ll get $10 in credit via this referral link.)

Click Create Droplet on your DigitalOcean dashboard, and select:

  • Ubuntu 16.04.2 x64
  • 1GB/1CPU ($10/mo)
  • Any datacenter region
  • Under “Select additional options”: none are necessary
  • Your SSH key

Enter any hostname (for this guide, we’ll use deployphoenix), then click Create. Copy the ipv4 address, e.g. 188.166.182.170 (replace with your own).

SSH into the server:

ssh root@188.166.182.170

Let’s create a deploy user that we will subsequently log in with. You’ll be prompted for a password and other details for the user.

adduser deploy

Next, we’ll add the deploy user to the sudo group.

usermod -aG sudo deploy

Now, we’ll copy the SSH keys over to the deployuser’s home directory.

su - deploy
mkdir .ssh
sudo cp /root/.ssh/authorized_keys ~/.ssh/authorized_keys
sudo chown `whoami` ~/.ssh/authorized_keys

Let’s try SSH’ing in using a new shell window.

ssh deploy@188.166.182.170

Locking down the root account

Let’s perform some basic server hardening, starting with preventing SSH access to the root account.

Edit the sshd_config file with sudo vim /etc/ssh/sshd_config (or use sudo nano). Change the line

PermitRootLogin yes

to

PermitRootLogin no

and save the file. Then, run:

sudo service sshd restart

Running updates

We’ll now update system packages before we proceed.

sudo apt-get update
sudo apt-get upgrade

Setting up a firewall

Next, we’ll set up UFW.

sudo ufw app list    # You should see OpenSSH listed
sudo ufw allow OpenSSH
sudo ufw enable
sudo ufw status # Status: active

Installing PostgreSQL

Let’s install PostgreSQL.

sudo apt-get install postgresql-9.6

You may see an error like the following:

E: Unable to locate package postgresql-9.6
E: Couldn’t find any package by glob ‘postgresql-9.6’
E: Couldn’t find any package by regex ‘postgresql-9.6

Let’s add the Postgres Apt repository. (I found this solution on the AskUbuntu site)

sudo add-apt-repository "deb http://apt.postgresql.org/pub/repos/apt/ xenial-pgdg main"
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt-get update
sudo apt-get install postgresql-9.6

Now that we’ve installed PostgreSQL, let’s create a database.

sudo -i -u postgres
psql

This opens a psql console.

postgres=# CREATE DATABASE deployphoenix_prod;
postgres=# CREATE USER deployphoenix WITH PASSWORD 'mypassword';
postgres=# GRANT ALL PRIVILEGES ON DATABASE deployphoenix_prod TO deployphoenix;

We’ve just created a database that we’ll later configure Phoenix to use. Type in \q to quit the psql console, then exit to return to the deploy user shell.

Installing NodeJS

Since we’re using vanilla Phoenix for this guide, that means we’re using Brunch for asset compilation. Brunch requires npm, and npm requires Node.js. Let’s install that now.

First, to add the Node.js PPA maintained by NodeSource, we use curl to retrieve the installation script.

curl -sL https://deb.nodesource.com/setup_6.x -o nodesource_setup.sh

Then, we run the script:

sudo bash nodesource_setup.sh

The PPA will have been added, and the local package cache will be updated as well. Now, we can install Node.js.

sudo apt-get install nodejs

This installs npm as well. We will also need the build-essential package:

sudo apt-get install build-essential

Let’s step back for a moment and review what we’ve got so far:

  • A bare-bones Phoenix application
  • An Ubuntu server ready for compiling and deploying

Next Up

Give yourself a pat on the back if you’ve followed along so far. In Part 2, we will continue our deployment process. We will build and deploy a release, then get it to play nice with nginx.

Part 2 is now up!