Hashrocket.com / blog

S3 Asset Hosting Walkthrough

posted on and written by Paul Elliott in

Image 100x100 paul elliott

Are your servers hosting your static assets as well as your Rails app? That's a lot of unnecessary load if they are. The good news is that setting up your app to serve them from S3 is really easy with the asset pipeline. Follow these steps and you'll take a significant load off your server.

Rails Configuration

The first change is to enable the asset pipeline in your config/application.rb if you haven't already.

config.assets.enabled = true
config.assets.digest = true

The next step is not necessary but will speed up the rake task tremendously. It will prevent the Rails app from initializing when you run the precompile task. This will be fine unless you are accessing Rails resources in your asset files. You'll find out real fast if you need to leave this on by running rake assets:precompile locally.

config.assets.initialize_on_precompile = false

Now you need to tell the pipeline what to include in the precompile. By default it will include the application.js and application.css manifest files and anything they reference. Typically these will include all your SASS and CoffeeScript files so you won't need to do anything else. If you have other assets to include, just add them to this array in your configuration.

config.assets.precompile += %w(otherfile.js otherfile.css)

Asset Sync Config

Setting up the asset_sync gem is very easy. Of course we start with the Gemfile. Put the asset_sync gem in your :assets group along with the other asset related gems.

group :assets do
  gem 'asset_sync'
  gem 'coffee-rails'
  gem 'sass-rails'
  gem 'uglifier'
end

Next you need to set up your production.rb to tell the asset pipeline where the assets will be located.

config.action_controller.asset_host =  "//#{ENV['FOG_DIRECTORY']}.s3.amazonaws.com"
config.action_mailer.asset_host = "http://#{ENV['FOG_DIRECTORY']}.s3.amazonaws.com"

If you have a staging.rb or any other server environment, be sure to add it there as well.

Now we need to set up the environment variables. Asset sync looks for certain ones by default, so if you name them this way then it will just work. You can use these to configure your file upload gem as well.

AWS_ACCESS_KEY_ID=xxxx
AWS_SECRET_ACCESS_KEY=xxxx
FOG_DIRECTORY=<your s3 folder name>
FOG_PROVIDER=AWS
ASSET_SYNC_GZIP_COMPRESSION=true
ASSET_SYNC_MANIFEST=true
ASSET_SYNC_EXISTING_REMOTE_FILES=keep

If you want to configure it in a different way or see the other options, check out the readme for the asset_sync gem.

Using Assets Properly

Setting everything up properly means keeping all your assets under app/assets. Make sure you don't have assets under public because they won't work once you switch over. You also need to use the tag helpers whenever you reference any assets.

One cool thing is that setting asset host up for action mailer makes all the tag helpers work again. Without it they are all relative paths and broken for your users.

Be sure to check out the complete docs on proper asset usage. Once you set up your staging environment, browse around and make sure you aren't getting any 404's where images should be.

http://guides.rubyonrails.org/asset_pipeline.html#how-to-use-the-asset-pipeline

Deployment

Make a commit and deploy your app. If you are using Heroku the precompilation will happen automatically. If you have your own deployment script like capistrano then you will need to add the precompile step after the code deployment. Once it is set up then you should see a lot of activity in the log when the asset precompile step runs.

DON'T USE CLOUDFRONT

I know you'll be tempted to. I mean, it's a CDN, right? That's faster! Indeed it is, but it also caches resources for 24 hours, meaning that your app will be broken for a while after you deploy while you wait for the cache to refresh.

Congratulations!

That should be it. Make sure you read up on using assets correctly in your app, as it is very important to get the most out of the pipeline. Congrats on your performance boost!

Posted in Development and tagged with Ruby