Should I use Rails credentials or environment variables for secrets?
Either. The app reads ENV first and falls back to Rails.application.credentials, so a value set in the environment wins and a value in the encrypted file is the default. Use credentials when you want secrets versioned with the code, and environment variables when a platform or a team already manages them elsewhere.
The resolution order
ENV, then credentials. That order is what makes both work at once: commit a sensible default in the encrypted file, and override it per environment without editing anything.
bin/rails credentials:edit
bin/rails runner 'puts Rails.application.credentials.dig(:stripe, :secret_key).present?'
When credentials are the better fit
One repository, one team, and a deploy that is git push plus kamal deploy. Secrets travel with
the code, a new machine needs exactly one value (RAILS_MASTER_KEY) to get all of them, and the
history of what changed is in git.
The tradeoff is that everyone who can read config/master.key can read every secret, and rotating a
single value means an edit and a deploy.
When environment variables are the better fit
A platform that already has a secret store, a team where not everyone should see production credentials, or an app that runs in more than one environment with different values. Rotating one value is a change in one place with no deploy.
The tradeoff is that nothing is versioned, and a missing variable is discovered at runtime rather than at review.
Do not split the difference by accident
The failure mode is having both set, with different values, and not knowing which one is live. ENV wins, silently. If you use environment variables, keep the credentials file empty of those keys rather than stale.
The one that is not optional
RAILS_MASTER_KEY or SECRET_KEY_BASE. Production will not boot without one of them, because Rails
cannot decrypt credentials or sign cookies. Everything else in
docs/BRING_YOUR_OWN_KEYS.md has a fake behind it and is safe to leave unset.
Never commit the key
config/master.key is gitignored and should stay that way. bin/check runs a secret scan over the
tree, so a key pasted into a file fails the build rather than reaching the repository. That check is
worth keeping in CI for exactly this reason.