You’ve created your Digital Ocean Ubuntu droplet and now need to deploy a Ruby on Rails app. Here’s what to do:

Step 1 - Create a new user

Fresh droplets come with a single user called ‘root’. Although you could use the root user to set up your Rails server it’s best practice to create a separate user instead. Once created, our new user will need to be added to the sudo group.

SSH into your droplet using ssh root@your-ip-here then run the following commands:

adduser planetroast
usermod -aG sudo planetroast

For more info on adding and deleting users check out the Digital Ocean guide for adding users to Ubuntu 20.

Step 2 - Install Ruby, Bundler, and Rails

The holy trinity of web application development.

Installing Ruby (via rbenv)

I like using rbenv for installing various versions of Ruby but don’t install rbenv using apt because you’ll probably get an old version which will throw a load of errors. The rbenv github page mentions this and recommends to install by cloning the repo instead:

Note that the version of rbenv that is packaged and maintained in the Debian and Ubuntu repositories is out of date. To install the latest version, it is recommended to install rbenv using git.

git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'eval "$(~/.rbenv/bin/rbenv init - bash)"' >> ~/.bashrc

You might have to reload your shell (or opening another terminal tab) for these changes to take affect. Note that second line configures your bash shell to load rbenv.

Installing ruby-build

One of the first things you’ll likely want to do with rbenv is to install a specific version of Ruby, but you have to install ruby-build before rbenv’s install option is made available.

First let’s install a bunch of dependencies as listed on ruby-build’s Github.

# Don't just copy and paste this, go get the most recent list from above link
apt-get install autoconf patch build-essential rustc libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libgmp-dev libncurses5-dev libffi-dev libgdbm6 libgdbm-dev libdb-dev uuid-dev

As for the install of ruby-build itself, the official ruby-build github page says you can use a package manager such as apt, but the rbenv docs also say you can install it “as a plugin” by cloning the repo. And as we used this approach to install rbenv might as well take the same approach with ruby-build:

git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build

Install some Ruby versions

Once that’s done you should be able to rbenv install -l to list available versions of Ruby, then rbenv install 3.0.3 to install a specific one. You’ll probably want to set a global version using rbenv global 3.0.3 too.

Bundler & Rails

This bit is easy:

gem install bundler
gem install rails

Installing PostgreSQL

Just a simple ‘apt-get’ is all that’s needed here. You need the main package and a couple of dependencies.

sudo apt-get install postgresql postgresql-contrib libpq-dev

Once psql is installed there are a couple more things to do before you can access the command prompt. By default psql installs with a single user named ‘postgresql’ so you’ll need to create a new user for yourself. Below I’ve used the ‘–interactive’ option which prompts you for the name.

sudo -u postgres createuser --interactive

After that we create a database for the new user because each psql user must have their own database. This was something I always had to do when install Ubuntu 18 (and possibly 20) but during my most recent deployment of Ubuntu 22 I realised the new user’s database was created automatically so there may be no need to run this command depending on the vesion of postgres or Ubunt you are working with.

sudo -u postgres createdb planetroast

Does Rails require NodeJs?

If you’re using a recent version of Rails then you probably don’t need to install nodejs. Older versions of Rails required it to run Webpack, but as of version 7 there is no requirement for it.

Installing Nginx & Passenger

Note that it’s not totally necessary to have nginx in order to run Rails on a Digital Ocean droplet, you can just cd in there and run rails s -b 0.0.0.0 in the same way you do to make your app available to other devices on your home network. Nginx just helps out with load balancing and a bunch of other networking tools that I don’t yet understand.

That said, it’s probably worth using it because:

Installing Nginx

Using the prebuilt package seems to work fine for me but the official nginx website has instructions for installing in other ways. Digital Ocean also have a guide for installing nginx on Ubuntu 20.

sudo apt install nginx

Once installed you interact with nginx using systemctl commands. Here are some useful ones:

sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx
sudo systemctl reload nginx (lets you make changes without dropping connections)

To check if nginx is running you can visit the IP of your droplet and should see the nginx index page.

Installing Phusion Passenger

The following instructions are pretty much lifted straight out of the Phusion Passenger docs or Phusion Passenger docs for Ubuntu 22. Note that the title of is somewhat misleading as it suggests you are installing both “passenger + nginx”, when in fact it’s just for passenger.

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7
sudo apt-get install -y apt-transport-https ca-certificates

Watch out for the url in the below command, it is specific to the release of Linux you are using so might need changing. The command below is for the bionic release so check which version you have by running cat /etc/os-release.

# Don't copy and paste this line! You need to set the right version first
sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger bionic main > /etc/apt/sources.list.d/passenger.list'
sudo apt-get update
sudo apt-get install -y libnginx-mod-http-passenger

Setting up nginx server blocks

Each site you want to deploy with nginx needs a file in /etc/nginx/sites-available containing some configuration data. You can then enable a site by symlinking the file into /etc/nginx/sites-enabled.

Here’s an example server block for a rails site:

server {
	listen 80;
	server_name myapp.com www.myapp.com;
	root /var/www/myapp;
	passenger_enabled on;
	passenger_app_env production;
}

I’ve saved my app to /var/www here instead of the home directory because I was geting some permission issues resulting in a 403 Forbidden error when nginx tried to access the files. There should be no reason to do this as I’ve successfully served apps from the home directory before.

Troubleshooting

As well as the Digital Ocean guides, you might like to check out Jerome ZNG’s guide which covers everything above, plus a bunch more,

Tail those nginx errors

To see what’s going on with a failing nginx installation try running sudo tail -f /var/log/nginx/error.log.

Nginx won’t reload or restart properly

This usually means there is something wrong with your server blocks so go double check all of them. It could be that..

Passenger Ruby version errors

Phusion Passenger sometimes complains that you are using the wrong Ruby version even though you’ve set the correct version with rbenv, and if you run ruby -v within the app directory it shows the correct version.

You can fix this by telling passenger the path to Ruby with an extra line in the nginx server block. I’ve read you can also do this via the passenger config file in case you want a certain version of Ruby for all your apps.

passenger_ruby /home/planetroast/.rbenv/versions/2.7.6/bin/ruby;

Set your nginx error log locations

Adding the following lines to your nginx server block allow you to set where the log files go. Handy if you’re running several apps.

error_log /var/log/nginx/myapp-error.log;
error_log /var/log/nginx/myapp-access.log;

Problems with gems not installing or wrong version of Ruby?

You might have to run rbenv rehash. I’m not totally sure what this does but it has worked for me a few times. There should be no need to run it regularly as (according to the docs) it runs itself automatically during installation.

Try running Rails straight from the app

If you can’t work out why nginx or passenger is failing to work try running rails s from the repo just as you would when starting a development server. This can show some errors that are being hidden somewhere along the process.

Enable flexible SSL when using Cloudflare

I’ve found that setting the SSL settings in Cloudflare to ‘flexible’ can sometimes cure some issues so give that a try.

Watch out when using the ‘template’ nginx server block

There is an example server block file at /etg/nginx/sites-available which you might have copied to use as a base for your new block, but it’s easy to miss that this file has multiple blocks stacked on top of each other. One of these blocks mentions a ‘default’ server and if you haven’t got that set up then your nginx will refuse to start.

Read the entire template server block instead of just grabbing the first part that looks correct. You may find you only need the bottom section of it.

There is no need for UFW

Ubuntu servers come with Universal FireWall installed and disabled by default. Some of the documentation I’ve found online mentions turning this on and adding nginx to it’s list of allowed apps.

Although it’s probably a wise thing to have, note that it’s not necessary to have UFW enabled or running to run a Rails app on a Digital Ocean droplet. This is true for ‘https’ urls too btw.

Error about psql when running bundler

I’ve seen this one a couple of times, it happens when the psql gem fails to install for some reason. It could be that…

Serving static files with nginx

Here’s a server block for serving static files with nginx. I’ve used this in the past just to check everything works before getting Rails up and running.

server {
        listen 80;
        server_name www.myapp.com;
        root /var/www/myapp;
        index index.html;
        location / {
                try_files $uri $uri/ =404;
        }
}