Guide for deploying Ruby on Rails app on Digital Ocean Droplet
You’ve created your Digital Ocean Ubuntu droplet and now need to deploy a Ruby on Rails app. Here’s what to do:
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.
The holy trinity of web application development.
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.
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
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.
This bit is easy:
gem install bundler
gem install rails
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
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.
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:
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.
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
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.
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,
To see what’s going on with a failing nginx installation try running
sudo tail -f /var/log/nginx/error.log
.
This usually means there is something wrong with your server blocks so go double check all of them. It could be that..
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;
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;
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.
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.
I’ve found that setting the SSL settings in Cloudflare to ‘flexible’ can sometimes cure some issues so give that a try.
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.
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.
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…
bundle install
your rails app before installing psql on the serverrbenv rehash
for some reasonHere’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;
}
}