Deploying a Phoenix application to Dokku
Introduction
In this tutorial I’m going to show you how easy it is to deploy a Phoenix web application on a server running Dokku.
As an extra bonus, we will also configure our application to use a valid SSL certificate with just a few extra commands.
Prerequisites
- Server running Dokku with SSH configured to connect remotely (I highly recommend using Digital Ocean’s 1-click solution)
- A domain name pointing at your Digital Ocean VPS with a wildcard A record set
- A recent version of Git installed locally
1. Clone the sample application
First, we will need a sample application to deploy to our server. So we will be using Chris McCord’s Phoenix Chat Example.
You can either fork and clone or simply clone this project locally like so:
$ git clone https://github.com/chrismccord/phoenix_chat_example.git
Once we have the project on our machine, we are going to add a new remote URL to the repository.
$ git remote add dokku [email protected]:phoenix
This will set a second remote URL to the repository and allow us to push our updates to our Dokku server. We can see all remotes by running:
$ git remote
dokku
master
2. Configure the application for production
Next, we will have to make a small change to our sample application so that it will work correctly in our production environment.
Since this application does not use a database the only change we require is setting the host
and secret_key_base
to use environment variables we will configure shortly. To fix this, in config/prod.exs
replace:
config :chat, Chat.Endpoint,
http: [port: {:system, "PORT"}],
url: [host: "example.com"]
With:
config :chat, Chat.Endpoint,
http: [port: {:system, "PORT"}],
url: [host: System.get_env("HOSTNAME"), port: 80]
We also need to replace the hardcoded secret key with an environment variable. Open up config/prod.secret.exs
and replace:
secret_key_base: "XR7e8rPXq2nIdBXqtPsyxPz1R1UF3w4HDBFGdxZ..."
With:
secret_key_base: System.get_env("SECRET_KEY_BASE")
In the same file if we had a database to configure, we would also change:
# Configure your database
config :chat, Chat.Repo,
adapter: Ecto.Adapters.Postgres,
username: "postgres",
password: "postgres",
database: "chat_prod"
To:
# Configure your database
config :chat, Chat.Repo,
adapter: Ecto.Adapters.Postgres,
url: System.get_env("DATABASE_URL")
This DATABASE_URL
environment variable is given to us by Dokku when we link a Postgres database to our application.
3. Buildpacks
In order for Dokku to know how to install Elixir and Javascript dependencies and how to run our application, we need to tell it to use an Elixir buildpack.
Fortunately all the hard work of creating these has been done by productive members of the Elixir community.
We need a combination of 2 buildpacks in order to deploy our application. One for Elixir/Phoenix and another for our static assets. To use both of these buildpacks we first create a file in the root of our project named .buildpacks
and add the following lines:
https://github.com/HashNuke/heroku-buildpack-elixir.git
https://github.com/gjaldon/heroku-buildpack-phoenix-static.git
We then configure our phoenix-static
buildpack to use a later version of Node required by Phoenix 1.1 and above.
To do this we create a file phoenix_static_buildpack.config
and add the line below:
node_version=5.3.0
4. Create the Dokku App
We are now ready to create the application in Dokku. We can do this via the dokku-cli
gem, but for now we’ll just SSH into our server to configure the application.
$ ssh [email protected]
~# dokku apps:create phoenix
Creating phoenix... done
We will also add the environment variables we setup before:
~# dokku config:set phoenix SECRET_KEY_BASE=the_value_in_my_prod_secret_file HOSTNAME=phoenix.mydomain.com
-----> Setting config vars
HOSTNAME: phoenix.mydomain.com
SECRET_KEY_BASE: the_value_in_my_prod_secret_file
-----> Restarting app phoenix
App phoenix has not been deployed
4b. Create a database (optional)
Creating a databse on Dokku is very straightforward. First we install the Postgres Dokku plugin if we don’t have it already:
~# dokku plugin:install https://github.com/dokku/dokku-postgres.git postgres
Then we create the database itself:
~# dokku postgres:create phoenix
-----> Starting container
Waiting for container to be ready
Creating container database
Securing connection to database
=====> Postgres container created: phoenix
DSN: postgres://postgres:714bcafb6094059fe476dc82c80a91a6@dokku-postgres-phoenix:5432/phoenix
We can then link this container to our application with:
~# dokku postgres:link phoenix phoenix
-----> Setting config vars
DATABASE_URL: postgres://postgres:714bcafb6094059fe476dc82c80a91a6@dokku-postgres-phoenix:5432/phoenix
-----> Restarting app phoenix
...
=====> Application deployed:
https://phoenix.mydomain.com
The format is postgres:link <name> <app>
where name
is the name of the database and app
is the name of the application.
We can also see that our DATABASE_URL
environment variable has been set and is available to our application.
5. Push the application
Now on our local machine, we can now push the application to Dokku which will configure and deploy it:
$ git push dokku
-----> Cleaning up...
-----> Building phoenix from herokuish...
-----> Adding BUILD_ENV to build environment...
-----> Multipack app detected
=====> Downloading Buildpack: https://github.com/HashNuke/heroku-buildpack-elixir.git
=====> Detected Framework: elixir
-----> Checking Erlang and Elixir versions
...
-----> Running nginx-pre-reload
Reloading nginx
-----> Setting config vars
DOKKU_APP_RESTORE: 1
=====> Application deployed:
http://phoenix.mydomain.com
To [email protected]:phoenix
* [new branch] master -> master
If we navigate to http://phoenix.mydomain.com we will see the sample application running.
Pretty sweet.
6. Bonus - SSL Encryption
Previously, adding SSL to an application was a sometimes tedious and pricey endeavour. But thanks to the folks at Let’s Encrypt the process has been simplified enormously.
Let’s add SSL to our sample application.
To do this we need to install the Let’s Encrypt plugin for Dokku. So on your server run:
~# dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git
We then need to set an environment variable for our email, which is required to issue an SSL certificate:
~# dokku config:set --no-restart phoenix DOKKU_LETSENCRYPT_EMAIL=[email protected]
-----> Setting config vars
DOKKU_LETSENCRYPT_EMAIL: [email protected]
Now to add a certificate to your application we simply run:
~# dokku letsencrypt phoenix
-----> Enabling ACME proxy for phoenix...
-----> Getting letsencrypt certificate for phoenix...
- Domain 'phoenix.mydomain.com'
-----> Certificate retrieved successfully.
-----> Symlinking let's encrypt certificates
...
-----> Setting config vars
DOKKU_NGINX_SSL_PORT: 443
-----> Configuring SSL for phoenix.mydomain.com...(using /var/lib/dokku/plugins/available/nginx-vhosts/templates/nginx.ssl.conf.template)
-----> Creating https nginx.conf
-----> Running nginx-pre-reload
Reloading nginx
-----> Disabling ACME proxy for phoenix...
done
Now we can visit our site securely at https://phoenix.mydomain.com and see the valid SSL certificate in action: