What does SECRET_KEY_BASE actually do in a Rails app?
It is the root secret Rails derives every other key from: the one that signs session cookies, encrypts encrypted cookies, and verifies signed global ids and Active Storage URLs. Change it and every existing session becomes invalid, so every signed-in user is logged out at the moment of deploy.
Where it comes from
In production, either the SECRET_KEY_BASE environment variable or the encrypted credentials, which
are themselves unlocked by RAILS_MASTER_KEY. One of the two has to be present or the app raises
during initialization.
bin/kamal app exec 'bin/rails runner "puts Rails.application.secret_key_base.present?"'
In development and test Rails generates and caches one for you, which is why you have never had to think about it locally.
What breaks when it changes
- Every session. Session cookies are signed with a derived key. A new base means existing cookies fail verification and are discarded, so everyone is signed out.
- Signed URLs. Active Storage URLs and any
signed_idalready issued stop verifying. - Encrypted cookies. Anything stored in one is unreadable.
None of it is data loss. It is all re-derivable by signing in again. But doing it during business hours means every user hits a sign-in screen at once, which for a passwordless app means every user waits for an email.
It is not the same as the master key
Two different things, often confused:
| Secret | Protects |
|---|---|
RAILS_MASTER_KEY |
Decrypts config/credentials.yml.enc. |
SECRET_KEY_BASE |
Derives signing and encryption keys at runtime. |
The master key can be used to store the secret key base, which is how most apps are set up, and is why they look like one thing.
Rotating it
There is no zero-downtime path for a single value, because there is no window where both are valid. The practical approach is to deploy the change at a quiet hour and accept the sign-outs. Rails supports rotation for cookies specifically, which can smooth it, but it is configuration you add before you need it, not during.
Do not commit it
bin/check runs a secret scan, so a key pasted into a tracked file fails the build. That check is
the reason this value has never needed rotating in this repository.