Optimize Dokku Deployment Speed for Ruby on Rails with Dockerfile

Dokku lets you setup Rails hosting infrastructure on a simple VPS without much dev ops experience. Although it is easy to get started, a default config might result in very slow and unreliable deployments. In this blog post, I will describe how I’ve improved my Dokku based Ruby on Rails (NodeJS with Yarn and Webpack) application deployment speed by over 400% using a Docker image Dockerfile.

This tutorial is written in the context of RoR tech stack but solution presented (with small Docker image config tweaks) can be applied to all the server side technologies hosted with Dokku.

Getting started with Dokku

If you are not familiar with Dokku, you should check out one on my previous blog posts to get up and running quickly. Once you have a simple Rails app hosted on buildpacks based Dokku setup, you can follow rest of this tutorial to significantly reduce deployments speed.

But why buildpacks are so slow?

Buildpack is a list of commands that are executed during a deploy. I am not too much of a Docker expert to understand exactly what’s going on under the hood, but let’s take a look at a sample console output:

Apparently by default buildpacks are not too smart about caching and every single deployment downloads and recompiles some of the application dependencies. No wonder it is slow, and probably not the best way to use your application’s VPS CPU and RAM. What’s worse buildpacks based builds sometimes randomly fail due to 3rd party connectivity issues and network timeouts.

Now you just need to remove the .buildpacks file if you were using it before and remove one config variable:

dokku config:unset --no-restart DOKKU_PROXY_PORT_MAP

When you do a git push to dokku remote your Ruby on Rails app will use a Dockerfile instead of buildpacks:

In practice assets precompilation takes ~90% of deployment time. All the Dockerfile steps are cached and executed almost instantly after the initial deploy.

Use a custom Docker image with preinstalled dependencies

Alternatively, you could use my Ruby Jemalloc/NodeJS/Yarn buildpack ([Disclaimer] I am not a dev ops pro. Tips/PRs on how this image could be improved are welcome.). It has an additional advantage of using Ruby binary compiled with Jemalloc for reduced memory usage and NodeJS with Yarn is already in place:

You could also build and publish Dockerfile image yourself but that’s outside the scope of this tutorial.

Dockerfile caveats

ENV variables defined by dokku config:set command are not available during Dockerfile based deployments build time. bundle exec rake assets:precompile predeploy step will launch your Rails app process, and things could fall apart if some of the required ENV variables are missing.

Summary

Playing directly with Dockerfile images is a bit lower level than using a default Dokku buildpacks based approach. For me, the speed and reliability of Dockerfile powered deployments was more than worth the effort.