Credentials
Deploying secret API keys and other secret configuration to a production environment used to be a hassle. You don’t want to commit secrets unencrypted to your repository, and you also want to share them with other developers. Rails 5.2 introduced the concept of encrypted credentials to solve exactly this problem; Rails 6 added per-environment credentials on top.
You still have to store one central encryption key on your server and on all development systems, but that’s it. All other secrets/credentials are encrypted with that key and can be stored safely in your code repository.
We start with a new Rails application:
$ rails new shop
$ cd shop
Setup
In a fresh Rails 8 app you’ll find the master key that encrypts
your credentials in config/master.key. That file is added to
.gitignore automatically. Save this key in your team password
manager (1Password, Bitwarden, Vaultwarden — whatever you use)
so that your team can access it.
| If you lose the key, nobody (including you) can decrypt your credentials. |
Anyone who has the key can decrypt your credentials, so keep it safe.
You can also ship the key via the environment variable
RAILS_MASTER_KEY. In production that is usually the
preferred approach because config/master.key never hits
the server’s filesystem — see
Environment
Variables in Production.
|
Editing Credentials
The encrypted credentials live in config/credentials.yml.enc.
Because the file is encrypted you can’t open it in your editor
directly. Use bin/rails credentials:edit:
$ bin/rails credentials:edit
Rails launches $EDITOR with a temporary plaintext copy. When
you save and close, Rails re-encrypts the file and writes it
back to disk. If $EDITOR is not set you can specify one
inline:
$ EDITOR=vim bin/rails credentials:edit
or once in your shell profile: export EDITOR=code -w.
Credentials are written in YAML:
# aws:
# access_key_id: 123
# secret_access_key: 345
# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
secret_key_base: 9846dad34a3168...68d634f
test: foobar
Per-Environment Credentials
Since Rails 6 each environment can have its own credentials file. The development/test/production split is a major improvement over the old single-file model — you no longer have to share production secrets with every contributor.
Edit environment-specific credentials with --environment:
$ bin/rails credentials:edit --environment production
This creates (or opens) config/credentials/production.yml.enc
and its matching key config/credentials/production.key. The
environment-specific key takes precedence over
config/master.key when that environment is booted; if there is
no environment-specific key Rails falls back to
RAILS_MASTER_KEY or config/master.key.
Typical team workflow:
-
config/credentials.yml.enc+config/master.keyfor shared secrets used in development and test (stripe test keys, webhook signing secrets, SMTP creds for a staging inbox, …). -
config/credentials/production.yml.enc
config/credentials/production.keyfor the production-only secrets. Only the ops/deploy team has the production key.
Accessing a Credential
You can access a credential via
Rails.application.credentials.<key>:
$ bin/rails console
Loading development environment (Rails 8.1.3)
irb(main):001> Rails.application.credentials.test
=> "foobar"
irb(main):002> Rails.application.credentials.dig(:aws, :access_key_id)
=> "123"
irb(main):003> exit
credentials.dig(…) is safe even if a nested key is missing
(returns nil). For a required value use .fetch:
Rails.application.credentials.fetch(:stripe).fetch(:secret_key)
.fetch raises KeyError if the value is missing, which is
usually what you want at boot time.
Using the Credentials on the Production Web Server
Set RAILS_MASTER_KEY (or the per-environment key, for example
RAILS_PRODUCTION_KEY) in the environment of the production
process. With Kamal (the
Rails 8 default) you list it under env.secret: in
config/deploy.yml and store the real value in
.kamal/secrets. With Heroku you set it via
heroku config:set RAILS_MASTER_KEY=xxxx. With a hand-rolled
Docker / systemd / Nginx setup, put it in the unit file or
/etc/environment — wherever fits your setup.
Never deploy config/master.key as a file to the
production server filesystem. Use the environment
variable. If anyone gains read access to the server
they still shouldn’t have the decryption key.
|
Rotating the Key
If a key leaks (or you suspect it did) rotate immediately:
-
Generate a new random key:
bin/rails credentials:edit --environment productionwith the old key set, copy the secret values out. -
Delete
config/credentials/production.yml.encandconfig/credentials/production.key. -
Re-run
bin/rails credentials:edit --environment productionto create new files, paste the values back in. -
Ship the new key to your server via environment variable and redeploy.