Stories From The Field / Tech

Deploying a Rails 3.1 application to production

Posted on May 26 2011 by Richard Taylor (@moomerman)

There is a big change to the handling of assets in Rails 3.1 compared to previous versions called the asset pipeline and this can have an effect on your production deployments, more so than any previous Rails update I can remember. Here is a list of the gotchas I came across and how to fix them.

My deployment tools consist of capistrano, nginx and unicorn so the solutions listed here may be specific to those but you'll get the general idea if you are using something else. Information is correct as of the Rails 3.1.0.rc1 release, I'll update it if further releases change any of this (updates are now inline).

Javscript Runtime

The thing that has surprised me most is the requirement to have a javascript runtime present on the production server. To me this isn't a small thing, it's a rather large new dependency that I have to manage.

Update: Further release candidates have made the javascript runtime optional in production if you precompile assets before you deploy. If you want to precompile as part of your deployment process (like I do) then you will still need to follow these instructions.

If you deploy your application without a suitable runtime you are likely to see the following error message:

Could not find a JavaScript runtime. 
See https://github.com/sstephenson/execjs for a list of available 
runtimes.

To fix this, I chose one of the available ruby-wrapped runtimes, therubyracer, and configured it in my Gemfile:

group :production do
  ...
  gem 'therubyracer'
end

Be prepared to sit back and wait while it compiles and installs when you do your first deployment.

Update: If you're deploying to Heroku please read this article.

Asset Compilation

In production you generally won't want to use the asset pipeline directly, instead you will run the assets:precompile rake task so your webserver can serve the generated assets. My capistrano configuration in /config/deploy.rb is:

after 'deploy:update_code' do
  ...
  run "cd #{release_path}; RAILS_ENV=production rake assets:precompile"
end

This will copy all the assets to /public/assets/ and encode the MD5 hash of the contents into the path name. This is a great improvement as far as caching is concerned but has some side-effects (see below).

One interesting thing I haven't discovered yet is what happens if you have two assets with the same name in different folders? I guess one of them will win and the other will be ignored, or it will throw an error.

CSS Assets

A frustration I had with the new asset pipeline code and the production environment is inconsistent behaviour. One thing I liked about the asset pipeline in development is that it will 'find' the assets for you. In your CSS you can specify something like:

body { 
  background: #00ff00 url('rails.png') no-repeat fixed center;
}

As long as your rails.png asset exists somewhere in the /app/assets/ folder it will be served.

However, when you run the assets:precompile rake task as described above the asset you were referencing now becomes:

/public/assets/rails-9c0a079bdd7701d7e729bd956823d153.png

but the compiled CSS isn't changed accordingly to point to this generated asset. I thought this was a bug but apparently it is expected behaviour. You can see the comments on this rails ticket.

In short, you should either not use the asset pipeline (ie. reference your images directly using /images/rails.png as before) or use erb to generate your asset paths by appending .erb to the CSS filename and using the asset_path helper as follows:

body { 
  background: #00ff00 url(<%= asset_path 'rails.png' %>) no-repeat fixed center;
}

Asset Caching

If you've got this far and all your assets are compiled and are being referenced correctly you'll want to update your web server configuration to take advantage of this new cachability. My nginx configuration was updated with:


location ~* ^/assets {
  expires max;
  add_header Cache-Control public;
  break;
}

Other Bits & Pieces

At the time of writing I wasn't able to deploy with the latest version of rake. I was getting undefined method `task' errors in production so ended up hard coding gem 'rake', '0.8.7' in my Gemfile. I expect this will get fixed before the final release.

Update: The rake issue above is now fixed in the latest versions of Rake and Rails.

I'm also having some trouble with the include declaration of javascript assets. I ended up just including all of them and I believe some of them may be getting included more than once, but I'm assuming it is my fault.

That's all for now, I hope you find this information useful when deploying your Rails 3.1 applications. Please feel free to leave your war stories in the comments below.

Hacker News Icon Comments on Hacker News
Reddit Icon Comments on Reddit

Comments