If you’ve ever had to deploy a large Rails site using Capistrano, then you’re probably aware of how time consuming it is to precompile the assets pipeline on the server-side.

This is not really an issue for a small sites with a few images and JavaScripts, but when it starts taking upwards of half an hour to roll out a small or time critical patch you know somethings gotta give!

Some people store compiled assets using git in either the master or a separate repository, but that’s kind of overkill and it also introduces an extra step. The most efficient way is just to use Capistrano’s run_locally command to compile assets on the local machine and then rsync them to the remote server.

The following Capistrano script is what we currently use on a Rails 3.2 site, but it should work with other Rails versions too. Stick the following task somewhere in your deploy.rb:

namespace :deploy do
  namespace :assets do
   desc "Precompile assets locally and then rsync to deploy server"
    task :precompile, :only => { :primary => true } do
      run_locally "bundle exec rake assets:precompile"
      servers = find_servers :roles => [:app], :except => { :no_release => true }
      servers.each do |server|
        run_locally "rsync -av ./public/#{assets_prefix}/ #{user}@#{server}:#{current_path}/public/#{assets_prefix}/"
      run_locally "rm -rf public/#{assets_prefix}"

If you’re a Windows user you can obtain rsync binaries here. Just add the cwRsync/bin folder to your system path and everything will be peaches.

To make sure assets are compiles we need to call deploy:assets:precompile after each deployment. The order in which the task is called is no so critical here since Rails will be compiling assets from the local repository, but just in case anything else fails it would be best to call it after other tasks so we won’t have run a costly task for nothing.

A good time to run the task is after deploy:finalize_update like so:

after "deploy:finalize_update", "deploy:assets:precompile"