Rails 4+ Asset Pipeline on Heroku

The asset pipeline was introduced into Rails in version 3.1. This article contains information needed to run the asset pipeline in Rails version 4 and above on Heroku. This guide is not comprehensive, please see the Rails Asset Pipeline on Heroku Cedar for help with debugging and troubleshooting. This article adds additional information that is specific to the behavior in Rails 4.

Serve assets

By default Rails 4 will not serve your assets. To enable this functionality you need to go into config/application.rb and add this line:

config.serve_static_assets = true

Alternatively you can achieve the same result by including the rails_12factor gem in your Gemfile:

gem 'rails_12factor', group: :production

This gem will configure your application to serve static assets so that you do not need to do this manually in a config file.

Only generate digest assets

In Rails 3, the version of sprockets used in the asset pipeline would produce a “digest” or a “fingerprint” for an asset and then attach it to it’s file name. For example this file:

app/assets/stylesheets/application.css.erb

When precompiled would become:

public/assets/application-c655834251f73d7714351aa287be8556.css

Where “c655834251f73d7714351aa287be8556” is the “digest” or “fingerprint” generated by MD5. In addition to “digest” file names Rails 3 asset pipeline would produce a non-digest version public/assets/application.css . This was useful for things like sending out emails where the recipient may or may not download the asset in a timely manner.

In Rails 4 sprockets will only produce digest filenames. This means that you must use an ERB helper such as this to reference your assets:

<%= asset_path('logo.png') %>

Make sure to add a .erb extension to any files in app/assets that use an ERB helper. So application.css would need to be application.css.erb .

To get around the “email problem”, sprockets will now keep up to 3 copies of the same modified asset at a time. This also helps with rolling deploys so requests for older assets are still available. Even with this behavior it is recommend you use a CDN to serve your assets.

Caching

Heroku now caches 50mb worth of tmp/cache/assets which is a cache directory for the asset pipeline to store intermediate files. This means that future asset compilations will be faster due to not having to recalculate these files.

This is a difference in Heroku and not in the Rails framework. Rails 3 assets are generated on every deploy as there are several bugs that prevent caching from being used effectively.

The directory tmp/cache/assets/sprockets/ will be removed from runtime to reduce your slug size (which gives you a faster boot times). This directory will only be removed if your application does not need these files i.e. dynamic asset compilation is disabled:

config.assets.compile = false

If you see a large directory size that appears to exceed 50 megabytes while inspecting your dyno in a heroku run bash session, it may be due to sparse files. This is because a file that is smaller than the operating system’s block size will have a larger apparent size on disk. You can get the correct size that is stored in the cache between deploys by running the du command with the --apparent-size flag. For example:

$ heroku run bash ~ $ du -hd 1 tmp/cache/assets/ 386M tmp/cache/assets/sprockets ~ $ du -hd 1 tmp/cache/assets/ --apparent-size 27M tmp/cache/assets/sprockets

In this case the first call to du showed 386mb worth of assets, however the correct size for the files that would be zipped and stored in the cache is only 27mb.

Multiple versions

By default the Rails asset pipeline keeps the last 3 generated versions of an asset. This is to support assets being used in locations such as emails that might not be rendered until days or weeks later.

If you delete an asset, the last 3 versions of that asset will remain available. Sprockets does not track deleted assets and so it cannot know when an asset has been deleted. If you wish to remove an asset you have deleted, you can clear the cache which is how we persist the generated assets in public/assets between builds. Note that clearing the cache will remove all older versions of assets and not just the ones that have been deleted.

Debug output

When rake assets:precompile is run in production Rails 4 will now shows debug output while generating tmp files, this does not mean your files end up in /tmp . The files will be copied to their correct locations after they are generated.

Known issues

There are two known issues with Rails 4 (sprockets) that can be difficult to detect or debug. To help with this we recommend using the sprockets better errors gem gem in production. These checks have been merged into Rails 4.1+ but the gem is still needed for 4.0.x versions.

You may get errors if you use sprockets_better_errors with Rails 4.1+.

Dependencies improperly used

You can see a failure in sprockets When you change an asset file that is referenced in another file and the reference does not change. This is due to missing sprockets dependency declarations.

For example you can have a CSS file app/assets/stylesheets/application.css.erb that references another file:

.logo { background-image:url('<%= asset_path("logo.png") %> '); }

Here application.css.erb relies on app/assets/images/logo.png . When precompile is run both assets will be generated. What happens if the logo.png is resized to be larger per a client request? The next time precompile runs, sprockets will see that logo.png has changed since it’s MD5 fingerprint is now different so it will be compiled. Our application.css.erb however has not changed, so it will not be compiled again. This means our stylesheet now points to the wrong copy of our image. To fix this add an asset declaration to the top of your application.css.erb file:

//= depend_on_asset "logo.png" .logo { background-image:url('<%= asset_path("logo.png") %> '); }

Now when your logo.png changes, your application.css.erb will be required to compile again.

Missing Asset in precompile list

Another common production failure in sprockets is caused by assets not being declared as needing to be precompiled. By default all “application” assets and images are precompiled. This includes application.css and application.js . It is assumed that any CSS or JS in your project will be rolled into these files and so any extra JS or CSS in your project will not be precompiled. The gem sprockets_better_errors will check for missing assets in your precompile list in development.

Debugging

Before you can compile your assets on Heroku, you’ll need to be able to compile them locally, run this command to debug your assets:

$ RAILS_ENV=production bundle exec rake assets:precompile

This should complete with no errors. Do NOT check in the assets into git after running this command.

If you are getting an error at page load, but not while building assets you can use heroku run bash to debug it

$ heroku run bash $ ls public/assets

You should see a manifest- <digest>.json or .sprockets-manifest-<digest> where <digest> is a long alphanumeric string as well as all of your asset files.

You can also see the exact file name that Rails believes it should serve by running $ heroku run rails console . In the console you can use the helper object and asset_path to determine the full path.

$ heroku run rails console > puts helper.asset_path("application.js") /assets/application-6aae32862efc758cf08c7b7fc0e85e15.js

Hanging asset builds

If your asset compilation appears to hang, try removing the assets from your vendor/assets directory and putting them in your app/assets directory.

Additional troubleshooting

Please see a comprehensive list in the Asset Pipeline troubleshooting section.