Skip to content
FrameworkRuby

Rails environment variables vs credentials.yml.enc

Rails keeps secrets in encrypted credentials opened by a master key, and lets ENV override some of them. When to use which, and how to run Rails with Crunu.

Ruby on RailsAt a glance

Env files, highest priority first
No .env files: values come from the process environment or the platform.
Public prefix
None

No names are copied into browser code by a prefix.

Values applied
At runtime

Values are read from the environment when the app runs.

Built-in secret store
Encrypted credentials: config/credentials.yml.enc opened with a master key

guides.rubyonrails.org/security.html

Crunu command
crunu run --env rails-app/development -- bin/rails serverSee the systemd integration

Common pitfalls

  1. ENV SECRET_KEY_BASE takes precedence over the value in credentials, so a forgotten variable silently replaces the key.
  2. Changing secret_key_base expires every active session and can break encrypted cookies and other data derived from it.
  3. Precompiling assets needs a secret_key_base. Use SECRET_KEY_BASE_DUMMY=1 in the build instead of the real key.
  4. Whoever ever held the master key can decrypt every version of credentials.yml.enc in Git history, not only the current one.
  5. Rails 8.1 has no built-in .env loader. ENV holds only what the process was started with.
On this page

Rails reads environment variables through Ruby's ENV, which holds whatever the process was started with. Rails 8.1 doesn't load a .env file on its own. For secrets, Rails has its own store: encrypted credentials in config/credentials.yml.enc, or config/credentials/production.yml.enc per environment, opened with a master key from config/master.key or the RAILS_MASTER_KEY variable. A few settings check ENV first and credentials second, most importantly secret_key_base. All of it is read when the app boots, so new values need a restart. This page follows the Rails security guide and the Rails 8.1 API, checked October 30, 2026, with Rails 8.1.4 current.

Two sources, and which one wins

ValueLooked up inNotes
secret_key_base1. ENV["SECRET_KEY_BASE"] 2. credentialsin development and test, generated into tmp/local_secret.txt if missing
credentials key1. ENV["RAILS_MASTER_KEY"] 2. key fileconfig/credentials/ENV.key, else config/master.key
credentials file1. config/credentials/ENV.yml.enc 2. config/credentials.yml.encENV here means the Rails environment name
your own settingswherever your code reads themENV.fetch or Rails.application.credentials

There is no file chain like .env.local over .env. If you want one, a gem has to provide it. Everything else is a decision your code makes, one setting at a time.

How credentials work

bin/rails credentials:edit decrypts the credentials file into your editor and encrypts it again when you close it. It creates the file and a master key if neither exists. Add --environment production to edit the production file, which gets its own key. The encrypted file is meant to be committed: the guide says it can be stored in version control as long as the master key is kept safe, and it says in so many words not to commit the master key.

Code reads values through Rails.application.credentials, for example Rails.application.credentials.stripe_secret_key, or a nested path like credentials.aws.access_key_id. The bang form, stripe_secret_key!, raises KeyError when the value is blank, which is the safer choice for anything required.

By default the credentials file contains secret_key_base, the input for the key generator behind every message verifier and encryptor in the app, including signed and encrypted cookies. Generate a new value with bin/rails secret.

What changing secret_key_base breaks

The guide spells it out: changing secret_key_base expires all active sessions and requires every user to log in again, and encrypted or signed cookies and Active Storage files can be affected too. Two consequences follow:

  • An environment variable can change it by accident. ENV["SECRET_KEY_BASE"] beats the credentials value. A variable left over in a server's environment, a container platform's settings or a CI job quietly replaces the key in credentials, and the symptom is that everyone is logged out after a deploy.
  • Builds don't need the real key. rails assets:precompile boots the app, so it needs some secret_key_base. Rails documents SECRET_KEY_BASE_DUMMY=1 for exactly this case, for example in a Dockerfile, which generates a random throwaway key for the build. The real key then only exists where the app runs.

Credentials or environment variables?

Both are reasonable, and the trade-offs are about people more than about Ruby:

Encrypted credentialsEnvironment variables
Where values livein the repository, encryptedoutside the repository
What a server needsone key per Rails environmentevery value
Changing one valueedit, commit, deploychange the variable, restart
Removing a person's accessnew master key, re-encrypt, reissue valuesdepends on the store you use
Old versionsstay in Git historydepend on the store

The fourth and fifth rows are the ones teams underestimate. A master key opens every version of the file that was ever committed with it. When someone who had the production key leaves, generating a new key protects only future versions. Every value they could read is still readable from history and has to be reissued at its provider.

Before you change anything, take stock

A Rails app that has been around for a while usually reads secrets from three places at once: credentials, ENV lookups added later, and gem defaults that read their own variables. Moving any of it is easier after a short inventory:

  • List every key in each credentials file, by name only. Note which ones code still reads. Unused keys are values you'd otherwise keep reissuing for nothing.
  • Search the code for ENV[ and ENV.fetch. Each hit is a variable some server must provide. Compare the list with what your hosts actually set.
  • Check every place that can set SECRET_KEY_BASE. Server environments, container settings and CI jobs. If one sets it, that value wins over credentials, and you want to know before a deploy changes it.
  • Find out who holds each master key. Laptops, CI variables, password managers, the old deploy server. That list is your offboarding scope for credentials.

The result tells you which values belong in credentials, which in the environment, and which can simply be deleted.

Run Rails with values from Crunu

crunu run starts your command with the variables of a Crunu environment in its process environment, so Rails sees them in ENV. There are two ways to combine that with credentials.

Keep credentials, move only the key. Store RAILS_MASTER_KEY for each Rails environment in the matching Crunu environment. The server never has a key file on disk, and Crunu controls who can read the key. Everything about credentials stays the same, including the offboarding caveat above.

Move the values themselves. Read secrets with ENV.fetch("STRIPE_SECRET_KEY") in initializers or in config/environments/production.rb, and keep them in Crunu. Each environment keeps its own version history, access is granted per environment, and when someone leaves, Crunu's offboarding report shows which versions of which environments they received and lists the values to reissue.

  1. Put RAILS_ENV and either RAILS_MASTER_KEY or the individual secrets into Crunu environments such as rails-app/development and rails-app/production.
  2. Develop with crunu run --env rails-app/development -- bin/rails server after a one-time crunu login.
  3. Build images with SECRET_KEY_BASE_DUMMY=1 for asset precompilation, so no real secret is needed during the build.
  4. On a Linux server with the agent, start Puma with crunu run --env rails-app/production --restart on-change -- bundle exec puma -C config/puma.rb.

RAILS_ENV belongs in the environment because crunu run passes only a short allowlist from your shell, such as PATH and HOME. PATH keeps rbenv or asdf shims working. Variables like BUNDLE_GEMFILE or SSH_AUTH_SOCK, which Bundler may need for private Git gems, are not passed, so add them or use --inherit-env for that command.

With --restart on-change, crunu restarts Puma after a value changes, which is how a new database password or API key reaches the app. Don't let it rotate SECRET_KEY_BASE casually: the restart applies the new key, and every user is signed out.

Frequently asked questions

Something else? Write to support@crunu.com

Should I use Rails credentials or environment variables?

Credentials keep encrypted values in the repository and need only one key per environment on the server. Environment variables keep values out of the repository entirely and let each value change on its own. Many apps combine them: credentials for most values, ENV for what differs per deployment.

Where should RAILS_MASTER_KEY live?

Anywhere except the repository. The Rails guide says not to commit the master key. On servers, pass it as the RAILS_MASTER_KEY environment variable, for example from Crunu, or as the key file with restricted permissions.

Do per-environment credentials need their own key?

Yes. config/credentials/production.yml.enc is opened with config/credentials/production.key or RAILS_MASTER_KEY. Rails falls back to credentials.yml.enc and master.key only when the environment file doesn't exist.

How do I generate a new secret_key_base?

Run bin/rails secret. Changing the key expires sessions, so plan the switch.

Sources

Every source this page relies on, with the date we last checked it.

  1. Rails Guides: Securing Rails applications · checked October 30, 2026
  2. Rails 8.1 API: Rails::Application · checked October 30, 2026

Keep your .env values in one place

Free during early access. Values are encrypted on your devices, each environment has its own key, and crunu run hands them to your app without writing a file.