We've moved discussions to Discord

Deploying To Heroku With Multiple Environments

Gordon Evans
We’re using Heroku to deploy our application to both a staging and production environment and we’re using the secure credentials for our stripe and sendgrid API keys. However, Heroku requires deployments to use RAILS_ENV=production, which in turn will cause our application to always read from the production encrypted credentials file. We’ve also found that we cannot use the `<%= ENV["MY_KEY"] %>` pattern to access environment variables, as this yml file is read literally and not interpreted (i.e. `Rails.application.credentials.sendgrid.password` will literally print `<%= ENV["SENDGRID_API_KEY"] %>`.

Since our staging and production environments require different keys, does anyone have a recommendation about how we might go about having different credentials for our different Heroku environments?
Gordon Evans
For those seeking a solution, here's what we came up with...

1. Generate a staging credentials file & master key.
2. In config/environments/production.rb set the value for config.credentials.content_path using an environment variable that you can then configure in the Heroku config vars as follows.
config.credentials.content_path = "config/credentials/#{ENV.fetch('RAILS_CREDENTIALS_ENV', "production")}.yml.enc"

Chris Oliver
Hey Gordon,

For staging, you simply:

1. Generate your staging credentials and master key
2. Set RAILS_ENV=staging on Heroku's env vars
3. Deploy

The RAILS_ENV will make sure it loads the staging environment configs, staging credentials, etc.
Ariel Fogel
Hey Chris Oliver
I also unfortunately ran into a similar issue, even after doing what you suggested. I'm going to try Gordon's suggestion. Thanks!

My issue, specifically, is that assets didn't finish precompiling when I set RAILS_ENV=staging and using my staging RAILS_MASTER_KEY.
Full stack trace:

remote:        rake aborted!
remote:        LoadError: cannot load such file -- uglifier
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/bootsnap-1.4.6/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:34:in `require'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/zeitwerk-2.3.0/lib/zeitwerk/kernel.rb:23:in `require'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/autoload/uglifier.rb:2:in `<main>'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/bootsnap-1.4.6/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/bootsnap-1.4.6/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/bootsnap-1.4.6/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/bootsnap-1.4.6/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/bootsnap-1.4.6/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/zeitwerk-2.3.0/lib/zeitwerk/kernel.rb:23:in `require'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/uglifier_compressor.rb:43:in `initialize'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/uglifier_compressor.rb:26:in `new'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/uglifier_compressor.rb:26:in `instance'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/uglifier_compressor.rb:34:in `cache_key'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/processor_utils.rb:102:in `processor_cache_key'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/cached_environment.rb:49:in `processor_cache_key'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/processor_utils.rb:111:in `block in processors_cache_keys'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/processor_utils.rb:111:in `map'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/processor_utils.rb:111:in `processors_cache_keys'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/processing.rb:165:in `resolve_processors_cache_key_uri'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets.rb:213:in `block in <module:Sprockets>'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/dependencies.rb:68:in `resolve_dependency'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/cached_environment.rb:54:in `resolve_dependency'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/loader.rb:286:in `block in resolve_dependencies'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/loader.rb:286:in `map'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/loader.rb:286:in `resolve_dependencies'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/loader.rb:54:in `block in load'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/loader.rb:329:in `block in fetch_asset_from_dependency_cache'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/loader.rb:325:in `each'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/loader.rb:325:in `each_with_index'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/loader.rb:325:in `fetch_asset_from_dependency_cache'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/loader.rb:43:in `load'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/cached_environment.rb:44:in `load'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/base.rb:81:in `find_asset'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/base.rb:88:in `find_all_linked_assets'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/sprockets-4.0.0/lib/sprockets/manifest.rb:125:in `block (2 levels) in find'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.6/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb:24:in `block in execute'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.6/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb:41:in `block in synchronize'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.6/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb:41:in `synchronize'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.6/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb:41:in `synchronize'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.6/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb:19:in `execute'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.6/lib/concurrent-ruby/concurrent/promise.rb:563:in `block in realize'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.6/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:353:in `run_task'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.6/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:342:in `block (3 levels) in create_worker'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.6/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:325:in `loop'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.6/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:325:in `block (2 levels) in create_worker'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.6/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:324:in `catch'
remote:        /tmp/build_d8c3416ca5a47cf7442b834104a1b573/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.6/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:324:in `block in create_worker'
remote:        Tasks: TOP => assets:precompile
remote:        (See full trace by running task with --trace)
remote:
remote:  !
remote:  !     Precompiling assets failed.
remote:  !
remote:  !     Push rejected, failed to compile Ruby app.
Charles Forcey
Appreciating the discussion above, if a year later!  Heroku explains its 12-factor caution against custom environments for staging here: https://devcenter.heroku.com/articles/deploying-to-a-…

Gordon's solution works great and helps limit the concept of staging to the narrow use case of naming a RAILS_CREDENTIALS_ENV.  This way we keep the 12 Factor ideal of ENV variables as the main difference between staging and production from the Rails code point of view.  

Wonder if the heroku docs should link to the above guide and offer Gordon's snippet as a way to conform to that recommendation when deploying a jumpstart app.

Thanks! C
Chris Oliver
Rails automatically uses environment credentials based upon the RAILS_ENV if the config/credentials/{environment}.yml.enc exists, so no need to add the config lines to change the content path. That's built-in to Rails 6.0 now.

Just make sure you run "rails credentials:edit --environment=staging" to generate the right files.
Notifications
You’re not receiving notifications from this thread.