In my struggle to have complete Rails3 environments setup in shared web hosts, the deployment step was missing. Lately I managed to deploy Rails3 Unicorn applications using Capistrano on a shared web host, as a non-root user with very limited privileges (jailshell). So I decided to share this with you.
- Setup a Git repository to deploy source files
- Install Capistrano
- Adapt Unicorn configuration
- Deploy your application
- Useful links
For information, here are the versions used:
- On my development host (from where I issue cap commands) [Windows 7 64bits]:
- Ruby: 1.9.3p194
- RubyGems: 1.8.24
- Bundler: 1.2.4
- Rails: 3.2.12
- Capistrano: 2.14.2
- capistrano-unicorn: master branch from GitHub repository
- On my production environment [Linux i686]:
- Ruby: 1.8.7p370
- RubyGems: 1.6.2
- Bundler: 1.2.4
- Unicorn: 4.6.0
Here are some prerequisites for this tutorial:
- The production environment is accessed using SSH.
- The production host provides a development environment (cc, ld, make).
- We will push our code to the production host using Git.
- If git is not installed on your production host, you can download the sources here as a Zip file, then compile and install it in your production user home space. No need to be root.
For the sake of this tutorial, the production host is called “mysite.com”, the SSH connection is made on port “1234”, the user running everything is named “myuser”. The Rails3 application is called “myapp”, it is present on development host in “/path/to/myapp”, and will be pushed on our production host in “/home/myuser/myapp.git” and deployed in “/home/myuser/myapp_deployed” directories. Prompts made on the production host begin with “[@production”, whereas the ones on the development (local) host begin with “[@development”.
Here are all the steps:
Setup a Git repository to deploy source files
First thing is to setup a git repository on the remote production host. This repository will then be accessed using SSH to push files, then Capistrano will pull from it to deploy your Rails3 application.
- Initialize a bare Git repository on production host:
[@production:/home/myuser]> mkdir myapp.git [@production:/home/myuser]> cd myapp.git [@production:/home/myuser]> git init --bare
This repository will then be accessible using URL ssh://myuser@mysite.com:1234/home/myuser/myapp.git
- Setup your local Git repository to push to this remote one.:
[@development:/path/to/myapp]> git remote add production ssh://myuser@mysite.com:1234/home/myuser/myapp.git
Install Capistrano
- Add
capistrano
andcapistrano-unicorn
gems in your Gemfile - Run
bundle install
to install those new gems. - Run
capify .
to capify you Rails3 application. This will create 2 files: Capfile and config/deploy.rb. - Edit the generated
Capfile
file to uncomment the line related to assets pipeline. This will define additional code for Capistrano to precompile assets when deploying in production.load 'deploy' # Uncomment if you are using Rails' asset pipeline load 'deploy/assets' load 'config/deploy' # remove this line to skip loading any of the default tasks
- If your development and production platforms are different (MacOS/Linux/Windows): edit your
.gitignore
file and make sure that Gemfile.lock is part of it.. The reason is simple: Bundler is not meant to be cross-platform (see this bug report). Some of the gems you bundle from your development host won’t be the same on your production host. Therefore we don’t want to deployGemfile.lock
on our production platform: it will be generated with a nicebundle install
made by Capistrano.# Ignore private files private # No Gemfile.lock as dev and prod platforms are not the same Gemfile.lock
- Edit file
config/deploy.rb
, and adapt it to your needs. Here are the considerations to take into account:- Add
require 'bundler/capistrano'
: It will add tasks needed to issue Bundler commands. - Set your application name.
set :application, 'My application'
- Set your SSH configuration. This includes your SSH username, SSH connection port, directive to not use sudo, and terminal type.
set :user, 'myuser' ssh_options[:port] = 1234 set :use_sudo, false default_run_options[:pty] = true
- Setup environment variables: Beware that if you tuned some environment variables in your SSH sessions to use locally installed gems or binaries, SSH connections made by Capistrano might not have them. You can set them this way.
set :default_environment, { 'PATH' => '/home/myuser/bin:/home/myuser/ruby/gems/bin:/usr/local/bin:/bin:/usr/bin', 'RUBYOPT' => '-I/home/myuser/rubygems/inst/lib', 'GEM_PATH' => '/home/myuser/ruby/gems:/usr/lib/ruby/gems/1.8', 'GEM_HOME' => '/home/myuser/ruby/gems' }
- Setup the Git repository location. Here we specify the production repository URL, accessed from the local development environment using
:local_repository
and from the production environment using:repository
.# Source repository taken for deployments set :local_repository, 'ssh://myuser@mysite.com:1234/home/myuser/myapp.git' set :repository, '/home/myuser/myapp.git' set :scm, :git # You can set :scm explicitly or Capistrano will make an intelligent guess based on known version control directory names # Or: `accurev`, `bzr`, `cvs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none`
- If your development and production platforms are different (MacOS/Linux/Windows): Set bundler flags manually to not include default
--deployment
. By default, Capistrano willbundle install
using the--deployment
flag, expecting aGemfile.lock
to be present. As we want it to be regenerated, we have to force the bundler flags without this one.set :bundle_flags, ''
- Set the deployment directory:
set :deploy_to, '/home/myuser/myapp_deployed'
- Set your production server for all the roles.
role :web, 'mysite.com' # Your HTTP server, Apache/etc role :app, 'mysite.com' # This may be the same as your `Web` server role :db, 'mysite.com', :primary => true # This is where Rails migrations will run
- At the end of the file, add the Unicorn specific tasks. It is important to require the ‘capistrano-unicorn’ gem after previous directives, as it can overwrite some variables you have set previously 🙁
# Unicorn tasks require 'capistrano-unicorn' after 'deploy:restart', 'unicorn:reload' # app IS NOT preloaded after 'deploy:restart', 'unicorn:restart' # app preloaded
Here is an example of a complete deploy.rb file:
require 'bundler/capistrano' set :application, 'My application' # SSH configuration set :user, 'myuser' set :use_sudo, false ssh_options[:port] = 1234 default_run_options[:pty] = true set :default_environment, { 'PATH' => '/home/myuser/bin:/home/myuser/ruby/gems/bin:/usr/local/bin:/bin:/usr/bin', 'RUBYOPT' => '-I/home/myuser/rubygems/inst/lib', 'GEM_PATH' => '/home/myuser/ruby/gems:/usr/lib/ruby/gems/1.8', 'GEM_HOME' => '/home/myuser/ruby/gems' } # Source repository taken for deployments set :local_repository, 'ssh://myuser@mysite.com:1234/home/myuser/myapp.git' set :repository, '/home/myuser/myapp.git' set :scm, :git # You can set :scm explicitly or Capistrano will make an intelligent guess based on known version control directory names # Or: `accurev`, `bzr`, `cvs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none` set :bundle_flags, '' # Destination of deployments set :deploy_to, '/home/myuser/myapp_deployed' # set :deploy_via, :copy role :web, 'mysite.com' # Your HTTP server, Apache/etc role :app, 'mysite.com' # This may be the same as your `Web` server role :db, 'mysite.com', :primary => true # This is where Rails migrations will run # if you want to clean up old releases on each deploy uncomment this: after 'deploy:restart', 'deploy:cleanup' # if you're still using the script/reaper helper you will need # these http://github.com/rails/irs_process_scripts # If you are using Passenger mod_rails uncomment this: # namespace :deploy do # task :start do ; end # task :stop do ; end # task :restart, :roles => :app, :except => { :no_release => true } do # run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}" # end # end # Unicorn tasks require 'capistrano-unicorn' after 'deploy:restart', 'unicorn:reload' # app IS NOT preloaded after 'deploy:restart', 'unicorn:restart' # app preloaded
- Add
. For the time of this writing, capistrano-unicorn gem has to be fetch from its GitHub repository, as released version (0.1.6) is buggy.
gem 'jquery-rails' gem 'prototype-rails' group :development do gem 'meta_request', '0.2.0' gem 'sqlite3' # Capistrano stuff gem 'capistrano' gem 'capistrano-unicorn', :git => 'https://github.com/sosedoff/capistrano-unicorn.git', :branch => 'master', :require => false end group :production do gem 'unicorn' gem 'mysql2'
Putting those in the :development
group is enough as production environment won’t need them.
Adapt Unicorn configuration
With our application deployed using Capistrano, the deployed application path on our production host is “/home/myuser/myapp_deployed/current”. Capistrano also requires the unicorn.rb configuration file to be by default in “config/unicorn/production.rb”. So here are a few changes to do.
- Move your unicorn configuration file:
[@development:/path/to/myapp]> mkdir config/unicorn [@development:/path/to/myapp]> mv config/unicorn.rb config/unicorn/production.rb
- Adapt unicorn configuration with the new deployed path. This has to be adapted to your specific configuration needs.
# Production specific settings if env == "production" # Help ensure your application will always spawn in the symlinked # "current" directory that Capistrano sets up. working_directory '/home/myuser/myapp_deployed/current' # feel free to point this anywhere accessible on the filesystem user 'myuser', 'mygroup' shared_path = '/home/myuser/myapp_deployed/current' stderr_path '/home/myuser/myapp_deployed/current/log/unicorn.stderr.log' stdout_path '/home/myuser/myapp_deployed/current/log/unicorn.stdout.log' end
Deploy your application
In this section, commands run should deploy your application. For each command, check the output in detail and do not issue next commands if you encounter errors with previous ones. In case of errors, investigation and correction is needed before continuing.
- Commit modified files in your local Git repository.
[@development:/path/to/myapp]> git add -A [@development:/path/to/myapp]> git commit -m"Added Capistrano support for mysite.com"
- Push your repository to your production host
[@development:/path/to/myapp]> git push production master
- Setup Capistrano for its first time use. This step is not needed for subsequent deploys.
[@development:/path/to/myapp]> cap deploy:setup [@development:/path/to/myapp]> cap deploy:check
If you get the error
cannot load such file -- capistrano-unicorn (LoadError)
, using thebundle exec
command in front of all yourcap
commands:[@development:/path/to/myapp]> bundle exec cap deploy:setup [@development:/path/to/myapp]> bundle exec cap deploy:check
- Deploy using Capistrano:
[@development:/path/to/myapp]> cap deploy
If you have database migrations to be run, use the following instead:
[@development:/path/to/myapp]> cap deploy:migrations
And that’s it! You should have your running Unicorn server on your production host, installed in /home/myuser/myapp_deployed/current.
Subsequent deploys can be performed, following the same commands from your development host only:
[@development:/path/to/myapp]> git add -A [@development:/path/to/myapp]> git commit -m"New commit" [@development:/path/to/myapp]> git push production master [@development:/path/to/myapp]> bundle exec cap deploy:migrations
Useful links
In case of problems, here are some links that helped me a lot in setting this up: