About

View Archive

Bye Bye Heroku, Hello Dokku: Migrating a Rails App from Heroku to Dokku (Part 2)

Posted on November 22, 2014

In the Part 1, we successfully setup a Dokku-deployed DigitalOcean-powered Rails app with Postgres, Redis, Elasticsearch and Sidekiq.

In this post, I’ll show you how to setup SSL/TLS, schedule background jobs, and migrate your data from Heroku.

SSL

On Heroku, I used the SSL Endpoint add-on for enabling SSL. Setting up SSL for your app is just as easy with Dokku. First, SSH into your DigitalOcean droplet, navigate to your app’s directory and create a directory called tls.

cd ~/dokku/appname
mkdir tls

Then copy the server.crt and server.key files to the tls folder. I did this by using SCP to copy the files from my local machine to the server.

scp server.crt root@yourdomain.com:/home/dokku/appname/tls
scp server.key root@yourdomain.com:/home/dokku/appname/tls

You’ll need to reload nginx in order for the TLS configurationt to be applied.

nginx -s reload

For more instructions on setting up SSL/TLS for all of your applications, check out the Dokku documentation

Scheduled Jobs

The Heroku Scheduler add-on provided a rudimentary way to run jobs on your app at scheduled time intervals, much like cron in a traditional server environment.

After some research, I decided to replace Heroku Scheduler with a gem called Clockwork, which “runs a lightweight, long-running Ruby process…to schedule recurring work[jobs] at particular times or dates.”

Add the clockwork gem to your Gemfile and run bundle install:

# Scheduling jobs
gem 'clockwork'

Create a clock.rb and define the schedule of your jobs:

# lib/clock.rb

require File.expand_path('../../config/boot',        __FILE__)
require File.expand_path('../../config/environment', __FILE__)
require 'clockwork'

include Clockwork

every(1.day, 'Run my worker daily', at: '04:30', tz: 'UTC') { Sidekiq::Client.enqueue(MyWorker) }
every(7.day, 'Send weekly reports', at: '02:00', tz: 'UTC') { Sidekiq::Client.enqueue(ReportWorker) }

Add the clockwork process your Procfile:

web: bundle exec puma --preload -t ${PUMA_MIN_THREADS:-0}:${PUMA_MAX_THREADS:-16} -w ${PUMA_WORKERS:-1} -p $PORT -e $RACK_ENV
clock: bundle exec clockwork lib/clock.rb

This last step is important since Dokku runs only the web process in the Procfile by default.

On your server, install the dokku-logging-supervisord plugin:

It will run all process types (web, worker, etc.) and will restart crashed applications.

Additionally, it creates and binds a shared directory for each app from /var/log/dokku/$APP on the host machine to /var/log/app in the app’s container. The supervisord config is setup to have each process in your Procfile send it’s stdout and stderr to a separate file in that directory named $PROCESSNAME.$PROCESSNUM.log. Output for the supervisord process itself (startup/shutdown notices, etc) will be logged to a file named supervisor.log in the same log directory.

git clone https://github.com/sehrope/dokku-logging-supervisord.git /var/lib/dokku/plugins/logging-supervisord
dokku plugins-install

Finally, commit your changes and push your app:

git push dokku@yourdomain.com:appname master

Migrating Postgres Data

Put the Heroku app in maintenance mode to prevent further changes to the data and capture a backup of the Postgres database using Heroku’s PGBackups add-on:

heroku maintenance:on --remote production
heroku pgbackups:capture --expire --remote production

After the backup is captured, copy the URL for the backup:

heroku pgbackups:url --remote production

On your server, navigate to the app’s directory, download the backup and import the backup dump into your Dokku app’s database:

cd /home/dokku/appname
curl -o latest.dump "https://s3.amazonaws.com/hkpgbackups/app46@heroku.com/b905.dump"
dokku postgresql:restore sport < latest.sql

Migrating Redis Data

Unfortunately, I wasn’t able to successfully migrate Redis data from the RedisToGo. However, I’ll document it in case it works for someone else, or if someone knows what I did wrong.

Since I was using RedisToGo, I copied the URL for a backup from the Backups section on the RedisToGo dashboard. On the server, copy the host IP address for your app’s Redis container:

# dokku redis:info appname

       Host: 172.17.0.12
       Public port: 32120

Then download and save the backup from RedisToGo:

curl -o backup.rdb "http://s3.amazonaws.com/redistogo-backups/231/2014-11-17T00:58:27.rdb"

Import the backup with the following command:

redis-cli -h 172.17.0.12 < backup.rdb

During the import, I ended up getting the following errors that just kept repeating:

(error) ERR unknown command ')'
(error) ERR unknown command ')'
(error) ERR unknown command ')'

Wrapping Up

We’ve successfully setup SSL/TLS and no longer need the SSL Endpoint add-on, replaced Heroku Scheduler with scheduled jobs using the Clockwork gem, and migrated data from Heroku’s Postgres add-on to our instance of Postgres on our server. If anyone has a solution for migrating the Redis data, please share in the comments below.

About

Close

Hi, I'm Don Pottinger.

I love building things with computers. At 10 years old, I wrote my first program using BASIC. I built my first computer when I was 12 years old.

I graduated from Georgia Institute of Technology with a BS in Electrical and Computer Engineering. I have a background as a technology consultant and software engineer.

Now, I'm a co-founder of LanguaTalk, a language learning platform, and an engineering manager at Google.

I live in Atlanta with my amazing wife and kids. For fun, I play too much soccer and follow my favorite clubs, FC Barcelona and Arsenal F.C.

My CV so far

  • Engineering Manager at Google (2022 - Present)
  • Co-founder at LanguaTalk (2020 - Present)
  • Cloud Engineer at Google (2019 - 2022)
  • Founder and CEO at Kevy (2014 - 2019)
  • Co-founder and Head of Technical Development of Body Boss Fitness (2010 - 2015)
  • Engineering Manager and Software Engineer at Big Nerd Ranch (2012 - 2014)
  • IT and Management Consultant for Accenture and Slalom Consulting (2008 - 2012)
  • Graduate of Georgia Tech with BS in Electrical and Computer Engineering (2008)

Facts About Me

  • I was born in Jamaica and raised in the United States.
  • I speak English, Spanish and Jamaican Patois.
  • I love to travel. So far, I've visited Peru, Argentina, Spain, Switzerland, Germany, Greece, France, Italy, Canada, Jamaica, Hawaii, Mexico and England.