Open Democracy Manitoba Logo

Process & Technology An Open Democracy Manitoba Blog

Home - About - Archive - Feed

VPS Wordpress & Rails Hosting

ODM Technology Post

In this post we will configure Rails and Wordpress on a Digital Ocean VPS.

This is an update to a similar post from two years ago, which was an update of a post from 2014.

Our final hosting setup will be:

NOTE: I used MariaDB instead of Postgres because Wordpress doesn’t support Postgres.

This tutorial assumes a familiarity with the Linux command line, server administration, and Rails configuration.

Background Information

A VPS is a Virtual Private Server. In our case, a virtualized Ubuntu Linux server from Digital Ocean to host ODM Ruby projects.

The 1GB plan on Digital Ocean is only $5 a month. Nice.

Automation and Config Management

This post does not rely on a configuration management tool.

I figured I’d be done setting up the server long before I’d figured out the intricacies of Puppet, Chef, or Ansible. I tend to do a big deploy like this once every two years, often with significant changes required to the workflow, so I’m glad I haven’t automated the process yet.

That said, if you can see yourself setting up multiple servers following this tutorial, you may wish to look into one of the automated provisioning solution listed in the previous paragraph.

Step 1 - Create the Droplet

Digital Ocean (DO) calls their VPS instances Droplets. Once you have a DO account you can create new droplets by clicking on the ‘Create’ button in your admin dashboard. I selected the following options:

Step 2 - User Setup

Once the droplet was created, I was emailed a root username and password. I was then able to SSH to the server as root using Putty. My first order of business was to create a new user with sudoer privileges, and then lock root out of SSH access:

adduser serveruser
visudo

The second command opens up the sudoers file to which I added:

serveruser    ALL=(ALL:ALL) ALL

I then edited the /etc/ssh/sshd_config file to add:

PermitRootLogin no

And then I restarted ssh and logged back in with my serveruser user:

sudo service ssh restart

From now on when I need to run a command as root I will proceed the command with sudo.

Not shown in this tutorial: How to use SSH keys with Digital Ocean Droplets.

Step 3 - Create a Swap File (Optional)

Some of the following steps require compilation, so it’s nice to have a swap file on the server. By default DO droplets to not have swap files, but adding one isn’t difficult. This steps was much more necessary when I was using droplets with only 512MB RAM so feel free to skip this step.

I created a 1GB swapfile, half my physical RAM, since I’m going to dial the swappiness way down:

sudo fallocate -l 1G /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
sudo chown root:root /swapfile
sudo chmod 0600 /swapfile

Then open /etc/fstab with sudo privs and add:

/swapfile       none    swap    sw      0       0 

Using swap too aggressively on a SSD drive can lead to hardware degradation. So let’s dial down the “swappiness” from 60 to 5:

echo 5 | sudo tee /proc/sys/vm/swappiness
echo vm.swappiness = 5 | sudo tee -a /etc/sysctl.conf

Step 4 - Install Required Ubuntu Packages

Let’s start by upgrading all the pre-installed packages:

sudo apt-get update
sudo apt-get upgrade

Reboot after that:

sudo reboot

Then install the ubuntu packages we need

sudo apt-get install git g++ make nodejs libsqlite3-dev postgresql postgresql-contrib gconf2 libcurl4-openssl-dev imagemagick zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev sqlite3 libxml2-dev libxslt1-dev libffi-dev php-fpm php-curl php-imagick php-mbstring php-zip  php-mysql  subversion autoconf bison mariadb-server mariadb-client

Step 5 - MariaDB Setup

MariaDB is a fork of the MySQL project. I normally use Postgres when working with Rails, but because I needed to support Wordpress too, I used Maria.

Switch to the root user:

sudo su -

Secure the database (press ENTER for the current password):

sudo mysql_secure_installation

Login with the root password you just set:

sudo mariadb -uroot -p

Add a new database from the SQL prompt:

create database newdatabasename;

Create a new user with full permissions to this database. Also from the SQL prompt:

GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER ON newdatabasename.* TO 'newusername'@'localhost' IDENTIFIED BY 'newuserpassword';

(Be sure to replacce the newdatabasename, newusername and newuserpassword with values of your own.)

Exit the SQL prompt and switch back to the serveruser:

exit
exit

Step 6 - Install Ruby and Rails

We are going to install Ruby by way of rbenv.

git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
source ~/.bash_profile
cd ~/.rbenv && src/configure && make -C src

mkdir -p "$(rbenv root)"/plugins
git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build
~/.rbenv/bin/rbenv init

echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
source ~/.bash_profile

Install Ruby (the latest version at the time of this post was 2.5.1):

rbenv install 2.5.1
rbenv global 2.5.1

Test your install with version switch:

ruby -v

And then install bundler using gem:

echo "gem: --no-document" > ~/.gemrc
gem install bundler

I also need to install Ruby 1.8.7 for legacy reasons. The apt-get is a fix for openssl support.

sudo apt-get install libssl1.0-dev
CONFIGURE_OPTS="--with-readline-dir=/usr/include/readline --with-openssl-dir=/usr/include/openssl" rbenv install 1.8.7-p375

Step 7 - Install Passenger & Nginx:

Install Phusion Passenger and the Nginx web server:

# Install our PGP key and add HTTPS support for APT
sudo apt-get install -u dirmngr gnupg
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7
sudo apt-get install -y apt-transport-https ca-certificates

# Add our APT repository
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

# Install Passenger + Nginx
sudo apt-get install -y nginx-extras libnginx-mod-http-passenger

Re-start Nginx:

sudo service nginx restart

Step 8 - File Permissions and Other Git Config

Create the /var/www folder and give it permissions. If /var/www already exists you can skip the mkdir step.

mkdir /var/www
sudo groupadd www-data
sudo usermod -a -G www-data serveruser 
sudo chown -R serveruser:www-data /var/www
sudo chmod 2775 /var/www

Set up a ssh key for the server:

ssh-keygen -t rsa -b 4096 -C "stungeye@gmail.com"

Configure git:

git config --global user.email "youremail@example.com"
git config --global user.name "Your Name"

Not shown in this tutorial: Linking the SSH key created above to your Github account.

Step 9 - Configure Nginx to Host a Rails App

To test a Rails app, here’s simple ngnix server block that you can put in your /etc/nginx/sites-available/default config file. You will need to edit this file using sudo and you should replace the existing server block that is already present in the file.

server {
    listen       80;
    server_name  localhost;
    passenger_enabled on;
    rails_env development;
    root /var/www/railsapp/public;
}

Note that the folder you specific for the root must point to the public folder of a Rails app you’ve install in your /var/www/ folder. If you’ve mapped your droplet to a domain, replace localhost with the name of your domain.

You’ll also want to configure your Rails app to use MySQL using the credentials you created above. And don’t forget to run your migrations!

If you’re hosting multiple sites you might want to use the ensite tool along with the /etc/nginx/sites-available folder.

Step 10 - Install Wordpress

Grab the latest version of Wordpress and move it to your /var/www folder:

wget http://wordpress.org/latest.tar.gz
tar -xzf latest.tar.gz 
mkdir /var/www/wordpress
mv latest /var/www/wordpress/production

Configure Wordpress:

cd /var/www/wordpress/production
cp wp-config-sample.php wp-config.php
curl -s https://api.wordpress.org/secret-key/1.1/salt/ 

The last of those three commands will provide you with some secrets to add to the wp-config.php file. You also need to add your database credentials to this config file.

I also changed the database encoding type (as per this article):

define('DB_CHARSET', 'utf8mb4');

If you want Wordpress to be able to directly download plugins/updates you should also add:

define('FS_METHOD', 'direct');

Next you’ll want to set up some file permissions:

mkdir /var/www/wordpress/production/wp-content/uploads
sudo chown -R www-data:www-data /var/www/winnipegelection/production/wp-content/ 

Step 11 - Configure Nginx to host Wordpress

Here’s a sample Nginx file for Wordpress hosting:

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    root /var/www/wordpress/production;

    index index.php;

    server_name _;

    location = /favicon.ico {
        log_not_found off;
        access_log off;
    }

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    # Required for supporting fancy permalinks
    location / {
        try_files $uri $uri/ /index.php?q=$uri&$args;
    }

    # pass PHP scripts to FastCGI server
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
    }
}

I also find it handy to increase the upload size nginx and php can handle to allow for large image uploads through Wordpress.

# File: /etc/nginx/nginx.conf

client_max_body_size 25M;

# File /etc/php/<version>/fpm/php.ini  Where version your php version.

upload_max_filesize  = 25M
post_max_size = 25M

Step 12 - Extra Security Stuff (Optional)

Enable SSL with certbot:

sudo apt-get update
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install python-certbot-nginx
sudo certbot --nginx
sudo service nginx reload

Certbox will also add a daily cron task to check and renew certificates if required.

Enable the ufw Firewall to only permit web (port 80) and ssh (port 22) traffic:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable

At this point you might also want to install fail2ban. I found the following two tutorials helpful:

Be cautious with fail2ban. You don’t want to lock yourself out of your own server. If you do get blocked, you can login using the Digital Ocean web console for your droplet.

Step 13 - Test Your Sever

Congrats! At this point you should now be able to load either a Rails app or Wordpress. When testing your app be sure to trigger actions that both read and write to your database to ensure that MariaDB is properly configured.

Step 14 - Keeping Your System Up To Date

When you connect via SSH to your droplet a login message will let you know if there are pending security updates. To download and install these updates, run the following commands:

sudo apt-get update
sudo apt-get upgrade
sudo apt-get dist-upgrade

After which you may have to restart your droplet:

sudo reboot

To list newly available Ruby versions:

rbenv install -l

To install a new version:

rbenv install <version number>
rbenv global <version number>
rbenv rehash

To install a new version of Rails, update your Rails project Gemfile and then run:

bundle update
rbenv rehash