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 deploy
user’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
.