How to Migrate a Ruby on Rails App from Heroku to Dokku

 
Containers represent Dokku and Heroku

Dokku is dev ops for dummies and a cheaper alternative to Heroku. Recently I’ve migrated a couple of my projects to it. In this tutorial, I will describe how to setup and migrate a Rails app to Dokku with PostgreSQL, Sidekiq, Redis and Let’s Encrypt or Cloudflare for free SSL.

This tutorial is based on Dokku version 0.28.4. For a more in-depth tutorials, you can check out Dokku docs. I will focus on things you need to get up and running quickly. This blog post assumes you already have a Rails app running on Heroku.

Initial setup

You need to start with purchasing a barebones VPS and adding an SSH access to it. I will not elaborate on how to do it in this tutorial.

I use a Hetzner Cloud VPS, but there should be no much difference in setup process for different providers.

Let’s assume that your VPS has an IP address 192.192.8.8 and your Rails app will use my-rails-app.com as a domain.

SSH into your VPS:

ssh [email protected]

and run:

wget https://raw.githubusercontent.com/dokku/dokku/v0.28.4/bootstrap.sh;
sudo DOKKU_TAG=v0.28.4 bash bootstrap.sh
Replace v0.28.4 with the newest version of Dokku from releases page

Next, you need to specify your app name:

dokku apps:create my-rails-app

You also need to add Dokku plugins we will use for our Rails app:

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

Now you need to configure your public SSH key to be accepted by Dokku remote by running:

echo 'YOUR_PUB_KEY' | dokku ssh-keys:add admin

You can find the contents or your public key on GitHub:

https://github.com/[YOUR GITHUB USERNAME].keys

You can now exit your VPS. All the remaining commands can be executed locally.


Setup Dokku remote target

The same as with Heroku, Dokku deploys are performed automatically when you push code changes to a remote repository. To set it up you need to add following lines to your .git/config file.

[remote "dokku"]
  url = [email protected]:my-rails-app
  fetch = +refs/heads/*:refs/remotes/dokku/*

You can validate if the setup was successful by running:

git push dokku master

Deploy will fail at this point. Just make sure you can access the VPS with your Dokku SSH keys.

Copy Heroku environment variables

You probably have a bunch of environment variables setup in your Rails Heroku app. To list them just type:

heroku config

For most of the variables you need to run the following command:

dokku config:set ADMIN_LOGIN=admin
dokku config:set ADMIN_PASSWORD=secret
...

Just don’t copy the values of DATABASE_URL and REDIS_URL we will set those in the next step.

Setup Postgres and Redis

With Dokku each of the services will be running in seperate Docker container. Dokku abstracts away all the logic of how containers work with a high-level API. To add Redis and PostgreSQL linked containers to your Rails app you just have to:

dokku postgres:create my-rails-app-pg
dokku postgres:link my-rails-app-pg my-rails-app
dokku redis:create my-rails-app-redis
dokku redis:link my-rails-app-redis my-rails-app

This will set DATABASE_URL and REDIS_URL environment variables for your Rails app. You can try to run:

git push dokku master

again now and your app should be up and running on Dokku.

Dokku uses the same Procfile format as Heroku. If your app uses a worker process (eg. Sidekiq) don’t forget to initialize it by running:

dokku ps:scale web=1 worker=1

This is a Procfile for a Rails app running on Puma with Sidekiq:

web: bundle exec puma -C config/puma.rb
worker: bundle exec sidekiq -C config/sidekiq.yml

Automatic backups to Amazon S3

You probably want to backup your data. Setting up an automated backup schedule to Amazon S3 is as simple as configuring AWS credentials:

dokku postgres:backup-auth my-rails-app-pg AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY

and adding the following line to /etc/crontab

@daily root dokku postgres:backup my-rails-app-pg your-bucket-name

Deployment hooks

You could run a database migration manually after each deploy or integrate it into the process. To do it add an app.json file to the root of your project:

{
  "name": "abot",
  "description": "Slack anonymous feedback",
  "keywords": [
  ],
  "scripts": {
    "dokku": {
      "postdeploy": "bundle exec rake db:migrate"
    }
  }
}
Deployment configuration for Abot

Setup Domains and SSL

Before you disconnect your domain from Heroku you can work with a local override by modifying /etc/hosts file. Add the following line:

192.192.8.8 my-rails-app.com

Now execute this command:

dokku domains:add my-rails-app my-rails-app.com

You should be able to access your Dokku Rails app via a browser now. It is not yet exposed to the world and does not support https connections.

Add SSL with Let’s Encrypt

Let’s Encrypt let’s you add a free SSL/TLS protection to your websites. Enabling it for Dokku apps is as simple as running:

dokku config:set --no-restart my-rails-app DOKKU_LETSENCRYPT_EMAIL=[email protected]
dokku letsencrypt my-rails-app
dokku letsencrypt:auto-renew my-rails-app
dokku letsencrypt:cron-job --add my-rails-app

That’s it. Your certificate file should be installed on NGINX and automatically renewed using CRON task every 2 months.

Remember that you can run those commands only after your domain has already been redirected to Dokku VPS on a DNS level. Otherwise, domain ownership validation will fail.

Add SSL with Cloudflare

Alternatively, you can use Cloudflare to add a free SSL to your website. You can check out my other post for more info about it.

Once you setup SSL in Cloudflare panel you need to install certificate files on your server. Dokku offers a tool to do it. You will need two files certificate.crt and certificate.key. Both can be downloaded from Cloudflare.

I had problems with adding certificate files without making a tar package out of them. To make a tar package and add the certificates run:

tar cvf certificates.tar certificate.crt certificate.key
dokku certs:add my-rails-app < certificates.tar

That’s it. Your app should be running with a secure connection now.

Heroku PostgreSQL data migration

Now that your Dokku app is active you can migrate your production data there. This is a part when you will have a downtime for your app so you might want to try the process out in a staging environment first. Downtime duration is dependent on your data size.

First, you should turn on the maintenance mode on Heroku:

heroku maintenance:on

then disable all the processes so that they don’t modify data:

heroku ps:scale web=0
heroku ps:scale worker=0

next, create an up to date backup and download it:

heroku pg:backups:capture
heroku pg:backups:download

Now you can load the data into a Dokku database:

dokku postgres:import my-rails-app-pg < latest.dump

Now you just need to change your DNS entries for a domain to start pointing to Dokku VPS server. That’s it. The migration from Heroku is over.

Useful Dokku commands

I am no dev ops pro and this post just scratches a surface of what can be done with Dokku. For more info you should check out documentation of Dokku and plugins I’ve mentioned:

Dokku Postgres plugin

Dokku Redis plugin

Dokku Let’s Encrypt plugin

Below I list a bunch of commands I use daily when working with Dokku apps:

dokku logs -t // application logs
dokku nginx:access-logs -t // NGINX logs
dokku ps:restart // restart the app
dokku --rm run rails c // access application Rails console
dokku postgres:connect my-rails-app-pg // access PostgreSQL console
docker rm -v $(docker ps -a -q -f status=exited) // remove all exited docker containers

Caveats

Long deploy process

Problem with Dokku is that deploys take much longer than in case of Heroku. For a simple Rails app, it is something around 2 minutes. I also have a bit more complex app which is dependent on both Ruby and Webpack with NodeJS and its deploy process takes around 5 minutes.

It could be a fault of my VPS provider and its slow internet connection or CPU. I know there are some hacks to speed up building a release from a buildpack with caching but I did not look into it yet.

I ended up using Heroku free instances with its quick deploy cycles for staging environment. That’s where I deploy often during development. Occasional longer production deploy is ok for my workflow.

[Update] there is a better way to optimize Dokku deployment speed using Dockerfile. Check out my new blog post.

Single server infrastructure

Limitation of Dokku is that it supports only one server. If you need a fancy production infrastructure with multiple server instances behind a load balancer it is not a tool for you.

You can always scale Dokku vertically so for less complex cases, side-projects etc. it could be an optimal choice.

Summary

Throughout my career, I have never been too much into dev ops stuff. I have either used Heroku or had a dedicated specialist in the team.

With Dokku I was surprised how simple it was to use even if you don’t have much dev ops experience. You should definitely give it a try if you are hosting your side-project on Heroku and overpaying for it. Migrating Abot - Slack Anonymous Feedback allowed me to reduce its infrastructure costs from $16 to $3 a month.



Back to index