Ruby on Rails

From DreamHost
Revision as of 06:46, 13 May 2012 by Sampablokuper (Talk | contribs)

Jump to: navigation, search
Rails Logo

As of mid-2008, DH recommends that Rails apps be run under Passenger. See Passenger. However, as confirmed in an email from DreamHost on 13 May 2012, if you wish to deploy your Rails app to a shared server, and your app depends on a newer version of Rack than is installed on that shared server, then you will need to use FastCGI because Passenger on shared server will always use the system version of Rack: it is not currently possible for a DreamHost user on a shared host to specify that it should use a different version.

FastCGI, as described below, still works. But Passenger is far easier to configure and use. Tests show that it is as fast or faster than FastCGI. See Passenger website for more info. To get information on setting up a Rails application using Passenger, see Getting Started with Ruby on Rails.

You may choose to use FCGI if you wish to have your rails app on a sub-URI of a domain. For example, if you wish http://www.mydomain.com/rails/app1 to go to one rails app and http://www.mydomain.com/rails/app2 to go to a second app, FCGI may be for you.

If you are having issues with your Rails 3 site you should refer to the Rails 3 Troubleshooting Guide for help!


Using FastCGI

Note that using Passenger is now the preferred method of running Rails applications in DreamHost's system. Using FastCGI is not recommended.

Quick Start

Common Instructions

1. Make sure that FastCGI is enabled on your domain.

To do that, go to the Domains > Manage Domains section of your panel and then click the "Edit" link next to your domain. On the next page make sure "FastCGI Support?" is enabled, and then click the "Save Changes" button.

2. From your shell account, go to the home directory, type:

With the following commands, in italics, for example nameofyourproject is text that should be substituted for its real value.

rails new nameofyourrailsproject

If you want to have a rails application in a subdirectory on your domain, instead type:

ln -s ~/nameofyourrailsproject/public ~/nameofyourdomain/nameofyourrailsproject

Warning: if you take the advice above you must check that your private files are not publicly accessible (sometimes they are). To do that, type this address into your browser:

http://yourdomainname/nameofyourrailsproject/config/database.yml

If you can read the file this way, anyone can!

If you want to have a rails application at top-level of your domain, go to the control panel and set the folder of that domain to "nameofyourrailsproject/public"

Rails 3

Rails_3#Using_FastCGI

Older Versions

3. Change Permissions

chmod 755 public
chmod 755 public/dispatch.*

4. Set up your database Create your mysql database from the Dreamhost control panel. Change the production:, development:, and test: sections of the config/database.yml to contain the appropriate information for connecting to that database (white space is important- two spaces at the beginning of each line and none at the end):

  adapter: mysql
  database: dbname
  username: username
  password: password
  host: yourdb.yourdomain.com

(Source: gizmoojo)

5. Visit Your App!

No need to start an extra server like webrick or mongrel (which are not allowed on our shared servers), you can check out your site now by visiting your domain with a web browser.


Transferring an Existing Rails App

If you're transferring a locally working rails app to DH, it is recommend that you:

  1. create a dummy rails app using "rails myapp"
  2. copy the created "dispatch.fcgi" into your real app's public/ directory
  3. edit the hidden file called .htaccess in your public/ directory by changing the string "dispatch.cgi" to "dispatch.fcgi":
RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]

4. change all the permissions. From the public/ directory, type the following in the command line:

chmod 755 public
chmod 755 public/dispatch.*

5. upload your application and delete the dummy app

Note At least in one case when developing in rails 2.2.3 and then uploading to Dreamhost the app/controllers/application_controller.rb had to renamed to application.rb

Common Gotchas

1. Application Failed to Start

This is a very generic error message, so the best way to figure out what's going on is to run your dispatch file manually through the shell:

cd public/
./dispatch.fcgi

If running dispatch.fcgi from the command line results in

undefined method `downcase' for nil:NilClass

in your log/production.log file (http://www.joelpm.com/2007/5/23/mephisto-capistrano-svn-site5) try running:

REQUEST_METHOD=GET ./dispatch.fcgi

or possibly:

export REQUEST_METHOD=GET
ruby --debug dispatch.fcgi

If you see the HTML for a 500 Error that's usually OK, but you may see a Rails error trace instead. That'll usually point to a syntax error or other goof in the code. But if the dispatch file runs without any output you'll need to check your app's logs in the log/ directory to see what's causing the problem.

tail -50 log/production.log
tail -50 log/development.log
tail -50 log/fastcgi.crash.log

Note: It is strongly discouraged to run your Rails app in Development mode and FastCGI. There's some pretty wicked memory leaks that can result.

If you see the following error when you run your dispatch file, it means that your dispatch file isn't using Unix-friendly line endings:

: bad interpreter: No such file or directory

You can fix that by opening the file in a text editor like vi or emacs and then saving it in Unix format, or using the dos2unix command to convert the file like so:

dos2unix public/dispatch.fcgi

Other error messages are usually self-explanatory. And in the absolute worst-case scenario, you'll at least have something to toss at the fine folks that work for DreamHost Tech Support. You can contact them at any time in the Support > Contact Support section of your panel.

You may notice the following errors in your /home/username/logs/domain.com/http/error.log file:

[...] [error] [client ...] FastCGI: comm with (dynamic) server ".../current/public/dispatch.fcgi" aborted: (first read) idle timeout (60 sec)
[...] [error] [client ...] FastCGI: incomplete headers (0 bytes) received from server ".../current/public/dispatch.fcgi"

This is a very generic error that literally translates to "something went wrong with something somewhere". The above troubleshooting steps will help out with this, too.

One last thing to try out is to add the following to your dispatch.fcgi file (but before the RailsFCGIHandler.process! line):

 class RailsFCGIHandler
 private
   def frao_handler(signal)
     dispatcher_log :info, "asked to terminate immediately"
     dispatcher_log :info, "frao handler working its magic!"
     restart_handler(signal)
   end
   alias_method :exit_now_handler, :frao_handler
 end

If you have a custom Rails gem installed (2.0.2 in my case) locally (if you don't know what this means this is not applicable to you) and are having the following symptoms: 1. idle timeout for FastCGI in http/error.log 2. running ./dispatch.fcgi works fine 3. you get an application error when trying to access the site from a browser 4. production.log doesn't show any trace of the browser request. Make sure that your ENV['GEM_ENV'] variable mentioned here Rails#Installing_Ruby_on_Rails is set in the environment.rb file and not the environments/production.rb file. For some reason this makes a difference.


1.1. Symptom: Browser gives Application Error, but ./dispatch.fcgi works

Details: The log file (/home/u/logs/domain/http/error.log) gives something like:

FastCGI: comm with server "/home/-user-/-websitepath-/public/dispatch.fcgi" aborted: error parsing headers: malformed header 'SET LAYOUT'

Solution: You may have left 1+ 'puts' calls in your code, and they were hit at the point your webapp crashed. Please grep and replace, using Logger if at all. Restart your app and it will be one step closer to working. In the above code, the first puts statement was called during Rails' attempt to process a controller's layout command.

Background: In Dreamhost's setup the Ruby puts actually outputs the text into the HTTP headers section of the returned page, which then cannot be processed correctly. Running ./dispatch.fcgi at the commandline bypasses those (Apache?) checks so it outputs it as you'd expect.


1.2. Symptom: As above, browser gives Application Error, but ./dispatch.fcgi works

Details: The log file (/home/u/logs/domain/http/error.log) gives something like:

[Fri Apr 04 12:40:37 2008] [error] [client 65.116.251.178] FastCGI: comm with (dynamic) server "/home/u/domain/public/dispatch.fcgi" aborted: (first read) idle timeout (60 sec) [Fri Apr 04 12:40:37 2008] [error] [client 65.116.251.178] FastCGI: incomplete headers (0 bytes) received from server "/home/u/domain/public/dispatch.fcgi"

Solution: This can be caused if you changed the group of your dispatch.fcgi file, or possibly the group of other files. Change the group of all your files back to your default group. You can find your default group by typing the command 'groups' at the command line; the first group listed is probably the one you want. Use the command 'chgrp -R yourgroup *' in the top level of your application to change the group for all of your files at once.

Background: The daemon that Dreamhost uses to execute dispatch.fcgi files for the webserver is apparently very picky. It will refuse to run the code if dispatch.fcgi has the wrong group set. When you execute dispatch.fcgi from the command line, no such group check is performed.


2. Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)

This means that your Rails app is trying to connect to MySQL through localhost. Make sure that your database.yml file has the proper MySQL host info and that your Rails app is actually running in the mode you want it to be running in. You can find all of your MySQL info in the Goodies > Manage MySQL section of your panel.

The config/database.yml file should have a line like this in the appropriate environment:

host: mysql.host.for.your.project.com

If you're running a daemon on a private server and you're getting the "Can't connect" error, you will need to change the first line of the file /public/dispatch.rb from: #!/usr/local/bin/ruby to: #!/usr/bin/env ruby


3. My Rails app is unacceptably slow!

Apache uses the RewriteRules in your public/.htaccess file to kickstart your app. By default your .htaccess file will have a line like the following:

RewriteRule ^(.*)$ dispatch.cgi [QSA,L]

This default setting will work, but it's going to be very slow because each time someone makes a request to your Rails app, it'll basically have to load up the whole thing from scratch. A better solution is to use FastCGI instead. To do that, change that line to:

RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]


4. Code changes aren't taking effect

When your app is running with FastCGI you'll need to kill all dispatch.fcgi processes if you make changes to your code (just like every time you would normally restart the web server). To do that, run either of these two commands in the shell:

killall -USR1 dispatch.fcgi
killall -USR1 ruby

(If both these commands return 'no process killed' you may need to do a 'ps -e' to find the exact name of the process/es you need to kill.)

If you're ever reading install instructions or a Ruby tutorial and it tells you to restart the server, just kill the running dispatch.fcgi processes and you'll be good to go.


5. Can't access the Analog stats page

Change the rewrite conditions of your .htaccess file in /public. An example of working rewrite conditions (assumes fast cgi):
 RewriteEngine On
 RewriteCond %{REQUEST_URI} ^/stats/(.*)$ [OR]
 RewriteCond %{REQUEST_URI} ^/failed_auth.html$
 RewriteRule .* - [L]
 RewriteRule ^$ index.html [QSA]
 RewriteRule ^([^.]+)$ $1.html [QSA]
 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]

Alternate Solutions (for Passenger) The alternatives for passenger and stats on our servers are:

Attaching stats to subdomain:

Although Passenger makes it difficult to access stats at http://domain.com/stats, it is possible to work around Passenger by attaching the stats package to a subdomain. To do this:

  1. Create a subdomain such as stats.domain.com in the Dreamhost panel. I recommend that you do not enable stats for this subdomain.
  2. Once stats.domain.com exists in your home directory, create symlinks that point to the Dreamhost-generated statistic files:
cd /home/USERNAME/stats.DOMAIN.COM/
for f in `ls /home/USERNAME/logs/DOMAIN.COM/http/html/`; do ln -s /home/USERNAME/logs/DOMAIN.COM/http/html/$f; done

Note: the enclosing quotes around the ls command are not quotes at all - they are backticks! These are usually found near the Escape key on the upper-left of the keyboard.

This method is not perfect. In particular, it will prompt for a username and password for the many resources it tries to fetch at /stats. Other reports beyond the default (monthly) also do not seem to work. I'm sure these issues can be solved through custom Rewrite rules. In the meantime, this is a good way to get basic statistics.

Using the command line:

Using alternative stats programs:


Other Possible Gotchas

  • Dreamhost servers are CaSE-seNsiTiVe. If you require any files, they have to use the same case the actual file has (e.g., require 'MD5' will not work, but require 'md5' will).
  • Make sure your Dreamhost database schema is synchronized with your local database schema. If you're missing a column, rails will throw an error due to your model complaining.
  • RMagick (and ImageMagick) versions may be out of date. You can install your own versions of RMagick and ImageMagick.
  • Take out any puts statements in your code, use the Rails <%= myvar %> syntax to print to the page instead.

Installing Ruby on Rails

Since DreamHost now has Ruby and Rails installed, the Ruby on Rails Installation instructions have been moved. You shouldn't need to install Ruby or Rails unless you have specific requirements that can't be met with the standard config. Even then, you can contact support and request specific gem packages for specific servers if you need them and they will try to accommodate you.

To see what is available on your server, from the shell, type:

 which ruby
 ruby -v
 gem list | more

You can also see the current version of Ruby on Rails and a list of installed gems at http://rails.dreamhosters.com.


Outdated Rails version Dreamhost keep the last three installed versions available by default. It is highly recommended to freeze the rails version your application is using, so when your version gets outdated the application will not suddenly stop working (It happened to me, and it's not fun). To do that, run the following command on the server. Make sure the current directory is the Rails app directory.

rake rails:freeze:edge TAG=rel_2-1-0

Make sure you input the correct version number. One possible gotcha can be a faulty boot.rb file, make sure it fits the rails version you're using.


Automating deployment with Capistrano (formerly SwitchTower)

Capistrano (formerly SwitchTower) is an additional utility to automate the deployment, migration, upgrades and rollbacks (should anything go horribly wrong!) of your Rails application. Full details for using it along with DreamHost can be found on the Capistrano page, or for the earlier SwitchTower, here: Using SwitchTower with Ruby on Rails and DreamHost.

Deploying to Dreamhost with Capistrano is a blog posting by Parker Morse detailing some of the non-trivial aspects of using Capistrano with your Dreamhost hosting.


#create the_rails_app_production database
#setup db.the-rails-app.com db server
#setup domain the-rails-app.com to look in ./current


#!/bin/bash
cd ~/the_rails_app
mkdir config/deploy
for e in %(staging production testing); touch config/deploy/$e.rb; done;

echo '#!/bin/bash' > script/spin
echo 'touch tmp/restart' >> script/spin && chmod 755 script/spin


# dreamhost only:
# change config/environment.rb to require rails 2.2.2
# change config/database.yaml (production:) and delete the socket ref for mysql
# add symlink to older application.rb controller

svn ci -m 'initial capify'

capify .
cap deploy:setup
cap deploy:check
cap deploy:update #fix all errors until this succeeds
cap deploy:migrate #initial migration of db
#if using 2.2.2 be sure to symlink application_controller.rb to application.rb

cap deploy:start


# if you modify anything, be sure to checkin and deploy:update again

Using Gems Installed in Your Home Directory

You may install gems which are not pre-installed on Dreamhost's servers in your home directory. Assuming you use bash, do the following:

[server]$ mkdir ~/.gems
[server]$ cat >> ~/.bash_profile
export GEM_HOME=$HOME/.gems
export GEM_PATH=$GEM_HOME:/usr/lib/ruby/gems/1.8

Then press CTRL+D to exit cat and return to the command prompt. You may then install gems in the usual way (e.g. gem install gem_name).

The .bash_profile method above resulted in the following error: Could not find RubyGem sources (> 0.0.0) when I tried installing a gem but the instructions on altering your .gemrc file below worked perfectly. HOWEVER, without altering the .bash_profile rake kept complaining about not seeing my locally installed gems even when I passed it GEM_HOME and GEM_PATH manuall. So, you may need both.

  • An alternative to setting the GEM_PATH environment is to create a ~/.gemrc file with the following contents:
gemhome: /home/<YOUR USER NAME HERE>/.gems
gempath:
  - /home/<YOUR USER NAME HERE>/.gems
  - /usr/lib/ruby/gems/1.8

In order to get your rails app to use gems installed in your home directory, you first have to unpack them in RAILS_ROOT/vendor. Therefore, to be able to require them in your code, enter the RAILS_ROOT/vendor directory and do the following:

gem unpack gem_name

where gem_name is the name of the gem you wish to use.

If your gem doesn't add itself to the load path, then it might not load up as expected. You'll have to add it yourself, by putting something like:

config.load_paths += %W( #{RAILS_ROOT}/vendor/gem_name/lib )

in your environment.rb file, obviously replacing gem_name with whatever gem you're loading (remember to include the version number).

As an alternative to using RAILS_ROOT/vendor, you can put the following line at the top of your config/environment.rb file in your rails application(s):

ENV['GEM_PATH'] = File.expand_path('~/.gems') + ':/usr/lib/ruby/gems/1.8'

This allows multiple rails applications to share the same gems directory.

There's also a good How To on this topic here: DreamHost Your Own Packages and Gems

Note: I believe the local installed gems must be unpacked to the app's vendor directory. I spent several days investigating an problem with my app that keeps showing the 500 error page after the dispatch.fcgi silently dies when serving a request. What solved it in the end was removing references to the ~/.gems dir in both dispatch.fcgi and environment.rb plus freezing the rails gems to the vendor dir.

Switching Between Environments

Rails can be used in different enivronments. Each environment will change the behaviour of your application in small ways, and can be used to help you with each stage of the development cycle. The three environments are:

1. Development - (not support on dreamhost) In development, you are generally changing the code rapidly and reloading your application. You don’t need to restart the server or the application to see your changes. Rails error messages will be technically descriptive.

2. Test

3. Production - In production, your code is expected not to change, so models and controllers are cached to run faster. That means no repeated trips to the database to ask about columns and types, resulting in a quicker loading application. Rails error messages are very concise and non technical.

The following instructions are for Dreamhost Rails applications on Apache servers (by default they are), with FastCGI support selected. FastCGI is not supported out of the box so if you suspect your application to not be using FastCGI then see the Configuring Your Application section above.

Open the file environment.rb in the config directory and replace the line:

RAILS_ENV = ENV['RAILS_ENV'] || 'production'

with

RAILS_ENV = 'development'

You can replace development with production in the line above to switch back to that environment again.

Housekeeping

No one really enjoys housework, but with Rails there are sometimes a few chores that need to be done.

Clearing Out Sessions

If you use sessions in your application, you have to periodically purge old session files from your tmp directory. If you do not purge the old files eventually your application may become unstable. The following can be run as a server cron job:
[server]$ find /tmp/ -name 'sess_*' -ctime +1 -exec rm -fv \{\} \;
This will delete ruby session files from the tmp directory. The -f flag to rm is a bit dangerous, but asking questions for every file you are not allowed to delete will take forever. The v switch will print out every file that gets deleted.
You can restrict the find command to your own files with the -user flag, and keep find from trying to go where it's not allowed to go with -maxdepth:
[server]$ find /tmp/ -maxdepth 1 -name 'sess_*' -user 'yourusername' -ctime +1 -exec rm -f \{\} \;
'yourusername' is the username that your rails app runs as, specified in the "Web Hosting" options of the Dreamhost Panel.

Using Basic Authorization

If you need to use basic authorization (for example, to support authenticated RSS feeds), you can do so in a hackish way with this rule in mod_rewrite:

 RewriteRule ^(.*)$ dispatch.fcgi [E=X_http_AUTHORIZATION:%{http:AUTHORIZATION},QSA,L]

The controller to do is described on the Rails Wiki, except that you need the final elsif here:

 def get_auth_data 
   user, pass = "", "" 
   # extract authorisation credentials 
   if request.env.has_key? 'X-HTTP_AUTHORIZATION' 
     # try to get it where mod_rewrite might have put it 
     authdata = request.env['X-HTTP_AUTHORIZATION'].to_s.split 
   elsif request.env.has_key? 'Authorization' 
     # for Apace/mod_fastcgi with -pass-header Authorization 
     authdata = request.env['Authorization'].to_s.split 
   elsif request.env.has_key? 'HTTP_AUTHORIZATION' 
     # this is the regular location 
     authdata = request.env['HTTP_AUTHORIZATION'].to_s.split  
   elsif request.env.has_key? 'REDIRECT_REDIRECT_X_http_AUTHORIZATION'
     # special Dreamhost header
     authdata = @request.env['REDIRECT_REDIRECT_X_http_AUTHORIZATION'].to_s.split
   end 

This solution was created by psross.

I streamlined this solution to be more general to most all Apache setups, I can't imagine how this could be a security risk but YMMV:

 def get_auth_data 
   user, pass, authdata = nil, nil, nil
   request.env.each do |key,value|
     if /HTTP_AUTHORIZATION$/.match(key)
       authdata = value.to_s.split
     end
   end
   # at the moment we only support basic authentication 
   if authdata and authdata[0] == 'Basic' 
     user, pass = Base64.decode64(authdata[1]).split(':')[0..1] 
   end 
   return [user, pass] 
 end

-- BrianPalmer.

Nock's Method for Rails 2.0.2

This worked for me on 4/24/08, but I make no promises

  • cd ~/
  • rails your_app_name -d mysql
  • copy app/, database.yml, routes.rb, db/
  • change public/.htaccess from .cgi to .fcgi (line 32)
  • put your app into production mode (uncomment line 5 in environment.rb)
  • run rake db:migrate RAILS_ENV=production
  • chmod -R 755 ~/your_app_name/app
  • rm your_app_name/public/index.html
  • ln -s ~/your_app_name/public ~/yourdomain.com/link_to_my_app
  • killall -USR1 dispatch.fcgi
  • killall -USR1 ruby

Rails 2.3.5 - Rack 1.0 already activated (fix)

When using Rails 2.3.5 you will get a problem from Passenger saying Rack 1.0.1 cannot be loaded because Rack 1.0 is already activated.

One way to solve this is by freezing Rails and unpack the Rack gem into vendor/gems/rack-1.0.1

Once Rails and Rack are in the vendor/rails and vendor/gems/rack-1.0.1 you must modify action_controller in file: vendor/rails/actionpack/lib/action_controller.rb

In line numbers 34 and 35 must be commented out and add the following to load rack from vendor/gems

 load "#{RAILS_ROOT}/vendor/gems/rack-1.0.1/lib/rack.rb"

The end result should look something like this:

 #gem 'rack', '~> 1.0.1'
 #require 'rack'
 load "#{RAILS_ROOT}/vendor/gems/rack-1.0.1/lib/rack.rb"

The real problem is that Passenger already loads Rack 1.0 and I believe Passenger must load 1.0.1 in order for this hack to go away.

External Links