Rails production server setup and deployment on Ubuntu/Debian

Please digg this story to spread the word! Thanks!

Okay, this is a big one! This article will show you (and explain to you) how to setup a Ruby on Rails production server with Ubuntu 7.04 or Debian 4.0 and how to deploy your Rails application there.

First, what’s getting installed:

  • Ruby 1.8.5
  • Ruby on Rails 1.2.3
  • Subversion 1.4
  • MySQL 5.x Server
  • Apache 2.2.x
  • Mongrel Cluster

I assume that you have just installed a fresh system with Ubuntu Linux 7.04 or Debian 4.0. If you haven’t, do so now! You don’t need to install the “DNS” or “LAMP” server in Ubuntu. Just a minimal system is enough for this tutorial.

I’ll be deploy an imaginary Rails application named “myapp” which uses MySQL and is stored in Subversion. More on that later on.

Well, let’s get going and get that Ruby on Rails server ready.

Update your system

Before you do anything, use apt-get to update your system to the latest possible version.

sudo apt-get update
sudo apt-get dist-upgrade

This probably installs a new kernel (linux-kernel) so a reboot may be in order for optimal performance.

Enable SSH

Most people will want to have SSH on their server to login remotely. In this case I install both the server and client so you can SSH out if you need to:

$ sudo apt-get install openssh-server openssh-client

You will now be able to login remotely wiht SSH.

You’ll need SSH later if you want to use Capistrano to deploy your Ruby on Rails application. In any case, SSH is a good thing to have around.

Subversion

If you are serious about your Ruby on Rails server, you want to have Subversion around. Most deployment scripts pull the latest revision of your code from subversion. No configuration needed here.

$ sudo apt-get install subversion

We only need the client on the production server. We’re not going to host Subversion repositories here.

Install MySQL Server

This is the first serious step you’ll have to take. Both Ubuntu 7.04 and Debian 4.0 come with MySQL 5.0.x.

$ sudo apt-get install mysql-server mysql-client libmysqlclient15-dev

Be sure to set a password for the root MySQL user. Failing to do so will leave your database open for anyone who wishes to see.

$ mysqladmin -u root -h localhost password 'secret'
$ mysqladmin -u root -h myhostname password 'secret'

Make sure to replace secret with your actual password.

Try logging in to MySQL with your new password to make sure everything works okay.

$ mysql -u root -p
Enter password:
mysql>

MySQL is in place now, so let’s get cracking at Ruby and Rails now.

Ruby, Gems, Rails

Installing Ruby is quite easy:

$ sudo apt-get install ruby

You’ll now have Ruby 1.8.5. You will also need to install some other develoment package to help you build native Ruby Gems.

$ sudo apt-get install make autoconf gcc ruby1.8-dev build-essentials

I’ll install ruby gems the conventional way (so I’m not going to use Ubuntu’s packages here). Download the latest Gems .tgz here.

$ wget http://rubyforge.org/frs/download.php/17190/rubygems-0.9.2.tgz
$ tar xvf rubygems-0.9.2.tgz
$ cd rubygems-0.9.2/
$ sudo ruby setup.rb
$ gem -v

That’s it for the gems. Now, install Rails an all it’s dependencies:

$ sudo gem install rails --include-dependencies

If you get an error message complaining that the ‘rails’ gem cannot be found include the –remote option.

Oh no! More gems!

Next, let’s install some essential Ruby Gems that will make your life quite a bit easier. Here we’ll install the following gems:

  • mysql – For good MySQL connectivity
  • capistrano – Just to have it handy when needed
  • mongrel – Rails server
  • mongrel-cluster – To operate mongrel clusters
$ sudo gem install mysql capistrano mongrel mongrel-cluster --include-dependencies

You’ll be asked several times to choose a version for different gems. Always choose the latest available version for ‘ruby’. (Don’t choose win32. I don’t need to explain why.)

Test Rails and MySQL operability

Before you continue you may want to take some time to test Rails and MySQL. It’s not essential, but I recommend it because it will save you a lot of trouble later on.

Create a new Rails application in your homedir and a create the corresponding MySQL database. Also edit config/database.yml to reflect your root password!

$ mysqladmin -u root -p create testapp_development
$ mkdir testapp
$ rails testapp
$ cd testapp
$ vi config/database.yml
$ rake db:migrate

If you get any errors from that last command, check the previous steps. Normally, all should be fine and you can continue safely.

Most people configure a ’socket’ in their config/database.yml for MySQL. Note that this socket is in different places for different distribution. Ubuntu and Debian keep it in: /var/run/mysqld/mysqld.sock. You may need to update your configuration in order to connect to the database.

Apache 2.2

The good thing about Ubuntu/Debian is that they both include Apache 2.2.x now. This branch of Apache includes the a balancing proxy, which allows you to distribute your workload over several Mongrel servers (in your mongrel cluster). I’ll come back to that later.

$ sudo apt-get install apache2

Before you continue, enable several modules which we’ll be using later on.

$ sudo a2enmod proxy_balancer
$ sudo a2enmod proxy_ftp
$ sudo a2enmod proxy_http
$ sudo a2enmod proxy_connect
$ sudo a2enmod rewrite

That’s it for now on Apache. Let’s move along.

Prepping the Rails application

Okay, let’s prepare the Rails application ‘myapp’ for deployment now, shall we?

First, create a production database on your server, and configure it in config/database.yml:

$ mysql -u root -p
> create database myapp_production;
> grant all privileges on myapp_production.* to 'myapp'@'localhost'
identified by 'secret_password'

The next step is to install capistrano. You can install it on your development machine as a gem, as demonstrated above. Next, apply capistrano to your application:

$ sudo gem install capistrano
$ cap --apply-to myapp

Take a look at myapp/config/deploy.rb. This file contains the configuration for the deployment of your application. Take special care of the following, here’s an example for ‘myapp’:

require 'mongrel_cluster/recipes'
set :application, "myapp"
set :repository, "http://svn.myhost.com/svn/#{application}/trunk"
# We only have one host
role :web, "myapp.com"
role :app, "myapp.com"
role :db,  "myapp.com", :primary => true
# Don't forget to change this
set :deploy_to, "/home/ariejan/apps/#{application}"
set :mongrel_conf, "#{current_path}/config/mongrel_cluster.yml"

As you can see, I’ve already included several lines that enable the use of mongrel, we’ll get to that next.

Make sure you adapt this file to your own needs. myapp.com is the address of the server you’re going to deploy your application to. the mongrel_cluster.yml file will be created in a moment.

On the server, make sure you create the ‘apps’ directory. You can now setup a basic file structure for the deployment:

$ cd myapp
$ cap setup

On your server, you’ll notice that the /home/ariejan/apps/myapp directory was created, including some subdirectories.

If you are annoyed with entering your SSH password every time, create and upload your public SSH key to automate this. (I’ll write something up about that later on.)

Now, configure mongrel. For a normal setup, with moderate traffic, you can handle all traffic with two mongrel instances. The mongrel servers will only be accessable through ‘localhost’ on the server on non-default ports. Apache will do the rest later.

In your rails app, run the following command:

$ mongrel_rails cluster::configure -e production -p 9000 \
-a 127.0.0.1 -N 2 -c /home/ariejan/apps/myapp/current

The configuration file we saw earlier has been created. Check-in all new files in to subversion now, and cold deloy your application!

$ cap cold_deploy

The deployment will checkout the most recent code from Subversion and start the mongrel servers.

After the deployment, migrate your production database and restart the mongrel cluster:

$ cap migrate
$ cap restart

To check that your application is running, issue the following command on your server. It should return you the HTML code from your app:

$ curl -L http://127.0.0.1:9000

Configure Apache and the Balacing Proxy

You have two mongrel servers running, ready to handle incoming requests. But, you want your visitors to use ‘myapp.com’ and not an IP address with different port numbers. This is where apache comes in.

Create a new file in /etc/apache2/sites-availbale named ‘myapp’ and add the following:

<proxy>
  BalancerMember http://127.0.0.1:9000
  BalancerMember http://127.0.0.1:9001
</proxy>
 
<virtualhost>
  ServerName myapp.com
  ServerAlias www.myapp.com
 
  DocumentRoot /home/ariejan/apps/myapp/current/public
 
  <directory>
    Options FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
  </directory>
 
  RewriteEngine On
 
  RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
  RewriteCond %{SCRIPT_FILENAME} !maintenance.html
  RewriteRule ^.*$ /system/maintenance.html [L]
 
  RewriteRule ^/$ /index.html [QSA]
 
  RewriteRule ^([^.]+)$ $1.html [QSA]
 
  RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
  RewriteRule ^/(.*)$ balancer://myapps_mongrel_cluster%{REQUEST_URI} [P,QSA,L]
 
  ErrorLog /home/ariejan/apps/myapps/shared/log/tankfactions_errors_log
  CustomLog /home/ariejan/apps/myapps/shared/log/tankfactions_log combined
</virtualhost>

Now enable this new site in apache:

$ sudo a2ensite myapp
$ /etc/init.d/apache2 force-reload

In some cases you may need to make a small change to /etc/apache2/mods-enabled/proxy.conf and swap

Order deny,allow
Deny from all

for

Order allow,deny
Allow from all

That’s all, you can now access your app on myapp.com!

Maintaining your application

Now, happily develop your application and make update (you check them in to Subversion). To update your web server run:

$ cap deploy

If you made changes in the database, you may want to run a long_deploy:

$ cap long_deploy

And if for some reason, your mongrel cluster dies, just restart it.

$ cap restart

That’s it! Happy hacking :)

Please digg this story to help spread the word! Thanks a lot!

Comments can be posted here or in my new and shiny Google Group!

  • Twitter
  • Digg
  • del.icio.us
  • DZone
  • Reddit
  • email

31 Responses to “Rails production server setup and deployment on Ubuntu/Debian”

  1. Jonas says:

    Why not use the deprec gem or use a ready for use image of some linux dist and then configure the server later on instead?

  2. Ariejan says:

    @Jonas: You want to make your Rails app public now, not “later”. The idea is to show the entire process here.

    I’m not sure what you mean by deprec gem?

  3. blackant says:

    The deprec gem (for Debian systems) will do almost all of this work for you. Your writeup is very good, but it should be noted that there is another (simpler) alternative.

  4. Augusto says:

    heya, nice tutorial, just a small correction: at the beginning, in the “Ruby, gems, rails” part, the correct command is

    $ sudo apt-get install make autoconf gcc ruby1.8-dev build-essential

    “build-essential”, without the last ’s’.

  5. Shot says:

    Why build RubyGems from source, when there’s a rubygems package right there in the distribution?

  6. Matt says:

    Nice job, thanks again for a great post.

  7. Ariejan says:

    @Shot: RubyGems is updated regularly. Good practice is not to update packages you installed yourself. I want to keep my RubyGems up-to-date, without waiting for Debian or Ubuntu to serve up new packages.

    It’s just a flexibility thing.

  8. Dave says:

    Hi!

    Thanks for the great tutorial! I’m trying to setup a server to run two different rails apps. Do you know how I can configure apache and mongrel to accept two separate virtual hosts?

    Thanks!

  9. Ariejan says:

    @Dave: If you’re using Apache, it’s very easy.

    1. Setup a new virtual host in apache, this is very well documented, and rather easy.
    2. Setup mongrel for your second app, but change the port numbers mongrel_cluster uses. In the article I used 9000 and 9001. Change them to 9100, for example.
    3. Make the proper changes to your balacing proxy configuration (they need the correct port numbers)

    Now, restart apache, and there you are!

  10. Dave says:

    Thanks Ariejan! I had run across into one small problem where I had called the mongrel clusters the same name, so sometimes the request was hitting the wrong mongrel cluster! :) Thanks again!

  11. Cameron says:

    Great guide Ariejan.

    However, when installing stuff using gem I was getting a lot of ruby errors about rdoc not available.

    I’d suggest that when installing Ruby, you also install irb and rdoc:

    sudo gem install ruby irb rdoc.

    Cheers,

    Cameron

  12. CZ says:

    When I run sudo gem install mysql capistrano mongrel mongrel-cluster --include-dependencies (or just plain sudo gem install mysql) I get the following errors:

    Building native extensions. This could take a while...
    ERROR: While executing gem ... (Gem::Installer::ExtensionBuildError)
    ERROR: Failed to build gem native extension.

    ruby extconf.rb install mysql capistrano mongrel mongrel-cluster --include-dependencies
    extconf.rb:1:in `require': no such file to load -- mkmf (LoadError)
    from extconf.rb:1

    Gem files will remain installed in /usr/lib/ruby/gems/1.8/gems/mysql-2.7 for inspection.
    Results logged to /usr/lib/ruby/gems/1.8/gems/mysql-2.7/gem_make.out

    The gem_make.out file just reiterates ruby extconf.rb install mysql capistrano mongrel mongrel-cluster --include-dependencies
    extconf.rb:1:in `require': no such file to load -- mkmf (LoadError)
    from extconf.rb:1

    I found another site that mentioned that the path to the mysql_config might need to be added (http://wiki.rubyonrails.org/rails/pages/MySQL+Database+access+problem). After a search I found mysql_config in my /usr/bin but even adding the correct path gives me the above error. Any suggestions?

  13. Felix says:

    The gem line for “mongrel-cluster” should read “mongrel_cluster” – i.e. underscore instead of hyphen.

  14. Travor says:

    Hi Ariejan,I got the same errors as CZ,any suggestions?

  15. Travor says:

    To CZ:
    You should execute “sudo apt-get install ir rdoc” before you execute “sudo ruby setup.rb”.

  16. Christos says:

    @CZ #12

    I just hit the same problem and have now solved it. Follow the advise here:

    http://ubuntuforums.org/archive/index.php/t-210558.html

    I installed the mysql client first, then the mysql gem.

    Hope that helps!

  17. CZ says:

    Thanks to Travor and Christos!

    I actually still had problems when I tried running a ‘rake db:migrate’. After another search, I found a site that recommended uninstalling and reinstalling the mysql gem and now I’m able to rake a migration! (After all this time, this calls for a drink!)

  18. Hi!

    I was wondering if it is possible to set up this configuration, for handling rails websites, and at the same time use the apache server for managing my php websites?

    Thanks by the way for this extensive guide!

    – Niels

  19. Ariejan says:

    Hi!
    I was wondering if it is possible to set up this configuration, for handling rails websites, and at the same time use the apache server for managing my php websites?
    Thanks by the way for this extensive guide!
    – Niels

    Niels, of course! Just install mod_php like you’d normally do and add new virtual hosts. Works perfectly.

  20. David K. says:

    Just a note to those finding this tutorial now, the capistrano directive “–apply-to” is dead. You should instead use “capify”.

    Rock on.

  21. [...] how I’m going to do it. I’d recommend reading this post when it comes to installing ruby, rails and gems on Debian/Ubuntu but forget any of that Capistrano/Apache nonsense that they talk about. I recommend vlad an nginx [...]

  22. rufio says:

    Hi, can I set up for more applications (domains). If Yes, do U know how? Thanks.

  23. @rufio: Yes, just create multiple virtualhosts and proxy definitions. Make sure you run your mongrels on different ports.

  24. Very thorough tutorial. Thanks for posting it. Another series of Deployment 101 articles that might be helpful is posted here:

    http://jupiterit.com/wordpress/?p=190

    I documented all the problems I had with deployment and created a step by step guide for each stage of deployment. Hope its helpful.

  25. Jon says:

    Thanks for a great writeup; I tried several other instructions before a I found yours.

    I have one problem: I got all the way thru, but now apache won’t start because there is no httpd.conf file in /etc/apache2…

    Was that supposed to be automagically created during the install or at some other point? Sort of looked as if it was supposed to be handled in the sites-available file… If not, do you have a quick punch list for it?

    Thanks much…jon

  26. @Jon: In most cases httpd.conf is generated automatically. Some systems use apache2.conf. Just create an empty httpd.conf file and see if apache accepts that.

  27. Jon says:

    Ariejan… Thanks for the feedback; ‘touch httpd.conf’ did the trick (and I found that it is Included from apache2.conf.

    A quick fix: I found that two of your directives had to be updated:

    proxy becomes:

    and the directives required a directory name; i.e.,

    just to resolve syntax errors.

    However, I have been completely unable to connect apache & mongrel, and I have tested numerous configurations.

    Right now, from the Linux box:
    http://edplist/ ==> Forbidden “/” (I do have a directive in there for “/” allowing all
    and
    http://edplist:9000/ ==> my rails application

    Any suggestions? Thanks again for a great article!

  28. thank you ..
    useful notes, keep your information uptodate ..

  29. arasu says:

    thanks for the tutorial.

    well, i have problem in my VirtualBox where i’m using Ubuntu. I already installed capistrano but when i run this command :-

    analogkid@ubuntu:~$ cap –apply-to question

    its returning error like this :-

    /usr/local/lib/site_ruby/1.8/rubygems.rb:578:in `report_activate_error’: Could not find RubyGem echoe (>= 0) (Gem::LoadError)
    from /usr/local/lib/site_ruby/1.8/rubygems.rb:134:in `activate’
    from /usr/local/lib/site_ruby/1.8/rubygems.rb:158:in `activate’
    from /usr/local/lib/site_ruby/1.8/rubygems.rb:157:in `each’
    from /usr/local/lib/site_ruby/1.8/rubygems.rb:157:in `activate’
    from /usr/local/lib/site_ruby/1.8/rubygems.rb:158:in `activate’
    from /usr/local/lib/site_ruby/1.8/rubygems.rb:157:in `each’
    from /usr/local/lib/site_ruby/1.8/rubygems.rb:157:in `activate’
    from /usr/local/lib/site_ruby/1.8/rubygems.rb:49:in `gem’
    from /usr/bin/cap:18

  30. roger pack says:

    definitely try mod_rails–it is actually friendly to the user :)

Leave a Reply