Deploying a New Rails App On a Subdomain
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