Setting up Rails on Dokku in Digital Ocean
This is a step by step tutorial that will walk you through setting up a Ruby on Rails application through Docker and Dokku on Digital Ocean.
Prerequisites
Digital Ocean account. If you don't already have one you can sign up for a digital ocean account.
Step 1: Create a digital ocean droplet
If you have a real domain available use that as your hostname.
Select the dokku app from the list of applications.
Add your SSH key and create the droplet.
Step 2: Set up a Custom Domain (Optional)
Dokku is your own personal Heroku. If you are familiar with Heroku you know that it runs apps on the domain herokuapp.com
. So if you deploy an app called hello
it will be accessible at hello.herokuapp.com
.
When you set up dokku, you can create your own app domain.
In your DNS settings create the following two A records
.
apps.yourdomain.com ---> ip of the droplet
*.apps.yourdomain.com ---> ip of the droplet
This means that if you deploy an app called hello
it will be accessible at hello.apps.yourdomain.com
. Of course, you can customize this and skip the apps
subdomain if you wish.
Step 3: Use /etc/hosts
instead of Custom Domain
It can take a while for the DNS records to propagate. Usually it's less than an hour, but Digital Ocean's DNS settings page warns that it can take up to 24 hours. If you don't want to set up your own domain for the dokku or if you don't want to have to wait for the DNS to propagate you can set up a 'fake' DNS entry in your /etc/hosts
.
# /etc/hosts
123.123.123.123 apps.yourdomain.com
Where 123.123.123.123 is the IP of your digital ocean droplet.
Step 4: Dokku VHOST configuration
We need to check that the domain is configured properly on Dokku. The docs explain that if the hostname cannot be resolved at the time you create your dokku image, the domain may not be set.
To check if the domain was set, log in to your droplet as root.
```
ssh root@apps.yourdomain.com
```
You should not need to enter a password because the droplet is set up to use public key authentication.
Once you're logged in, let's check if the VHOST file exists in
cd /home/dokku
ls
In my case, the VHOST file was not there. This was causing dokku to ask me for the dokku
user's password when I tried to deploy the app.
root@apps:/home/dokku# ls
HOSTNAME VERSION
If you do not see the VHOST file, you need to create it.
echo \"apps.yourdomain.com\" > /home/dokku/VHOST
chown dokku:root /home/dokku/VHOST
Add your development box's public key to the dokku user. This will allow you to push code without needing a password (sets up public key authentication).
cat ~/.ssh/id_rsa.pub | ssh root@apps.yourdomain.com \"sshcommand acl-add dokku dokku\"
Step 5: Install postgres
Let's use the [dokku-pg-plugin](https://github.com/Kloadut/dokku-pg-plugin postgresql).
From your remote server:
cd /var/lib/dokku/plugins
git clone https://github.com/Kloadut/dokku-pg-plugin postgresql
dokku plugins-install
If you get any nginx-related errors, see the Troubleshooting section below.
Now you should have a few extra pg commands available through dokku:
root@apps:~# dokku help
postgresql:console <db> Open a PostgreSQL console
postgresql:create <db> Create a PostgreSQL container
postgresql:delete <db> Delete specified PostgreSQL container
postgresql:dump <db> > dump_file.sql Dump database data
postgresql:info <db> Display database informations
postgresql:link <app> <db> Link an app to a PostgreSQL database
postgresql:list Display list of PostgreSQL containers
postgresql:logs <db> Display last logs from PostgreSQL container
postgresql:restore <db> < dump_file.sql Restore database data from a previous dump
Install the postgresql client so you can access the database.
From the remote server run:
apt-get install postgresql-client-9.3
Step 6: Create the database
On the remote server run:
dokku postgresql:create
-----> Creating /home/dokku/daffy/ENV
-----> Setting config vars and restarting daffy
DATABASE_URL: postgres://root:supersecret@172.0.1.1:49155/db
-----> Releasing daffy ...
-----> Release complete!
-----> Deploying daffy ...
-----> Checking status of PostgreSQL
Found image postgresql/daffy database
Checking status... ok.
-----> Deploy complete!
-----> daffy linked to postgresql/daffy database
-----> PostgreSQL container created: postgresql/daffy
Host: 172.0.1.1
Port: 49155
User: 'root'
Password: 'supersecret'
Database: 'db'
Url: 'postgres://root:supersecret@172.0.1.1:49155/db'
Now run this command to set the environment variable DATABASE_URL
that will be used in your Rails application's config/database.yml
.
dokku postgresql:link <app-name> <database-name>
Step 7: App Setup
Set up your app as you would for a Heroku deployment.
Procfile
Make sure you have a Procfile in your git repo. I am using puma so my Procfile looks like this:
web: bundle exec puma -C config/puma.rb
You can read more about Procfiles here.
Test your production setup on your local machine first. Foreman is a program that reads your Procfile and launches your application.
foreman start
If you don't have foreman installed, you can install it by running gem install foreman
.
Add git remote
Add your dokku remote to your git repo.
git remote add dokku dokku@apps.yourdomain.com:<app-name>
Set up config/database.yml
On Heroku, they inject a custom config/database.yml
into the app. In many projects database.yml gitignored.
On dokku you will have to make sure config/database.yml
is in your repo (not gitignored) and it contains the following for the production environment:
production:
adapter: postgresql
url: <%= ENV['DATABASE_URL'] %>
encoding: unicode
pool: 5
Sidenote: If you want to keep config/database.yml
gitignored there are some things you can do, but I'm not going to go into that here. Hint: If you're using Puma you can play around with config/puma.rb
.
Step 8: Deploy
Exciting! Time to deploy. You deploy dokku apps the same way you would on Heroku.
Push the master branch to dokku.
git push dokku master
This should trigger the deployment.
Counting objects: 2023, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (1236/1236), done.
Writing objects: 100% (2023/2023), 1.53 MiB, done.
Total 2023 (delta 1155), reused 1141 (delta 705)
-----> Cleaning up ...
-----> Building daffy ...
remote: Cloning into '/tmp/tmp.P2f4h7p5tB'...
remote: warning: You appear to have cloned an empty repository.
remote: done.
remote: HEAD is now at 606a3a2... Merge branch 'develop'
-----> Ruby app detected
-----> Compiling Ruby/Rails
-----> Using Ruby version: ruby-2.0.0
-----> Installing dependencies using 1.6.3
Running: bundle install --without development:test --path vendor/bundle --binstubs vendor/bundle/bin -j4 --deployment
Fetching gem metadata from https://rubygems.org/.........
Installing i18n 0.6.9
Installing rake 10.3.2
Installing minitest 5.3.4
Installing thread_safe 0.3.4
Installing builder 3.2.2
Installing erubis 2.7.0
Installing json 1.8.1
Installing rack 1.5.2
Installing mime-types 1.25.1
Installing polyglot 0.3.4
Installing arel 5.0.1.20140414130214
Installing c3-rails 0.3.0
Installing coffee-script-source 1.7.0
Installing execjs 2.0.2
Installing multi_json 1.10.0
Installing thor 0.19.1
Installing request_store 1.1.0
Installing hike 1.2.3
Using bundler 1.6.3
Installing rack-cors 0.2.9
Installing tilt 1.4.1
Installing temple 0.6.7
Installing rack-test 0.6.2
Installing tzinfo 1.2.0
Installing treetop 1.4.15
Installing coffee-script 2.2.0
Installing sprockets 2.11.0
Installing slim 2.0.2
Installing puma 2.8.1
Installing mail 2.5.4
Installing activesupport 4.1.1
Installing activemodel 4.1.1
Installing actionview 4.1.1
Installing activerecord 4.1.1
Installing actionpack 4.1.1
Installing actionmailer 4.1.1
Installing railties 4.1.1
Installing gon 5.2.0
Installing sprockets-rails 2.1.3
Installing coffee-rails 4.0.1
Installing d3_rails 3.4.11
Installing jquery-rails 3.1.0
Installing slim-rails 2.1.4
Installing turbolinks 2.2.2
Installing rails 4.1.1
Installing pg 0.17.1
Your bundle is complete!
Gems in the groups development and test were not installed.
It was installed into ./vendor/bundle
Bundle completed (23.01s)
Cleaning up the bundler cache.
Detected manifest file, assuming assets were compiled locally
###### WARNING:
Include 'rails_12factor' gem to enable all platform features
See https://devcenter.heroku.com/articles/rails-integration-gems for more information.
###### WARNING:
You have not declared a Ruby version in your Gemfile.
To set your Ruby version add this line to your Gemfile:
ruby '2.0.0'
# See https://devcenter.heroku.com/articles/ruby-versions for more information.
-----> Discovering process types
Procfile declares types -> web
Default process types for Ruby -> rake, console, web, worker
-----> Releasing daffy ...
-----> Deploying daffy ...
=====> Application deployed:
http://daffy.apps.yourdomain.com
To dokku@apps.yourdomain.com:daffy
* [new branch] master -> master
Step 9: Set up the database
From the remote server, let's create and migrate the database.
dokku run <app-name> rake db:create
dokku run <app-name> rake db:migrate
dokku run <app-name> rake db:seed
Restart the app.
docker restart <docker-container-id>
To find the container id you can run docker ps -a
.
You're done! Go visit your app url.
If anything goes wrong at this point, check out the Troubleshooting section below.
Further Reading
Dokku documentation - http://progrium.viewdocs.io/dokku/index
Troubleshooting
Error while Compiling Ruby/Rails
-----> Compiling Ruby/Rails
!
! Command: 'set -o pipefail; curl --fail --retry 3 --retry-delay 1 --connect-timeout 30 --max-time 30 https://s3-external-1.amazonaws.com/heroku-buildpack-ruby/cedar-14/ruby-2.1.0.tgz -s -o - | tar zxf - ' failed unexpectedly:
!
! gzip: stdin: unexpected end of file
! tar: Child returned status 1
! tar: Error is not recoverable: exiting now
!
This error means that you are using an unsupported version of ruby in your Gemfile. Dokku seems to be using the Heroku Cedar buildpacks, so you need to use one of the ruby versions supported by Heroku. Other versions might be support which are not listed on this page. For example, 2.1.2
works as of the writing of this article. Anyway, choose a version that makes sense for your app.
502 Bad Gateway
You visit your app url and gasp you get a 502
. This means your Rails server did not start or there is some other reason why nginx can't reach your app.
To find out what went wrong, let's look at the logs
docker logs -f a1b2c3d4e5f6
Where a1b2c3d4e5f6
is your container id. You can find your container id by running docker ps -a
.
In my case, I was missing the config/database.yml file in my Rails app.
If you are not sure if your Rails server is running, you can try accessing it from within the remote server:
curl http://127.0.0.1:`cat /home/dokku/<app-name>/PORT`
Nginx errors
When running dokku plugins-install
I got a bunch of errors that culminated in
dpkg: error processing package nginx-full (--configure):
dependency problems - leaving unconfigured
After a bit of googling someone suggested reinstalling nginx.
apt-get update
apt-get remove nginx
When prompted, choose N
to keep the version of /etc/nginx/nginx.conf
that was configured when dokku was installed.
This fixed things and I was able to run dokku plugins-install
successfully.
It looks like there have been some significant changes to the nginx-vhosts dokku plugin. Hopefully this fixed the issue.