Using CloudFront With Heroku Pipeline Review Apps and Rails

heroku, rails

Most of the applications I deploy follow a common pattern. Rails for the framework, deployed to Heroku, with a standard set of dependencies and integrations. One of those is Amazon CloudFront’s content distribution network for hosting static assets.

Heroku, CloudFront, and Rails usually work beautifully together in production to make applications load lightning quick.

We recently started using Heroku Pipelines to manage deployment and iterating faster. The killer feature for us is automatically-generated review applications. Every time someone opens a Pull Request on GitHub, that version of the code is automatically turned into a brand new, live application on its own subdomain. These domains are usually of the from “app-name-pr-n”, where n is the number of the pull request.

Then we can send product managers, investors, beta users, or whoever needs to see a new feature to the pull request application to get their feedback. It’s as easy as sending them a link.

One downside: this setup breaks our CloudFront configuration. When the application is compiled on the review app, the asset links point to CloudFront… which then goes to our master server, not the review app. Since the origin domain is set on a per-distribution basis, to make CloudFront work with each of our review apps would require creating a new distribution every time we create a pull request.

We probably could do that using the Amazon AWS API, but CloudFront really isn’t designed to be used that way, and creating a distribution can easily take five to ten minutes. Instead, we decided to configure our application so as to skip using the CDN entirely if it is on a review app deployment.

How to skip CloudFront on review apps

This isn’t as easy as you might think. Heroku dynos, by design, don’t know which domain they’re hooked up to - they just serve an application. The only way to really configure them on a per-application basis is using ENV vars.

We created a new ENV var called “PR_ENVIRONMENT” (though “REVIEW_ENVIRONMENT” would probably have been a better name). Then, we added “PR_ENVIRONMENT”: “true” to the “env” section of our application’s app.json, which is used to initialize the new review applications.

Then, we added a tiny bit of logic to our config/environments/production.rb to skip CloudFront if “PR_ENVIRONMENT” is set to true:

1
2
3
unless ENV["PR_ENVIRONMENT"] == "true"
  config.action_controller.asset_host = "<url>"
end

Finally, we need to remove the PR_ENVIRONMENT variable from the environments where we want CloudFront to be enabled. Just run heroku config:set PR_ENVIRONMENT="" --app <app_name> for the appropriate app names.

This way, your review apps will serve assets directly from the application, while your production apps keep the whiz-bang speed of CloudFront.