Deploy a Phoenix app with Dokku and Digital Ocean

Created: April 26, 2019 12:45 | Updated: November 27, 2019 07:47
Tags: Dokku, Digital Ocean, Postgres, Elixir, Phoenix

Credit to Jon Lunsford

Buy a domain name

I use Namecheap and have been happy with them, I don't know if they are the best but they are certainly not the worst and I have had no issues with them. Just stay away from GoDaddy.

Point your Domain name at Digital Ocean's DNS

We are doing this now because it can take a while for the settings to distribute across the web. In Namecheap I selected "Custom DNS" under Nameservers and put in Digital Ocean's 3 name servers:

  • ns1.digitalocean.com
  • ns2.digitalocean.com
  • ns3.digitalocean.com

I really like how creative they were with the names.

Sign up for Digital Ocean

Sign up for Digital Ocean using this link to get $100 in free credits over 60 days. Full disclosure: Once you spend $25 on their service I will get a $25 credit on my service. And I like free credits. I'd also be really happy that someone read this post. So please use the link!

Create a Droplet

You can create a droplet (read: virtual private server) with Dokku pre-installed! When creating a droplet choose Dokku under the One-Click-Apps tab, add your SSH keys and boot that puppy up.

Finish Dokku Setup

Do this right away, if someone else beats you to it then they will have access to your server instead of you.

Navigate to your server's IP address (listed in Digital Ocean) and finish the setup on the admin page that appears. You will have to paste in your public ssh key again and I recommend using virtual host naming for your apps. It means that if you create an app called myapp, it will be accessible at myapp.mydomain.com

Update your server

Trust me. I spent an hour troubleshooting a new server because I forget this step!

sudo apt update
sudo apt upgrade

Create an app on your server

SSH onto your server

ssh root@your.droplet.ip.address

Create an app

dokku apps:create awesomeapp

Install Postgres, create and link database

dokku plugin:install https://github.com/dokku/dokku-postgres.git
dokku postgres:create awesomeappdb
dokku postgres:link awesomeappdb awesomeapp

Create a swap file to help out on the ram front. You will only see output after the 3rd line.

fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile

Open fstab with Nano

nano /etc/fstab

Add this line at the bottom

/swapfile none swap sw 0 0

And then use CTRL+x, y, and then enter to save and exit.

Change file upload limit from 1mb to 50mb

Add to /etc/nginx/conf.d/dokku.conf

client_max_body_size 50m;

and then reload nginx

nginx -s reload

Configure Digital Ocean's DNS

Add the domain that you purchased on namecheap and you should see 3 entries pointing at each of Digital Ocean's cleverly named servers.

Now you need to create 'A' records for your domains. I created the following:

Type Hostname Value
A mydomain.com (select your server)
A www.mydomain.com (select your server)
A awesomeapp.mydomain.com (select your server)

etc.

Install dokku-cli

dokku-cli is very similar to the Heroku command line tool and is a joy to use. No configuration is needed! Just run in your project directory and use 'dokku' in the place of 'heroku'.

Install Dokku CLI

gem install dokku-cli

Make a new Phoenix App

mix phx.new awesomeapp
cd awesomeapp
mix deps.get

Configure your Phoenix Endpoint

# config/prod.exs

config :live_view_counter, LiveViewCounterWeb.Endpoint,
  http: [:inet6, port: System.get_env("PORT")],
  url: [host: System.get_env("WEB_HOST"), port: 5000],
  load_from_system_env: true,
  cache_static_manifest: "priv/static/cache_manifest.json"

Configure Secrets

Configure config/prod.secret.exs to use env variables, this way we can safely check the file into source control

# config/prod.secret.exs

use Mix.Config

# In this file, we keep production configuration that
# you'll likely want to automate and keep away from
# your version control system.
#
# You should document the content of this
# file or create a script for recreating it, since it's
# kept out of version control and might be hard to recover
# or recreate for your teammates (or yourself later on).
config :live_view_todos, LiveViewTodosWeb.Endpoint,
  secret_key_base: System.get_env("SECRET_KEY_BASE")

# Configure your database
config :live_view_todos, LiveViewTodos.Repo,
  username: "postgres",
  password: "postgres",
  database: "awesomeapdb",
  url: System.get_env("DATABASE_URL"),
  pool_size: 15

Remove secrets from .gitignore

Comment out the following line in .gitignore so we can include it in git

# .gitignore

# Alternatively, you may comment the line below and commit the
# secrets files as long as you replace their contents by environment
# variables.
# /config/*.secret.exs

Create a Procfile

This goes in your root directory, telling Dokku how to start your server

# Procfile

web: ./.platform_tools/elixir/bin/mix phx.server

Create a buildpacks file

This goes in your root directory, telling Dokku which packs to use

# .buildpacks (DON'T LEAVE THIS COMMENT IN YOUR FILE)

https://github.com/hashnuke/heroku-buildpack-elixir
https://github.com/gjaldon/heroku-buildpack-phoenix-static

Create elixir_buildpack.config

This goes in your root directory, telling Dokku which Elixir and Erlang versions to use. These are the latest versions as of this writing.

# elixir_buildpack.config

# Erlang version
erlang_version=21.3.7
# Elixir version
elixir_version=1.8.2

Create a phoenix_static_buildpack.config file

This goes in your root directory, more information here

# phoenix_static_buildpack.config

# Use phoenix 1.3 executable
phoenix_ex=phx

Automatic Migrations

This one just runs mix ecto.migrate automatically.

Create app.json in the root directory of your app

{
  "name": "awesomeapp",
  "description": "My awesome Phoenix app, running on Dokku!",
  "keywords": [
    "dokku",
    "elixir",
    "phoenix"
  ],
  "scripts": {
    "dokku": {
      "postdeploy": "mix ecto.migrate"
    }
  }
}

Add remote repository

Navigate to your awesomeapp project directory, initialize git and add the repository

git init
git add .
git commit -m 'new phoenix app'
git remote add dokku dokku@your.droplet.ip.address:awesomeapp

Set your environment variables

dokku config:set MIX_ENV="prod" PORT=5000 SECRET_KEY_BASE="`mix phx.gen.secret`" WEB_HOST="awesomeapp.yourdomain.com"

Push your code

git push dokku master

Migration

Migrate your database

dokku run mix ecto.migrate

Free SSL with Let's Encrypt

Let's Encrypt provides free SSL certificates. You can find more complete instructions and explanations here.

SSH onto your server

ssh root@your.droplet.ip.address

Install Let's Encrypt

dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git

Update if it was already installed

dokku plugin:update letsencrypt

Set your email address (note that you need to change MYAPP and ME@MYEMAIL.COM)

dokku config:set --no-restart awesomeapp DOKKU_LETSENCRYPT_EMAIL=ME@MYEMAIL.COM

Turn it on

dokku letsencrypt awesomeapp

Set up auto-renewal with a cronjob

dokku letsencrypt:cron-job --add

Backup your databases

I wrote a shell script for this very issue. It downloads all your Postgres databases from Heroku and Dokku and uploads them (conveiently dated) to Google Drive. Check it out and let me know what you think!

Written by Alan Vardy. Let me know how I can make this better!