This post: a step-by-step recipe for deploying a new Rails app on a subdomain. Our Ubuntu server runs Apache / Passenger / Rails / Capistrano; our domain registrar is namecheap.

Introduction

Inferentialist LLC runs multiple sites from a single, dynamic IP address. When deploying a new Rails app, this requires several configuration steps.

  • Set up a Git Repository
  • RVM / Rails
  • Configure Capistrano
  • Register a New Subdomain
  • Update Apache Configuration
  • Deploy with Capistrano

We mention a few pre-requisites. We use Apache for our webserver with the passenger mod enabled, and Passenger runs via a user named ‘deploy.’

Set up a Git Repository

I create a new, bare repo for each project in a GitRepos directory on my dev box,

mkdir -p /home/$USER/GitRepos
git init --bare /home/$USER/GitRepos/newproj.git

and maintain the project in a Workspace subdirectory.

mkdir -p /home/$USER/Workspace/newproj
cd /home/$USER/Workspace/newproj
git init 
git remote add origin file:///home/$USER/GitRepos/newproj.git/
echo "Hello World" > README
git add README
git commit -m "First commit"
git push origin master

Now you can clone the repo where ever you want, e.g. from my production machine:

git clone ssh://user@devbox/home/user/GitRepos/newproj.git

RVM / Rails

In the workspace directory, kick off RVM. The following command will install a ruby and gemset and will modify the shell to use them.

rvm --create --ruby-version ruby-2.2.1@api.inferentialist.com

# To revert to the default
# rvm use default

# Install Rails
gem install rails --no-document
rails new .

Gemfile Additions

Add the RubyRacer and Capistrano gems to the Gemfile:

gem 'therubyracer', platforms: :ruby

group :development do
  gem 'capistrano', '~> 3.0'
  gem 'capistrano-passenger'
  gem 'capistrano-rails',   '~> 1.1', require: false    
  gem 'capistrano-bundler', '~> 1.1', require: false    
  gem 'capistrano-rvm' 
end

Next, use bundle to install the gems.

bundle install

Index.html

We’ll need a landing page in order to see everything working in the production deployment. To that end, uncomment the following line in ./config/routes.rb.

root 'welcome#index'

Then, create the welcome controller with corresponding index stub. Run the server locally to check that the root landing page is now a Welcome#index skeleton template.

rails g controller welcome index
rails s

Configure Capistrano

For deployment, we use Capistrano. However, the defaults are insufficient, and need to be modified in order to use RVM.

cap install

This should create several files. Patch the following ones as follows:

./config/deploy.rb

Change ‘api.inferentialist.com’ to your fully qualified domain name with subdomain and change “dnlennon@192.168.1.101:~/GitRepos/#{fetch(:application)}.git” to refer to your Git repo.

patch ./config/deploy.rb <<END
4,5c4,5
< set :application, 'my_app_name'
< set :repo_url, 'git@example.com:me/my_repo.git'
---
> set :application, 'api.inferentialist.com'
> set :repo_url, "dnlennon@192.168.1.101:~/GitRepos/#{fetch(:application)}.git"
11a12
> set :deploy_to, "/home/deploy/#{fetch(:application)}"
23c24
< # set :pty, true
---
> set :pty, true
38d38
< 
46a47
> end
47a49,54
> desc "Install the rubies"
> task :rvm_create_install do
>   on roles(:app) do
>     bash_cmd = "rvm --create --install use #{fetch(:dot_ruby_version)}@#{fetch(:dot_ruby_gemset)} > /dev/null 2>&1"
>     execute "/bin/bash", "-c -l", "\"#{bash_cmd}\""
>   end
48a56,57
> 
> before "rvm:check", :rvm_create_install
END

./config/deploy/production.rb

Replace 192.168.1.102 with your production server.

patch ./config/deploy/production.rb <<END
6a7
> server '192.168.1.102', user: 'deploy', roles: %w{app db web}
9a11,15
> set :rvm_type, :user
> 
> set :dot_ruby_version, File.read('.ruby-version').strip
> set :dot_ruby_gemset, File.read('.ruby-gemset').strip
> set :rvm_ruby_version, "#{fetch(:dot_ruby_version)}@#{fetch(:dot_ruby_gemset)}"
END

./Capfile

patch Capfile <<END
18c18
< # require 'capistrano/rvm'
---
> require 'capistrano/rvm'
21,22c21,22
< # require 'capistrano/bundler'
< # require 'capistrano/rails/assets'
---
> require 'capistrano/bundler'
> require 'capistrano/rails/assets'
24c24
< # require 'capistrano/passenger'
---
> require 'capistrano/passenger'
END

Register a New Subdomain

I use Namecheap as my dynamic IP service. Establishing a new subdomain is pretty straight forward. Just add a new ‘A + Dynamic DNS’ record in the Advanced DNS section. Then update the ddclient config; this is described in some detail in a previous blog post.

Update Apache Configuration

Add the virtual host to the Apache configuration file. Replace ‘api.inferentialist.com’ with your fully qualified domain name. Finally, run ‘rake secret’ to generate your own secret key and set the environmental variable below.

<VirtualHost *:80>
    ServerName api.inferentialist.com
    DocumentRoot /home/deploy/api.inferentialist.com/current/public
    SetEnv SECRET_KEY_BASE d35ebf2fd71c10179d9623acbc7963fbb514b0652b86e7915d11c1c89d9979a248b122fb35fdcafd150ea9d3c96e6d4a909739438168343eb90d510c59d0a273
    <Directory  /home/deploy/api.inferentialist.com/current/public>
        Allow from all
        Options -MultiViews
        Require all granted
    </Directory>
</VirtualHost>

Deploy

We’re almost there. We just commit and push; then Capistrano pulls from our repo and sets up all the ancillary plumbing.

# Add the project to the repo
git add .gitignore .ruby-gemset .ruby-version Capfile Gemfile Gemfile.lock README.rdoc Rakefile app config config.ru db lib log public test vendor

# Commit and push to origin/master
git commit -m "first check in"
git push origin master

# This will take a few minutes the first time due to 'bundle install'
cap production deploy