Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ericmathison/30a59c08f0bf79e8003e to your computer and use it in GitHub Desktop.
Save ericmathison/30a59c08f0bf79e8003e to your computer and use it in GitHub Desktop.
This gist describes the process that we used to set up the server for the Recycling Prices application.

Set up instructions for https://github.com/ericmathison/recycling_prices

Sign up at www.atlantic.net (Select the Ubuntu 14.04 LTS operating system option). Atlantic.net provides virtual private server (VPS) hosting for about $1 a month so it's a great option if you are learning how to set up a Rails application on a production server. Unlike Heroku, you will have full control over the server. At some point in your application's lifetime this may become important to you if you want to make customizations Heroku isn't capable of. Unless future limitations aren't a concern AND you don't want to learn how to set up a virtual private server, I wouldn't suggest using Heroku. The setup process below would be very similar for any of the other big hosting options like Amazon's EC2, DigitalOcean, etc.

Confirmation email will have log in instructions with IP, password and username.

Some of the commands below are run on the remote machine and others on your local development machine. I will prefix each command with one of the following:

remote-root#
remote$
your-machine$

Commands prompts with a # indicate commands to be run by the root user and commands with a $ indicate commands to be run with an ordinary, non-root user. You don't type this part, just the command that comes after it.

Commands below that are supposed to be run as root should be prefixed with sudo (i.e. sudo apt-get install git instead of apt-get install git) if your prompt ends with a $ instead of a #.

At first we will log in as the root user and your prompt on the server will end with a #. Later, we will log in as an ordinary user and your prompt on the server will have a $ on the end.

In the examples below, replace with the ip address that you were sent in the email (something like: 123.123.123.123) and replace <username> with the username that you would like to use. I suggest a lower case, single word user name like bob, joe, or perhaps even myappname.

Log in with the credentials provided in the email:

your-machine$ ssh root@<ip address>

We are now logged in as the root user on the remote machine.

Since we want to host our rails app here, we don't want to host it using the root user. If the web app had a security flaw allowing shell access, running as root would open up even more vulnerabilities (like being able to install software without a password). We need to make a new user.

First, let's make an admin group:

remote-root# groupadd admin

Let's create a new user:

remote-root# useradd -G admin <username>

admin here refers to the admin group. We are creating the new user and immediately adding it to the admin group. By default, Ubuntu is set up to allow any users in the admin group to have root privileges (when using sudo and the user's password).

Next, change the password of our new user:

remote-root# passwd <username>

Make a home directory for our new user:

remote-root# mkdir /home/<username>

Set that home directory's permissions:

remote-root# chown <username>:<username> /home/<username>

Copy your public key from your computer to the server (to authenticate securely without needing a password). ssh-copy-id is generally only available if your computer is running Linux.

your-machine$ ssh-copy-id <username>@<ip address>

As an alternative to the previous command (e.g. if you have a mac), and you happen to have a github account with the public key that you want to authenticate with on your server, you can do the following instead:

remote$ mkdir -p ~/.ssh
remote$ cd ~/.ssh
remote$ wget 'https://github.com/<github username>.keys'
remote$ cat <github username>.keys >> authorized_keys

You can now log in to the server with your normal user:

your-machine$ ssh <username>@<ip address>

You shouldn't need to enter a password this time

Install ufw (uncomplicated firewall), postgres, git, build-essential (for compiling ruby), and nojejs (for as the JavaScript runtime):

remote-root# apt-get install postgresql git ufw build-essential nodejs

Enable firewall (make sure to allow port 22 for ssh before enabling the firewall or you'll be locked out):

remote-root# ufw allow 22
remote-root# ufw allow 80
remote-root# ufw enable

Restart the server for the firewall setting to take effect.

remote-root# reboot

Set bash as the default shell for our new user:

remote-root# chsh -s /bin/bash <username>

Add these lines to the top of /etc/ssh/sshd_config (with vim for example). Comment out or delete any other lines with PermitRootLogin or PasswordAuthentication settings:

PermitRootLogin no
PasswordAuthentication no
AllowUsers <username>

Restart ssh for the settings to take effect:

remote-root# service ssh restart

Update the installed packages:

remote-root# apt-get update
remote-root# apt-get upgrade

Note: In the middle of the upgrade process, I was presented with a dialog stating that "The GRUB boot loader was previously installed to a disk that is no longer present..." asking which device I would like to install the boot loader to. I installed to /dev/sda (which generally refers to a hard drive) instead of /dev/sda1 (a partition on that hard same drive). Apparently it's a "BAD" idea to install to the partition (http://askubuntu.com/a/19705/159696).

Install ruby-install for easy ruby installations:

remote$ wget -O ruby-install-0.5.0.tar.gz https://github.com/postmodern/ruby-install/archive/v0.5.0.tar.gz
remote$ tar -xzvf ruby-install-0.5.0.tar.gz
remote$ cd ruby-install-0.5.0/
remote-root# make install

Install ruby (installed ruby 2.1.3):

remote$ ruby-install ruby

Note: The first time I ran the previous command to install ruby, the compilation failed with a message saying "gcc: internal compiler error". This was due to insufficient memory (my server had 256 megs). The solution to this was creating a swap file so that the extra memory needed could be stored on disk during the installation. First create a big file full of zeros:

remote$ dd if=/dev/zero of=~/swapfile bs=1024 count=1024000

Then turn that file into a proper swap file:

remote$ mkswap ~/swapfile

Then enable the file for swapping:

remote-root# swapon ~/swapfile

You can check to make sure you have swappable memory with this command:

remote$ free

After you are done installing ruby, you can turn the swap off:

remote-root# swapoff ~/swapfile

Then overwrite any data that was put into the swapfile (it could possibly contain sensitive info like passwords that were stored in memory):

remote$ shred ~/swapfile

And delete it:

remote$ rm ~/swapfile

Clone recycling prices app (in home directory):

remote$ git clone https://github.com/ericmathison/recycling_prices.git

Create Database YAML File:

cp config/database.yml.example config/database.yml

Edit database.yml:

vim config/database.yml

Edit the file so that it looks exactly like the following 5 lines:

production:
  adapter: postgresql
  encoding: unicode
  database: recycling_prices_production
  pool: 5

Create database user:

remote$ sudo -u postgres createuser -s <username>

Create Database and Load Schema:

remote$ cd ~/recycling_prices
remote$ bundle exec rake db:create RAILS_ENV=production
remote$ bundle exec rake db:schema:load RAILS_ENV=production

Install Passenger and Nginx (offical docs: https://www.phusionpassenger.com/documentation/Users%20guide%20Nginx.html#install_on_debian_ubuntu):

remote-root# apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 561F9B9CAC40B2F7
remote-root# apt-get install apt-transport-https ca-certificates

Add this line:

deb https://oss-binaries.phusionpassenger.com/apt/passenger trusty main

to the end of /etc/apt/sources.list.d/passenger.list

remote-root# chown root: /etc/apt/sources.list.d/passenger.list
remote-root# chmod 600 /etc/apt/sources.list.d/passenger.list
remote-root# apt-get update

remote-root# apt-get install nginx-extras passenger

"Edit /etc/nginx/nginx.conf and uncomment passenger_root and passenger_ruby. Especially passenger_root is important: Phusion Passenger won’t work without it! If you don’t see a commented version of passenger_root inside nginx.conf, then you need to insert it yourself."

Make sure that the value of passenger_root in /etc/nginx/nginx.conf is the same as the output of:

remote$ /usr/bin/passenger-config --root

Replace the value of passenger_ruby with:

/home/<username>/.rubies/ruby-2.1.3/bin/ruby

That line should look something like:

passenger_ruby /home/myusername/.rubies/ruby-2.1.3/bin/ruby

Register recyclingprices.net at namecheap.com. Go to "Manage Domains", select recyclingprices.net, select "All Host Records". Now we will modify our dns settings so that recyclingprices.net properly refers to our ip address. Fill out the last two column just like they are shown below (replacing with your corresponding ip address):

Host Name     IP Address/URL              Record Type
@             <ip address>                A (address)
www           http://recylingprices.net   URL Redirect (301)

Replace the content of /etc/nginx/sites-enabled/default with:

server {
        listen 80 default_server;
        listen [::]:80 default_server ipv6only=on;

        root /home/deploy/recycling_prices/public;

        server_name recyclingprices.net;
        passenger_enabled on;
        rails_env production;

        # redirect server error pages to the static page /50x.html
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

In /etc/nginx/nginx.conf:

Uncomment this line:

server_names_hash_bucket_size 64;

Compile the assets:

remote$ bundle exec rake assets:precompile RAILS_ENV=production

restart nginx:

remote-root# service nginx restart

If passenger doesn't start up, you may need to run this to restart passenger:

touch ~/recycling_centers/tmp/restart.txt

Nginx note. This command is helpful when having trouble getting nginx to start properly:

remote-root# nginx -t

Pre-comile assets (from within the root of the project):

bundle exec rake assets:precompile RAILS_ENV=production

Note: the server we ran this on did not have sufficient memory to compile assets so we needed to temporarily add some virtual memory. These instructions are the same as we used above when compiling Ruby:

remote$ dd if=/dev/zero of=~/swapfile bs=1024 count=1024000
remote$ mkswap ~/swapfile
remote-root# swapon ~/swapfile

Now run the above command again to compile the assets. After compiling asssets, you can turn the swapfile off and remove it.

remote-root# swapoff ~/swapfile
remote$ shred ~/swapfile
remote$ rm ~/swapfile
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment