Rails on Red Hat Enterprise Linux HOWTO
Installation
First off, insure you have ruby installed. The version shipped with RHEL4 is 1.8.1. You need at least version 1.8.2 for rails to work properly. Ruby 1.8.4 was released in December of 2005. You can download the isos from centos’s testing repository for RHEL here:
wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-1.8.4-1.c4.i386.rpm wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-docs-1.8.4-1.c4.i386.rpm wget http://dev.centos.org/centos/4/testing/i386/RPMS/ri-1.8.4-1.c4.i386.rpm wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-libs-1.8.4-1.c4.i386.rpm wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-mode-1.8.4-1.c4.i386.rpm wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-tcltk-1.8.4-1.c4.i386.rpm wget http://dev.centos.org/centos/4/testing/i386/RPMS/irb-1.8.4-1.c4.i386.rpm wget http://dev.centos.org/centos/4/testing/i386/RPMS/rdoc-1.8.4-1.c4.i386.rpm wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-devel-1.8.4-1.c4.i386.rpm
Rails is so new, and cutting-edge, that rpms do not yet exist for rubygems. I did find one RPM that Mandrake had managed to produce, however, neither the infinitely awesome dag nor the regularly awesome pbone archives had produced one for RHEL yet. So, you have do it the old school way and install from source. Dennis Ritchie would be proud. I downloaded the latest version from rubyforge using the venerable wget.
% wget http://rubyforge.org/frs/download.php/5207/rubygems-0.8.11.tgz % tar zxf rubygems-0.8.11.tgz % cd rubygems-0.8.11 % sudo ruby setup.rb
Installing Gems
Next, you’ll need to install rails, and the MySQL bindings from gem. Make sure you have mysql-devel-4.1.12-3.RHEL4.1 installed from disc 4 before doing this.
% rpm -q mysql-devel
mysql-devel-4.1.12-3.RHEL4.1
% sudo gem install rails
% sudo gem install mysql -- --with-mysql-include=/usr/include/mysql\
--with-mysql-lib=/usr/lib/mysql --with-mysql-config
There are a lot of very useful packages that are available through the gems. So, as you need helpers and additional packages, you can install them through the gem interface. It’s worked great for me so far.
Question: Doesn’t ActiveRecord include the native drivers for MySQL?
Answer: Absolutely not. If you don’t compile these, rails will throw an error when trying to connect to MySQL
SQLite
I updated dag’s spec file and compiled the sqlite-3.3.5 source into a RHEL4 RPM. You’ll need to install these and the sqlite gem. If you still need sqlite < 3, don’t install these as I didn’t update the name from sqlite to sqlite3.
% wget http://rpm.involution.com/sqlite3/sqlite-3.3.5-1.el4.i386.rpm % wget http://rpm.involution.com/sqlite3/sqlite-devel-3.3.5-1.el4.i386.rpm % sudo rpm -ivh sqlite-* % sudo gem install sqlite3-ruby
Installing FastCGI
I compiled fastcgi from source which I hate doing. I always choose RPM over a source install, but at the time I couldn’t find any spec files or rpms for FastCGI. To compile mod_fastcgi, you’ll need httpd-devel which requires pcre-devel, apr-devel, and apr-util-devel as dependencies.
% wget http://www.fastcgi.com/dist/fcgi-2.4.0.tar.gz % cd fcgi-2.4.0 % ./configure % make % sudo make install % wget http://www.fastcgi.com/dist/mod_fastcgi-2.4.2.tar.gz % cd mod_fastcgi-2.4.2 % rpm -q httpd-devel % httpd-devel-2.0.52-22.ent % cp Makefile.AP2 Makefile % perl -p -i -e 's,top_dir.*,top_dir = /usr/lib/httpd,' Makefile % make % sudo make install % mkdir /tmp/fcgi_ipc/ % sudo chown apache.apache /tmp/fcgi_ipc/ % sudo service httpd restart
Apache Configuration for Pound and mongrel_cluster
Apache is configured as passing back all requests to the Pound load-balancer as follows (assuming Pound is running on port 4000).
<VirtualHost *:80>
ServerName rails.involution.com
ProxyRequests Off
ProxyPreserveHost On
RewriteEngine On
RewriteRule ^/(.*) http://127.0.0.1:4000/$1 [P,L]
ProxyPassReverse / http://127.0.0.1:4000/
ErrorLog logs/error_log
CustomLog logs/access_log combined
</VirtualHost>
Then, I invoked pound at start-up in /etc/rc.local. This should eventually be jiggered into a run control script in /etc/init.d.
/usr/local/sbin/pound -f /etc/pound.cfg
Performance Tuning FastCGI
Most people run Rails under FastCGI in “Static Mode” for performance reasons. Static mode implies that there are some number of static FastCGI processes running on your server at all times. This prevents the overhead of process invocation every time your infinitely popular Rails application is loaded by your cadre of users. Here is what I put in /etc/httpd.conf to make FastCGI run in static mode.
<IfModule mod_fastcgi.c>
FastCgiIpcDir /tmp/fcgi_ipc/
FastCgiServer /path/to/rails/public/dispatch.fcgi -appConnTimeout 0 -processes 2 -listen-queue-depth 100 -flush
FastCgiConfig -maxProcesses 50 -minProcesses 6 -appConnTimeout 0
AddHandler fastcgi-script .fcgi
</IfModule>
Trying it Out
% rails hoyhoy
rails hoyhoy
create
create app/controllers
...
config/database.yml
...
I’m a super permissions nerd, and I cringe everytime I see anyone execute a recursive 777 chmod. Also, I will admit to having turned off SELinux on my server because if I had to type “chcon -R user_u:object_r:httpd_user_content_t .” one more time, I would have stopped using computers and started a new career at subsistence farming. Sorry, I’m getting off topic here. Here is the RIGHT way to set rails permission in RHEL. Note, I’m RHCE certified, so, I have a piece of paper that looks nice in a frame, and it gives me credentials to feel like I’m an authority.
% sudo chgrp -R apache hoyhoy/
% chmod -R g+r hoyhoy/
% chmod -R g+w hoyhoy/log
% find hoyhoy/ -type d chmod g+x {} \;
% chcon -R user_u:object_r:httpd_user_content_t hoyhoy/
That may seem a bit tedious, but it’s the most secure and sane way that I’ve found to set permissions. You can point an Apache Virtualhost entry directly to that directory, and it should work like the clappers. You only need the chcon command if you still have SELinux enabled (it’s on by default). You may also be wondering about the find command. I do that to only set the execute bit of directories. Do not set the execute bit on everything. It’s a bad idea because it opens up potential security holes. The risk is slight, however, there’s no point in granting a permission on something that it doesn’t require. You wouldn’t give your toddler a key to the gun cabinet would you?
Lighttpd
I ended up installing Lighttpd as well because one of the people using my server requested it. I got the RPMs from Dag’s infinitely excellent RPM repository.
wget http://apt.sw.be/redhat/el4/en/i386/RPMS.dag/lighttpd-1.3.16-1.2.el4.rf.i386.rpm wget http://apt.sw.be/redhat/el4/en/i386/RPMS.dag/lighttpd-fastcgi-1.3.16-1.2.el4.rf.i386.rpm wget http://apt.sw.be/redhat/el4/en/i386/RPMS.dag/lighttpd-mod_mysql_vhost-1.3.16-1.2.el4.rf.i386.rpm rpm -ivh lighttpd*.rpm
Then, I setup some lighttpd VirtualHosts Proxies on the same Apache server that I was running the pure Apache+FastCGI code on. I actually even ran the same instance of Typo twice one on Lighttpd and another on Apache+FastCGI.
<VirtualHost *:80>
ServerName typo-l.involution.com:80
ProxyRequests Off
ProxyPreserveHost On
RewriteEngine On
RewriteRule ^/(.*) http://127.0.0.1:3334/$1 [P,L]
ProxyPassReverse / http://127.0.0.1:3334/
ErrorLog logs/error_log
CustomLog logs/access_log combined
</VirtualHost>
Troubleshooting
The most trouble I had was with database settings. Quadruple check the database.yml file and try out the settings on the mysql command line. The other trick you can do is execute the disptach.cgi or dispatch.fcgi directly on the command line to see if the ruby environment is invocated correctly. This will verify about 95% of most of the problems you could potentially have.
Here’s another tip, if you’re getting an httpd error like:
Error 500: Internal Server Error
Or some weird message from FastCGI like
[client 127.0.0.1] FastCGI: incomplete headers (35 bytes) received from server".
Check your config/database.yml. Insure that the database it’s pointing to has been created, and the user referenced can both read and write to all of the databases referenced. I have screwed this up at least 10 times, and I’ve been running a web server since 1996. There’s no shame in messing it up, however, there is shame in immediately blaming rails, your operating system, Apache, ruby, or Dave Thomas if you haven’t prevericated your database settings. Let me reiterate, check your database.yml! You’ve been warned.
Also, insure that mysql is actually running.
% /sbin/service mysqld status mysqld (pid 19253) is running...
The rails and httpd error logs are also very valuable guides when you’re getting strange errors. On RHEL, the httpd error log is by default here (unless you’ve overridden it):
/var/log/httpd/error_log
If you don’t create your /tmp/fcgi_ipc directory, you’ll see something like this in your error_log:
[Fri May 05 18:03:15 2006] [crit] (13)Permission denied: FastCGI: can't create server
Another problem you can encounter is directory permissions. If you install your rails app in a user directory, then the apache userid needs to be able to chdir to that directory. So, if your $HOME is go-wrx, this will case FastCGI to error out with something like this:
FastCGI: can't start server "/home/user/rails/public/dispatch.fcgi" (pid 15396), chdir() failed: Permission denied
To fix this, change your permissions like this for $HOME.
sudo chgrp apache ~user chmod g+rx ~user
Yet another problem that can happen is getting the dreaded “Internal Server Error: 500″. This happens a lot with Mongrel especially if you forget to set the mode to production when a database isn’t defined. I hit this running Instiki, and even with Mongrel in debug mode, it gave no indication of what the problem was in any of the log files. This is also true from Apache and Lighttpd as well. If you see this error from your app, check the database settings.
% mongrel_rails start -e production
Rails 1.1.2
I was running Ruby 1.8.3 which was incompatible with Rails 1.1.2. So, I updated with the CentOS RPMs….
wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-1.8.4-1.c4.i386.rpm wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-docs-1.8.4-1.c4.i386.rpm wget http://dev.centos.org/centos/4/testing/i386/RPMS/ri-1.8.4-1.c4.i386.rpm wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-libs-1.8.4-1.c4.i386.rpm wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-mode-1.8.4-1.c4.i386.rpm wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-tcltk-1.8.4-1.c4.i386.rpm wget http://dev.centos.org/centos/4/testing/i386/RPMS/irb-1.8.4-1.c4.i386.rpm wget http://dev.centos.org/centos/4/testing/i386/RPMS/rdoc-1.8.4-1.c4.i386.rpm wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-devel-1.8.4-1.c4.i386.rpm rpm -Uvh *.rpm
Then, I did the normal dance.
sudo gem update --system sudo gem update rake sudo gem update rails
For some strange reason, it made me update rake before updating Rails. Weird.
Mongrel
sudo gem install mongrel cd ~/railsapp mongrel_rails start -p 4000
I hacked Apache to Proxy out the request as follows:
ProxyPass / http://localhost:4000 ProxyPassReverse / http://localhost:4000
Pound and Mongrel Cluster
I setup Pound 2.1 so I could run a proxy balanced mongrel cluster under Apache. This allows concurrent application threads to process requests and prevents against any single Rails application process failing.
First, I downloaded and compiled Pound.
% wget http://www.apsis.ch/pound/Pound-2.1.tgz % cd Pound-2.1 % ./configure % make % sudo make install
Then, I made a Pound configuration file.
User "apache"
Group "apache"
ListenHTTP
Address 0.0.0.0
Port 4000
Service
BackEnd
Address 127.0.0.1
Port 8000
End
BackEnd
Address 127.0.0.1
Port 8001
End
BackEnd
Address 127.0.0.1
Port 8002
End
End
End
Next, I installed and configure mongrel_cluster, and invoked Pound.
% sudo gem install mongrel_cluster % cd /home/user/railsapp % mongrel_rails cluster::configure -p 8000 -N 3 -e production \ -l /home/user/railsapp/mongrel.log -m /etc/mime.yaml -u apache -g apache % mongrel_rails cluster::start % pound -f /etc/pound.cfg
Since I’m running Apache on port 80, I had to then proxy all connections to my Rails application’s VirtualHost name to the Pound proxy.
<VirtualHost *:80>
ServerName ninjarailsapp.com:80
ProxyRequests Off
ProxyPreserveHost On
RewriteEngine On
RewriteRule ^/(.*) http://127.0.0.1:4000/$1 [P,L]
ProxyPassReverse / http://127.0.0.1:4000/
ErrorLog logs/ninja_error_log
CustomLog logs/ninja_access_log combined
</VirtualHost>
So, my setup conceptually looks like this.

Troubleshooting Pound
Make sure that pound has permission to write into /var/run or you will get an error like this in /var/log/messages.
pound: Create "/var/run/pound.pid": Permission denied
Either run pound as root or set the User and Group in your configuration file. Pound does not display an error to stdout. So, if it fails, look for these types of errors in your syslog messages if you cannot otherwise connect to the port that you intended to start it on.
Apache 2.2 Configuration for mod_proxy_balancer and mongrel_cluster
I moved to using Apache 2.2′s mod_proxy_balancer instead of Pound. I installed mod_proxy and mod_proxy_balancer and then configured my VirtualHost as follows.
<VirtualHost *:80> ServerName rails.involution.com ProxyRequests Off ProxyPreserveHost On RewriteEngine On RewriteRule ^/(.*) balancer://mybalancer/$1 [P,QSA,L] ProxyPass / balancer://mybalancer ProxyPassReverse / balancer://mybalancer ErrorLog logs/error_log CustomLog logs/access_log combined <Proxy balancer://mybalancer> BalancerMember http://127.0.0.1:8000 BalancerMember http://127.0.0.1:8001 BalancerMember http://127.0.0.1:8002 </Proxy> </VirtualHost>







[...] Involution ยป Rails on Red Hat Enterprise Linux HOWTO Covers Apache, Rails, Mongrel, Pound, Ruby… pretty much everything except what I need, unfortunately. (tags: howto linux ruby rubyonrails) [...]
Had some issues with Pusion passenger module for Apache.
But this worked great for me.
Thankyou very much for a superb install guide for RoR
You can tell a lot of effort went into the installation and documenting everything.
I even enjoyed the bit where you were going to give up on IT completely and take up farming :). Mirroring my thoughts sometimes.
Colin
It is 2012 now and this article still made my day. I was almost ready to ‘setenforce 0′ and be done with it. What I was missing the httpd_user_content_t part.
Thanks for documenting everything mate.
Yeah, over six years later, and SELinux is still as arcane as ever!