How to set up RoR application for production mode

Hi everyone! This is my first post, so, do not judge me strictly.

Here is my experience of how to set up a rails application for production mode. First of all I must admit that this is my first deployment. Please, take into account that all operations were conducted on just installed ubuntu-server 14.04 as a virtualbox machine.

This post might be useful for those who want to deploy a rails app on its own server instead of pushing it to heroku, for example.

After hard days of development and tests I decided that my sTracker application is ready to be presented to the world. At this point I have the application which is working well in development mode.

Rails

Suppose, we have latest sources on ubuntu server in /opt/projects/stracker folder. We need to figure out if everything work well. We have the working application on our system, but it may not work on a new environment. The best way to check what is broken is to conduct tests.

cd stracker
snake@userv:/opt/projects/stracker$ rake tests
The program 'rake' is currently not installed. You can install it by typing:
sudo apt-get install rake

As expected, since the system is virgin, there is nothing except standard packages. We have to install ruby. As RoR site suggests the best practice is using rbenv. Here is the tutorial how to install rbenv. My steps are:

git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc

Now it’s time to restart my shell so that PATH changes take effect. Usually I do just like this:

bash

Or you can just run without restarting current shell

source ~/.bashrc

Check if everything is ok:

snake@userv:/opt/projects/stracker$ type rbenv
rbenv is a function
...

Now we have installed rbenv. I recommend to install two plugins which will make our life easier. We need ruby-build and rbenv-sudo plugins:

git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build  
git clone git://github.com/dcarley/rbenv-sudo.git ~/.rbenv/plugins/rbenv-sudo

Now it’s time for ruby itself. We will install ruby 2.1.0, during the process of installation ruby may need some developing packages. Here is list which may help you:

sudo apt-get install gcc g++ make libssl-dev sqlite3 libsqlite3-dev
rbenv install 2.1.0

And set 2.1.0 as global version and install bundle and rails:

rbenv global 2.1.0
gem install bundle
gem install rails

After installing some gems you may see that they don’t work from command line. To overcome this you have to restart shell.

Now make sure we are in our application folder. We will install all gems are necessary for the app:

bundle install

Now test it if all is up and ok:

rake test

Hope everything is ok.

rake db:setup
rails server

Now we should have our fully working application. Check it via web browser.

Web server

Setting up the application to run in production mode is pretty challenging. The biggest challenge is to setup web server. We will deploy the app using apache and phusion passenger (https://github.com/phusion/passenger). Passenger it’s kind of replacement of WEBrick. It’s web server and as it stated on its web site: It is designed to be easy to use, fast, stable and reliable and it’s written in c++.

First of all, we need to install Passenger gem.

gem install passenger

After restarting shell, you should see passenger’s help tool like passenger-install-apache2-module . Start it and it will guide you via a process of an integration passenger into apache.

passenger-install-apache2-module

During this process you may face “Your system does not have a lot of virtual memory” error. So, make sure you virtualbox machine has more than 512mb of memory.

At the end of successful compilation process you should get:

   LoadModule passenger_module /home/snake/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/passenger-4.0.52/buildout/apache2/mod_passenger.so
   <IfModule mod_passenger.c>
     PassengerRoot /home/snake/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/passenger-4.0.52
     PassengerDefaultRuby /home/snake/.rbenv/versions/2.1.0/bin/ruby
   </IfModule>

Add these lines into your Apache configuration file. Go to /etc/apache2/mods-available folder and create two files: passenger.load and passenger.conf. Add LoadModule passenger_module ...mod_passenger.so string into passenger.load and add remaining text <IfModule mod_passenger.c>... </IfModule> into passenger.conf. Now we will enable passenger module:

sudo a2enmod passenger
sudo service apache2 restart

Make sure there are no errors.

Let’s config vhost. Suppose you have a web application in /somewhere. Add a virtual host to your Apache configuration file and set its DocumentRoot to /somewhere/public:

   <VirtualHost *:80>
      ServerName www.yourhost.com
      # !!! Be sure to point DocumentRoot to 'public'!
      DocumentRoot /somewhere/public    
      <Directory /somewhere/public>
         # This relaxes Apache security settings.
         AllowOverride all
         # MultiViews must be turned off.
         Options -MultiViews
         # Uncomment this if you're on Apache >= 2.4:
         #Require all granted
      </Directory>
   </VirtualHost>

It’s turn for site configuration. Go to /etc/apache2/sites-available, create st.conf, for example, and add following text:

<VirtualHost *:80>
      #userv is hostname of my virtualbox pc
      ServerName userv
      #this is path to my RoR app
      DocumentRoot /opt/projects/stracker/public
      <Directory /opt/projects/stracker/public>
         # This relaxes Apache security settings.
         AllowOverride all
         # MultiViews must be turned off.
         Options -MultiViews
         # Uncomment this if you're on Apache >= 2.4:
         Require all granted
     </Directory>
</VirtualHost>

Enable site:

sudo a2ensite st.conf
sudo service apache2 restart

Now check if site is up, input userv (hostname of our virtualbox pc) into your browser and see what you get. At this point I got 500 Internal Server Error. Despite the fact that we followed all instructions passenger helper provided us it doesn’t work.

Look at /var/log/apache2/error.log:

[ 2014-10-03 11:15:05.2952 4807/7fb4b79e5780 agents/Watchdog/Main.cpp:728 ]: All Phusion Passenger agents started!
App 4888 stdout:
App 4888 stderr: /usr/bin/env:
App 4888 stderr: ruby
App 4888 stderr: : No such file or directory
App 4888 stderr:

Passenger can’t find ruby. The folder we pointed in passenger.conf is correct and everything should work…To overcome this issue open passenger.conf and replace current PassengerDefaultRuby /home/snake/.rbenv/versions/2.1.0/bin/ruby with PassengerDefaultRuby /home/snake/.rbenv/shims/ruby. Restart apache and check if error goes away.

Refresh your browser and check if our app is working.

Mine doesn’t work. I faced missing secret_key_base issue:

App 4888 stderr: [ 2014-10-03 11:15:15.5546 4919/0x007f2746d71008(Worker 1) utils.rb:84 ]: *** Exception RuntimeError in Rack application objec
t (Missing `secret_key_base` for 'production' environment, set this value in `config/secrets.yml`) (process 4919, thread 0x007f2746d71008(Worke
r 1)):

If you look at config/secrets.yml you may see this:

production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

All we need is to add secret key into environmental value SECRET_KEY_BASE. I found out for myself two ways to do this in elegant way:

1. Using Profile.d

All wide-system variables could be added into /etc/profile.d. To generate secret key we need be inside our app folder and launch rake secret command. Then generated key should be added into .sh script in profile.d folder. To do it in one command line string input following:

echo "export SECRET_KEY_BASE=`rake secret`" | sudo tee /etc/profile.d/st_env.sh 

To made SECRET_KEY_BASE available without rebooting just restart your shell.

Anything you put in /etc/profile.d/ will be run, every time you open a login shell.

2. Using dotenv-deployment gem

There is a useful gem dotenv which work with .env file. In this file you can initialize any variables. They will be added into environment automatically when your app is started. There are two versions of this gem. Dotenv gem is mainly used in developing mode. For production mode there is dotenv-deployment gem. Create .env file in app root folder. Paste SECRET_KEY_BASE=<seckret> into it. Add gem 'dotenv-deployment', require: 'dotenv/deployment' into Gemfile. require: 'dotenv/deployment' is needed here because .env file is loaded only when require is called.

Ok, at this point we have SECKET_KEY_BASE added. Refresh your browser. You should see you RoR application. If not, check log/production.log

Unfortunately I faced two problems. First, I forgot to setup my DB and second CSS styles is not working:

RAILS_ENV="production" rake db:setup
RAILS_ENV=”production” rake assets:precompile

Reload apache and try your browser again. Hope now it works flawlessly