Dokku aims to be a simple Heroku-like PaaS (Platform-as-a-Service) that you can use to run your applications on your own server.

Installing/configuring Dokku

For the purposes of this guide, we'll use a new Ubuntu 16.04 1GB RAM instance. When your instance is running, connect to it via SSH using ssh civo@my.ip.addr.ess and then run the following (from Dokku's main documentation site).

wget \
    https://raw.githubusercontent.com/dokku/dokku/v0.12.7/bootstrap.sh
sudo DOKKU_TAG=v0.12.7 bash bootstrap.sh

IMPORTANT: Once bootstrapping completes, there's a known bug with Docker, where it doesn't correctly set the MTU, so before continuing follow this helpful guide on how to fix Docker networking on Civo.

Then you need to go to http://my.ip.addr.ess in a browser to finish the installation. Verify that the SSH key is correct (the one you used to connect to the instance, should be automatically copied in to Dokku), give it a hostname and choose to "Use virtualhost naming for apps". For example, a good hostname would be something like app.example.com. Then click Finish Setup which completes the installation process. You should then setup a DNS A record pointing to my.ip.addr.ess for your app.example.com, and setup *.app.example.com to point to the same IP address.

Our Rails application

We're going to setup a MySQL connected Ruby on Rails application, on a custom domain name and protected with a Let's Encrypt SSL certificate to provide HTTPS support. Let's create a new Ruby on Rails application (on your local machine):

rails new sample -d mysql

We need to use the rails_12factor gem in order to easily help our application work in a 12 Factor style. Edit the Gemfile and add the line:

gem 'rails_12factor', group: :production

and then run bundle install. We need this to be a git repository in order to deploy it, so let's do that now.

git init  
git add .  
git commit -am "Initial commit"

Normally at this point you'd add a regular Git origin server and push it up to there, but we'll let you figure that out.

Creating a database

Dokku has a MySQL plugin but for now we're going to install MySQL in the base operating system and manually administer the databases. The reason is that it's easier to do this and backup the databases by normal mechanisms.

On the server run the following:

apt-get install mysql-server mysql-client  
nano -w /etc/mysql/mysql.conf.d/mysqld.cnf  
# Change "bind-address = 0.0.0.0"
service mysql restart  

mysql -u root -p mysql  
mysql> create database ruby_rails_sample;  
mysql> grant all on ruby_rails_sample.* to rails@'%' identified by 'password';

Deploying to Dokku

The default solution for running Dokku management commands is to either SSH to the server to run the commands, or each time you type them prefix them with ssh -t dokku@app.example.com. There is a nicer solution though dokku_client.sh. This is a contributed script in the Dokku repository that you put in your $PATH (e.g. maybe call it dokku and put it in /usr/local/bin/) and make executable with chmod +x /usr/local/bin/dokku.

You can then use this script to create applications and administer them from your local machine. To create an application it needs to know your Dokku host, so set an environment variable in your ~/.bash_profile or ~/.zshrc called DOKKU_HOST. After creating an application, it will then work using the git remote that it creates to know which Dokku host it should connect to. You can use it exactly the same as if you had SSHed to the server and run the commands on there.

You can test this (after downloading it, putting it in the right place, with the right name, making it executable and setting the DOKKU_HOST environment variable) by using dokku apps and it should show =====> My Apps. Now we'll create an application in Dokku for our Rails app and link it to our MySQL database:

dokku apps:create ruby-rails-sample  
dokku config:set ruby-rails-sample \
  DATABASE_URL=mysql2://rails:password@app.example.com/ruby_rails_sample

We can deploy our application with:

git remote add dokku dokku@app.example.com:ruby-rails-sample  
git push dokku master

At this point, it'll be working but without a route for / within the application, it will appear broken. As writing a Rails application isn't the point of this post, we'll leave that for you to do on your own.

Changing the domain name

Now it's running, we have two steps to make it production ready - we need to set the domain name to one that doesn't end in app.example.com (we'll use sample.example.com for now):

dokku domains:add ruby-rails-sample sample.example.com

This works perfectly fine now if you hit http://sample.andyjeffries.co.uk in a browser. We should clean up and remove the auto-generated app.example.com subdomain though:

dokku domains:remove ruby-rails-sample ruby-rails-sample.app.example.com

Now we need to protect the website using HTTPS.

Enabling TLS provided by Let's Encrypt

Let's Encrypt provides free TLS (used to be called SSL) certificates for hosting websites. Bargain! On the server run the following to install the Dokku Let's Encrypt plugin:

ssh civo@app.example.com  
sudo dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git

Now we can enable letsencrypt for our application:

dokku config:set --no-restart ruby-rails-sample \
  DOKKU_LETSENCRYPT_EMAIL=your@email.address  
dokku letsencrypt ruby-rails-sample

Let's Encrypt certificates have a short expiry, so we need to setup a cron job to automatically renew them:

dokku letsencrypt:cron-job --add

Now our site works with HTTPS using the URL: https://sample.example.com or http://sample.example.com. It's left as an exercise to the reader to configure the Rails application to ensure it redirects to HTTPS (hint config.force_ssl = true).

Switching to a Dokku-based MySQL service (optional)

Dokku can actually run a number of services directly that can be created via the dokku command line. One of them is MySQL. Personally we prefer managing this outside of Dokku, but for completeness we'll describe the process here. First off, let's remove the locally installed MySQL server.

apt-get remove mysql-server

Now let's install the MySQL plugin by running this command on the server:

sudo dokku plugin:install https://github.com/dokku/dokku-mysql.git mysql

Now we can create a database using the plugin and link it to our application:

dokku mysql:create ruby-rails-sample-db

There is a dokku mysql:link command, but for some reason this creates a config setting called DOKKU_MYSQL_FUCHSIA_URL which is weird and should be DATABASE_URL for automatic linking using the 12-factor gem, so get using the value for Dsn output by running dokku mysql:create, change the mysql://to mysql2:// because Rails requires that now the database adaptor gem is mysql2 and set it as the correct key:

dokku config:set ruby-rails-sample DATABASE_URL=mysql2://...

Custom readiness checks

During deployment Dokku will automatically wait for the process to run before switching over web traffic to the new process, but that doesn't mean it's ready to serve requests nor that it will stay running. There is a great supported mechanism for running custom checks against your application before switching over in a zero-downtime style. Create a file in the root of your project called CHECKS with content like this:

WAIT=3 # Wait 3 seconds between each attempt  
ATTEMPTS=10 # Try 10 times total before abandoning  
/ Welcome to my site
/application.js $(function() {

There are reports that if it doesn't work for you, ensure you have a blank line at the end of the file.

Also note this won't work with our sample application, because we don't currently have a route for root within config/routes.rb that will respond with a page containing "Welcome to my site".

Migrations

Most Rails applications use ActiveRecord to store content in a database, which brings with it the joy of running migrations. Dokku has built in support for running any commands necessary after deployment and before the checks/switchover is done.

To do this, simply create an app.json file in the root of your project with the following content:

{
  "name": "ruby-rails-sample",
  "description": "A sample Ruby on Rails application",
  "scripts": {
    "dokku": {
      "predeploy": "bundle exec rake db:migrate"
    }
  }
}

This structure is based on Heroku's app.json, but the only supported keys are scripts.dokku.postdeploy and scripts.dokku.predeploy.

Persistent storage

While it goes against 12 Factor principles, there may be times when you're using a gem like Paperclip, without attaching it to external storage such as Amazon's S3 but you don't want all the uploaded files disappearing on each deploy (shocking, I know!). The way to do this is by creating a local folder and mounting it in to your application. On the server run:

mkdir /var/lib/dokku/data/ruby-rails-sample-images  
chown dokku /var/lib/dokku/data/ruby-rails-sample-images  
dokku storage:mount ruby-rails-sample /var/lib/dokku/data/ruby-rails-sample-images:/app/public/system

You can mount the same mount point to multiple applications if you want to share files between applications.

It's important to note that this is only attached during build and run processes, so after mounting the storage you need to build a new version.