July 3, 2020

Guide to Running Rails Apps With Dokku on Digital Ocean

In this article, I will go through the steps to successfully launch a rails app using a Digital Ocean droplet and Dokku. We all love Heroku and the way it works, but as convenient as Heroku is, it doesn't always make sense for freelancers and those of us who want to put a few sites online with no real means to monetize them - even at Heroku's cheapest price point. That is where it is great to have options like a Digital Ocean, DreamHost, UpCloud, Linode, Vultr, or Kamatera.

Check them all out. They are all very similar but some might have a few nuances that you like better. This tutorial would be similar for all of them save for a few differences. I, in fact, have spun up servers on most all of them just to see what they were about and which ones I found easiest. Digital Ocean is at the top of the list for ease, so that's the one I am using.

I also went through several other tutorials about running Dokku and while all good, all seem to leave out a piece or two. So this aims to bring all of it together for a more complete guide. So let's get started.

Get a Digital Ocean Account

This goes without saying. Sign up for Digital Ocean and put your credit card details in. They will only charge you for what you use. But you will need a credit card to get started.

Create a Project

The next step is to start a new project which can hold a droplet or many droplets for that matter. I named my project, aptly, "Railscoder." You can fill in the other details about your project as you wish.

Create a Droplet

You need to create a droplet in your project. While Digital Ocean has predefined droplet for Dokku, do not use this one. Create a new droplet with just the Ubuntu distribution. That way we can get the latest version of Dokku and install it ourselves.

I chose the cheapest setup ($5 a month) and picked Toronto as my datacenter region (simply because I am huge Maple Leafs fan, so why not). You may want to pick a region that is close to you in proximity, although I have not noticed any latency between Toronto and Denver where I am.

You'll also want to set up SSH Keys to administer the server. Once you have an SSH key on file with Digital Ocean, you'll be able to just select that key with any new droplet.

Point Your Domain Name to the New Server

You will get an ipv4 address once your server is started. You will need to create an A record in your DNS entry wherever you have your domain hosted. I, personally use Google Domains and it's as simple as choosing a custom resource record, setting the resource to A and then adding your new IP address. It will look similar to below.


NOTE: Be sure to add "www", as well, to a CNAME record pointing to your domain - that way your domain will resolve correctly using either "http://example.com" or "http://www.example.com". It also will have implications later when we get to adding the encryption certificate.


Set Up The Server

Open your terminal and SSH into the server:

$ ssh [email protected]<server ip from droplet>

Now, let's get the latest version of Dokku on the server:

# SSH terminal to droplet server
# for debian systems, installs Dokku via apt-get
wget https://raw.githubusercontent.com/dokku/dokku/v0.20.4/bootstrap.sh;
sudo DOKKU_TAG=v0.20.4 bash bootstrap.sh

This was the latest version at the time of this writing. Check here to see if there is an update.

After it finishes, you need to go to your browser to finish the setup. And do this right away or it could expose security vulnerabilities.

In your browser, go to the droplet IP.

You should see you SSH Key listed that you already configured when setting up your account.

The only thing you should need to change is the "Hostname Configuration." In my case, I added my hostname "railscoder.com". I also checked that I wanted to use virtualhost naming for apps. That would mean apps on the domain would look like this: "example.railscoder.com".

Now, since we're using the smallest droplet which does not have a ton of RAM or other resources, let's enable swap to better handle deployments so we don't error out from not having enough resources.

Enable swap with following commands:

# SSH terminal to droplet server
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
sudo sh -c 'echo "/swapfile none swap sw 0 0" >> /etc/fstab'

Let's get the application set up on the server. Just replace "myapp" with your app name.

# SSH terminal to droplet server
dokku apps:create myapp

Add Heroku buildpacks. This will avoid setting up any dependencies manually.

# SSH terminal to droplet server
dokku buildpacks:add myapp https://github.com/heroku/heroku-buildpack-nodejs.git
dokku buildpacks:add myapp https://github.com/heroku/heroku-buildpack-ruby.git
# Add this if you plan on using Active Storage
dokku buildpacks:add myapp https://github.com/heroku/heroku-buildpack-activestorage-preview

Set up ENV variables on the server:

# SSH terminal to droplet server
dokku config:set myapp RAILS_ENV=production
dokku config:set myapp RAILS_MASTER_KEY=<master_key_from_app>

Your master key will be located in a file called "master.key" in your app's config folder.

Get Postgres up and running:

# SSH terminal to droplet server
dokku plugin:install https://github.com/dokku/dokku-postgres.git
dokku postgres:create myapp_db

Link Postgres DB to app:

# SSH terminal to droplet server
dokku postgres:link myapp_db myapp

Add Procfile to your App

You need to add a file called ".procfile" to the root of your app. The declarations in the file tell the app which processes should be running once your app is deployed.

In the procfile, add these lines:

# add to .procfile
# this tells the app upon deployment to run Puma to serve the app
web: bundle exec puma -C config/puma.rb
# add this line to automatically run migrations upon each deployment
release: bundle exec rails db:migrate

Add Domain and Get SSL

Add Domain To The App: Examples for both top level domain or subdomain below.

# SSH to droplet server
dokku domains:add myapp example.com
dokku domains:add myapp www.example.com
# If it were subdomain
dokku domains:add myapp myapp.example.com
dokku domains:add myapp www.mayapp.example.com

Acquire SSL Encryption and Certificate:

# SSH to droplet server
dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git
dokku config:set --no-restart myapp DOKKU_LETSENCRYPT_EMAIL=[email protected]
dokku letsencrypt myapp
# Add cron job so certificate automatically renews
dokku letsencrypt:cron-job --add myapp

Definitely add the cron job. Let's Encrypt's certificates are only valid for 3 months, so you will want to have the cron job handle getting the new one automatically to keep your domain from experiencing any disruptions.

Deploy Your App

Almost ready to roll. Now, we just need to add a remote repository.

Add Remote Repository

# Terminal within your app folder
git remote add dokku [email protected]_ADDRESS:myapp

With this setup, it is using the IP Address. If you domain is resolving already and correctly, you could use "myapp.com:myapp" for example. But for now, I will just stick with the IP address.

Deploy app just like you would on Heroku but with Dokku:

# Terminal within your app folder
git push dokku master

Dokku runs one instance of web process by default. You can up that by using this command.

# SSH to droplet server
dokku ps:scale myapp web=2

App Changes and Future Deployments

You should be good to go and if you enabled swap, your experience should be as smooth as using Heroku, just as mine has been.

Going forward, after you commit changes in git inside your app, all you should need to do is to run the "git push dokku master" command and your site will deploy the latest version.