I recently worked through Michael Hartl’s wonderful Ruby on Rails Tutorial as a refresher. The software implemented under his direction offers functionality that basically every modern website requires (e.g., user sign up, password retrieval, etc). That which follows documents the steps I took to deploy all the best parts of that tutorial in a production environment.
Get a server Much of this post was ripped off from this article . They recommend Digital Ocean. I like cloudatcost.com for no other reason than because they’re cheap. For the purposes of this post, it doesn’t really matter as long as it’s installed with Ubuntu 14.04.
Add a user account The templated Rails application is executed under this account:
1
2
3
sudo adduser deploy
sudo adduser deploy sudo
su deploy
Install Ruby Some dependencies 1
2
sudo apt-get update
sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev python-software-properties libffi-dev
rbenv 1
2
3
4
5
cd
git clone git://github.com/sstephenson/rbenv.git .rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
exec $SHELL
ruby-build plugin 1
2
3
git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
exec $SHELL
rbenv-gem-rehash plugins 1
git clone https://github.com/sstephenson/rbenv-gem-rehash.git ~/.rbenv/plugins/rbenv-gem-rehash
Ruby 1
2
3
rbenv install 2.2.1
rbenv global 2.2.1
ruby -v
bundler 1
2
echo "gem: --no-ri --no-rdoc" > ~/.gemrc
gem install bundler
The echo
command prevents documentation for each gem being installed locally.
Install NodeJS Since it is my intention to deploy this system to a production environment, I need to use the Asset Pipeline to prep my content for distribution across the web. All that requires node
.
1
2
3
sudo add-apt-repository ppa:chris-lea/node.js
sudo apt-get update
sudo apt-get install nodejs
Install Rails 1
2
gem install rails -v 4.2.0
rails -v
Nginx and Passenger Install Phusion’s PGP key to verify packages 1
2
gpg --keyserver keyserver.ubuntu.com --recv-keys 561F9B9CAC40B2F7
gpg --armor --export 561F9B9CAC40B2F7 | sudo apt-key add -
Add HTTPS support to APT 1
sudo apt-get install apt-transport-https
Add the passenger repository 1
2
3
4
sudo sh -c "echo 'deb https://oss-binaries.phusionpassenger.com/apt/passenger trusty main' >> /etc/apt/sources.list.d/passenger.list"
sudo chown root: /etc/apt/sources.list.d/passenger.list
sudo chmod 600 /etc/apt/sources.list.d/passenger.list
sudo apt-get update
nginx and passenger 1
sudo apt-get install nginx-full nginx-extras passenger
1
sudo vim /etc/nginx/nginx.conf
Uncomment the rbenv
Phusion Passenger stuff. There should be some helpful hints in the file itself:
1
2
3
4
5
6
7
8
9
10
11
##
# Phusion Passenger
##
# Uncomment it if you installed ruby-passenger or ruby-passenger-enterprise
##
passenger_root /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini;
passenger_ruby /home/deploy/.rbenv/shims/ruby; # If you use rbenv
# passenger_ruby /home/deploy/.rvm/wrappers/ruby-2.1.2/ruby; # If use use rvm, be sure to change the version number
# passenger_ruby /usr/bin/ruby; # If you use ruby from source
Get an SSL certificate These instructions will produce a self-signed certificate:
1
2
sudo mkdir /etc/nginx/ssl
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt
Alternatively, validate with startssl.com for free. This document provides some excellent additional information.
Add nginx host 1
2
3
sudo touch /etc/nginx/sites-available/mydomain.conf
sudo ln -s /etc/nginx/sites-available/mydomain.conf /etc/nginx/sites-enabled/mydomain.conf
sudo vim /etc/nginx/sites-available/mydomain.conf
Write the following to the file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
listen 443 ssl;
server_name gofish.mobi;
# SSL
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
# Error logs
access_log /var/log/nginx/gofish.access.log;
error_log /var/log/nginx/gofish.error.log;
# Passenger
passenger_enabled on;
rails_env production;
root /home/deploy/rails-tutorial-template/current/public;
# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# Static assets
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
}
Start, or restart nginx
:
1
sudo service nginx restart
PostgreSQL Install:
1
sudo apt-get install postgresql postgresql-contrib libpq-dev
Create the deploy postgres user:
1
2
3
sudo su - postgres
createuser -U postgres -d -e -E -I -P -r -s deploy
exit
You’ll need to set the database password in config/application.yml
.
Before deploying with capistrano
, a few files have to be in place. As the deploy user:
1
2
cd
mkdir -p rails-tutorial-template/shared/config
Get a secret key If you have a rails project nearby, you can just type in
Or, you can generate one by running irb
and executing the following instructions:
1
2
3
require 'securerandom'
SecureRandom.hex(64)
exit
Copy the string generated by the SecureRandom.hex(64)
command.
application.yml This template uses figaro to manage all the sensitive stuff that sometimes goes into environment variables. The config/application.yml
file it looks for isn’t committed to the repository, so you have to create it yourself:
1
2
cd rails-tutorial-template/shared/config
vim application.yml
Copy, paste, modify, and save the following:
1
2
3
4
5
6
7
8
9
10
11
12
# General
app_name: "rails_tutorial_template"
# Email
default_from: "noreply@gofish.mobi"
gmail_username: "noreply@gofish.mobi"
gmail_password: "secretnoreplypassword"
# Production
secret_key_base: "PasteTheSecretKeyFromThePreviousStepHere"
host: "gofish.mobi"
provider_database_password: "databasepassword"
I set up an account in Gmail to handle signup verifications and password resets.
database.yml and secrets.yml There’s no sensitive information contained in the database.yml
or secrets.yml
files, so these can be copied directly from github.
1
2
wget https://raw.githubusercontent.com/RaphaelDeLaGhetto/rails-tutorial-template/master/config/database.yml
wget https://raw.githubusercontent.com/RaphaelDeLaGhetto/rails-tutorial-template/master/config/secrets.yml
Clone the template This is meant to be completed on the development machine (not the server). It is assumed that postgresql
and all the other dependencies are already installed (if not, do so as above).
1
2
3
4
5
6
7
git clone https://github.com/RaphaelDeLaGhetto/rails-tutorial-template.git
cd rails-tutorial-template
bundle install
sudo npm install
rake db:setup
rake db:seed
vim config/application.yml
Then copy, paste, and save the following in the file:
1
default_from: 'noreply@example.com'
Tests should all pass
capistrano deployment I’m still working on making this easier. From the project’s directory on the development machine set the following in config/deploy/production.rb
1
2
# Replace 127.0.0.1 with your server's IP address!
server 'gofish.mobi', user: 'deploy', roles: %w{web app}
Then run
1
bundle exec cap production deploy --trace
The deployment should succeed, but the site will not be accessible until the database is set up. Log in to the production server as deploy :
1
2
3
ssh deploy@gofish.mobi
cd rails-tutorial-template/current
RAILS_ENV=production rake db:setup
Now, enable the deploy user to restart passenger
without providing a sudo password:
Add this to the end of the file and save:
1
deploy ALL=(root) NOPASSWD: /usr/bin/passenger-config
Back on the local machine, the deployment should now succeed:
1
bundle exec cap production deploy --trace
If everything worked out right, then the app should be accessible at the configured domain name (gofish.mobi in my case).