Dokku Your Own Heroku

Before we touch on Dokku, let me introduce you to Heroku. Heroku has been an invaluable part of my toolkit. As a web developer, I can easily deploy an application instance with a simple command.

However deploying on Heroku is not a bed of roses. You don’t have much control over the environment. A few years ago, they claimed that they had an intelligent routing protocol when in fact, they used random routing. Another factor is server location. Heroku has US and EU regions. That means at least 250ms in latency for clients with majority of users in Singapore. Hosting an application server in a Singapore would drop this latency to around 10 - 20ms.

I wanted ease of deployment, similar to that of Heroku, with a server located in Singapore. It is now possible with the release of a stable version of Docker and projects like Deis and Dokku.

Docker

Docker containers are applications isolated in their own environment. Very similar to a lightweight virtual machine. You can distribute them to any machine and they will run without any issues. This makes applications portable and safe.

Deis

Deis makes it easy to deploy and manage applications across multiple servers using Docker and CoreOS. Deis sponsors the development of Dokku. I’m really excited for this project as it is a true Heroku replacement. We won’t be talking about Deis in this tutorial as we only need one server for this project.

Dokku

Dokku is project which allows us to run our own platform as a service with docker. It is extremely easy to set up and use, the benefits of being limited to one server.

Dokku Alt is a fork that provides a complete solution with plugins that are stable. (Update: Dokku-Alt is no longer under active development. I strongly suggest using Dokku instead.)

Setting up

Dokku will work with Singapore servers from Digital Ocean (DO) or Amazon EC2. For this project, we will use DO as a provider.

DO provides a pre-built application image with the latest stable version of Dokku, v0.2.3, installed. For something quick and ready to go, that should be your choice.

In our case, we needed Dokku Alt (From now on, we’ll refer to it by it’s shortname Dokku), so we’ll have to spin up a vanilla Ubuntu 14.04 image. Dokku comes with a simple bootstrapping script.

However, before we start installing Dokku, take note that Ubuntu’s repository for Nginx is slightly out of date. Let’s add an up to date repository to ensure the Nginx instance installed is updated to the latest version.

$ sudo -s
# nginx=stable # use nginx=development for latest development version
# add-apt-repository ppa:nginx/$nginx
# bash -c "$(curl -fsSL https://raw.githubusercontent.com/dokku-alt/dokku-alt/master/bootstrap.sh)"

Follow the instructions from the script to add your public key to dokku, or close it and use this command on your local machine.

$ cat ~/.ssh/id_rsa.pub | ssh username@host access:add

That’s all there is to setting up Dokku! You can now start deploying your first app.

$ git clone https://github.com/heroku/node-js-sample
$ cd node-js-sample
$ git remote add dokku dokku@example.com:node-js-app
$ git push dokku master
Counting objects: 296, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (254/254), done.
Writing objects: 100% (296/296), 193.59 KiB, done.
Total 296 (delta 25), reused 276 (delta 13)
-----> Building node-js-app ...
       Node.js app detected
-----> Resolving engine versions

...
...

-----> Application deployed:
       http://node-js-app.example.com

We’re done! It was easy to set up and get your application up and running. However, with great power comes great responsibility. You now have the responsibility of managing your server’s security, backups and updates.

Follow on for some recommended packages and configurations

Install and configure iptables

Configure iptables according to your server requirements.

Configure ssh to deny all password authentication methods

PasswordAuthentication no

Install and configure fail2ban

Set up fail2ban according to your server requirements

Edit default error pages for Nginx

Change default error pages

# /etc/nginx/sites-available/default
error_page 404 /404.html

Install unattended-upgrades and apticron

# /etc/apt/apt.conf.d/50unattended-upgrades
APT::Periodic::Enable "1";
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";
Unattended-Upgrade::Mail "root";

Unattended-Upgrade::Allowed-Origins {
    "${distro_id} ${distro_codename}-security";
};

Unattended-Upgrade::Automatic-Reboot "false";

#  /etc/apticron/apticron.conf
EMAIL="root"

Only allow local connections to docker

# /etc/default/docker
DOCKER_OPTS="$DOCKER_OPTS --ip=127.0.0.1"

Develop a backup system for when things go wrong

It is good to have multiple backup systems in place. It’s not for “if things go wrong”, it’s for “when things go wrong”. Because it will go wrong and you have to be prepared.

rsnapshot, duplicity, backup gems, are all good tools to use. Personally, I use DO’s backup as well as my own backup system.