Andy from Webcrunch

Subscribe for email updates:

Portrait of Andy Leverenz
Andy Leverenz

March 30, 2022

•

Last updated October 20, 2026

The Complete Guide to Ruby on Rails Encrypted Credentials

Rails encrypted credentials let you keep application secrets in an encrypted file alongside your code. Your app reads those secrets with Rails.application.credentials; a separate encryption key unlocks the file.

The part that deserves extra attention is choosing the right file and the right key. A production credentials file does not merge with your shared credentials, and an environment variable can override the key sitting on your computer.

I'll walk through editing credentials, reading them in Ruby, separating environments, and troubleshooting deployment without printing secrets into your terminal logs.

This guide targets Rails 8.x and uses the Rails 8.1 documentation and source as its reference. Environment-specific credentials are also available in Rails 6 and 7. The original video walkthrough uses an older Rails setup; follow the commands below for the updated explanation.

Rails credentials quick reference

Run these commands from your application's root directory.

# Edit the currently selected credentials file.
# In a new app with the standard setup, this is config/credentials.yml.enc.
EDITOR="code --wait" bin/rails credentials:edit

# Create or edit credentials for a particular environment.
EDITOR="code --wait" bin/rails credentials:edit --environment development
EDITOR="code --wait" bin/rails credentials:edit --environment production

# Read a nested value from application code.
# Ruby: Rails.application.credentials.dig(:stripe, :secret_key)

Here are the conventional file pairs:

  • Shared: config/credentials.yml.enc, with its key in config/master.key.
  • Development: config/credentials/development.yml.enc, with its key in config/credentials/development.key.
  • Test: config/credentials/test.yml.enc, with its key in config/credentials/test.key.
  • Production: config/credentials/production.yml.enc, with its key in config/credentials/production.key.

The .yml.enc files can go into Git. The key files must not. A deployment can supply the matching key through RAILS_MASTER_KEY instead of a key file.

This list describes the normal pairing, not a guarantee that Rails found the right key. Custom configuration and an already-set RAILS_MASTER_KEY can change the result. We'll cover that shortly.

What encrypted credentials actually protect

Think of the encrypted file and its key as two separate pieces. Committing credentials.yml.enc lets your team version the application's configuration without committing the readable secrets. Anyone who also has the matching key can decrypt the entire file.

That last point matters. Credentials are not a permissions system for individual entries. Sharing a production key gives someone access to everything in that production file, not just the email API key they happen to need.

Keep encryption keys in an approved password manager or secret manager. Give developers access to the environments they need, and keep production access narrower where possible. Encryption at rest also does not stop application code from leaking a decrypted value through a log message, error report, or HTML response.

Credentials are a useful option, not a requirement for every app. A managed secret store or environment variables may fit your deployment better. Whichever you choose, make the source of each secret clear rather than maintaining several conflicting copies.

Edit credentials with an editor that waits

For a standard new Rails app, start with:

EDITOR="code --wait" bin/rails credentials:edit

code is VS Code's command-line launcher. The --wait flag keeps the command attached while you edit. Save and close the temporary file so Rails can finish encrypting it.

If VS Code's launcher isn't installed, use a terminal editor instead:

EDITOR="nano" bin/rails credentials:edit

Rails also recognizes VISUAL. If setting EDITOR opens a different editor, check whether VISUAL is already set and adjust that setting too.

Read the Editing ... path printed by the command before making changes. In Rails 8.x, an existing environment-specific file or custom credentials configuration can affect the default selection. Setting RAILS_ENV=production is not a way to force the shared file.

To explicitly edit an existing shared file even when an environment-specific file is present, Rails also provides the lower-level encrypted-file command:

EDITOR="code --wait" bin/rails encrypted:edit config/credentials.yml.enc --key config/master.key

RAILS_MASTER_KEY still takes precedence over the --key file. Do not run this with an unrelated production key exported in your shell.

Add a small, organized YAML section

Inside the editor, add a service namespace like this:

# Keep any existing secret_key_base entry unchanged.
# These example values are deliberately not working API keys.
stripe:
  publishable_key: "example-publishable-key"
  secret_key: "example-not-a-real-secret"

Use your provider's appropriate test credentials when setting up development. Don't paste production credentials into a tutorial app.

YAML indentation matters. Use spaces, keep related values together, and keep the same key names across environments. secret_key and private_key are different names as far as Rails is concerned.

Once you close the editor, check that Rails reports a successful save and no YAML warning. The encrypted file changes on disk. You don't manually edit its encrypted contents.

Rails can create missing files during the credentials editing workflow. It cannot recreate the original key for an existing encrypted file. If an established app is missing its key, get that key from its authorized owner or secret store rather than treating a new key as a replacement.

Read credentials in application code

For the YAML above, use:

Rails.application.credentials.dig(:stripe, :publishable_key)
Rails.application.credentials.dig(:stripe, :secret_key)

I like dig here because the lookup is explicit and easy to scan. A missing entry returns nil, so decide whether the value is optional or required before handing it to another library.

For a required setting, validate it without including its value in the error:

# For example, in the code that initializes your payment client.
stripe_secret_key = Rails.application.credentials.dig(:stripe, :secret_key)

if stripe_secret_key.blank?
  raise "Missing stripe.secret_key in application credentials"
end

# Pass stripe_secret_key to your payment client's configuration here.

This only checks that a value exists. It does not prove that the provider accepts it or that it belongs to the correct account or mode.

To check a value from the terminal without displaying it:

bin/rails runner 'puts Rails.application.credentials.dig(:stripe, :secret_key).present?'

The result is a boolean, not the secret. Keep actual secret values out of screenshots, support tickets, console transcripts, and logs. Rails has commands that display decrypted credentials, but those are not a good default for deployment diagnostics.

Separate development and production credentials

A development app should not need production API keys just to boot. Create a development-specific file with:

EDITOR="code --wait" bin/rails credentials:edit --environment development

With the standard configuration, the intended pair is:

config/credentials/development.yml.enc
config/credentials/development.key

Before creating separate environment files, make sure your shell is not supplying an unrelated RAILS_MASTER_KEY. Rails will use that environment variable rather than generate or read a different key just because you changed --environment.

Now create the production file when you're ready to configure that environment:

EDITOR="code --wait" bin/rails credentials:edit --environment production

Use the same YAML structure in both files, with values appropriate to each environment. Your Ruby lookup stays the same:

Rails.application.credentials.dig(:stripe, :secret_key)

Environment files replace the shared file; they do not merge

Suppose the shared credentials contain both stripe and email_service, but your production file contains only stripe.

In production, email_service does not fall back to the shared file. The production credentials file is selected as a whole. Put all credentials needed by that environment in its selected file, or deliberately use another configuration source for the missing values.

This also applies to secret_key_base when your deployment obtains it from credentials. Creating a production override can hide the value that was previously available from the shared file. Preserve the intended production value when migrating an existing app; don't casually replace it with a tutorial placeholder.

Adding top-level development: and production: sections inside one credentials file is a different arrangement. Rails does not automatically select one of those sections for your custom values. You would have to include that nesting in your own lookup.

How Rails chooses the file and key

Under the standard configuration, Rails makes two separate selections:

  1. For content, use config/credentials/<environment>.yml.enc if it exists; otherwise use config/credentials.yml.enc.
  2. For a key file, prefer config/credentials/<environment>.key if it exists; otherwise use config/master.key.
  3. For the actual decryption key, a nonblank RAILS_MASTER_KEY takes precedence over the selected key file.

An important consequence: if production.yml.enc exists but production.key does not, Rails does not switch back to the shared encrypted file. A fallback key that cannot decrypt the selected content will fail.

An app can override config.credentials.content_path and config.credentials.key_path. Check those settings before assuming the conventional filenames are authoritative. The Rails configuration guide documents both paths.

Deploy credentials without committing the key

The deploy needs the encrypted file and the matching key, supplied separately.

For production-specific credentials, that usually means:

  1. Include config/credentials/production.yml.enc in the deployed code.
  2. Store the contents of the matching production.key in your host's protected RAILS_MASTER_KEY secret setting.
  3. Make that setting available to the production processes that need it, including background workers.
  4. Restart or redeploy those processes after changing the secret configuration.

If production uses the shared file instead, supply its matching master key. The variable name is still RAILS_MASTER_KEY; it doesn't become RAILS_PRODUCTION_KEY.

Use your host's secret-management workflow. Avoid commands that paste the literal key into shell history, echo it into CI output, or bake it into an image. Keep build-time secrets separate from ordinary build arguments and supply them only when the build actually requires them.

If the application depends on encrypted credentials, you can require a key at boot:

# config/environments/production.rb
Rails.application.configure do
  config.require_master_key = true
end

Add that setting inside the existing configuration block. It requires a key to be available; it does not upload one, validate every required credential, or grant the deployment access to your password manager.

The master key is not secret_key_base

RAILS_MASTER_KEY unlocks encrypted credentials. secret_key_base is an application secret used in Rails' key-generation system, including signing and encryption associated with cookies.

They are not interchangeable. Don't put the master key into SECRET_KEY_BASE, and don't change secret_key_base just to fix a missing decryption key. Changing an existing production secret_key_base can invalidate existing cookies and sessions unless you plan a compatible rotation.

Troubleshoot without dumping secrets

Start with file selection and key availability, not with printing the entire credentials object.

On a configured production runtime, this reports paths and booleans only:

bin/rails runner -e production '
  config = Rails.application.config.credentials
  puts "environment=#{Rails.env}"
  puts "content_path=#{config.content_path}"
  puts "key_path=#{config.key_path}"
  puts "environment_key_present=#{ENV["RAILS_MASTER_KEY"].present?}"
  puts "key_file_present=#{File.exist?(config.key_path)}"
  puts "stripe_secret_present=#{Rails.application.credentials.dig(:stripe, :secret_key).present?}"
'

This boots the application. If an initializer fails first, the diagnostic won't run; inspect the boot error and your deployment's secret configuration before trying again.

“Couldn't decrypt” or InvalidMessage

The selected key and encrypted file may not match, or the encrypted content may have been altered. Check the deployed file, environment, and key source together. A stale RAILS_MASTER_KEY can override a perfectly valid local key file.

Don't delete the encrypted file to make the error disappear. That trades a configuration error for lost credentials.

A credential returns nil

Check spelling, nesting, and the active environment. Then ask whether a newly created environment-specific file replaced the shared one. Also restart long-running processes after editing credentials so you're not relying on already-loaded configuration.

Missing key

Retrieve the matching key through your team's authorized secret-sharing process. A newly generated key won't decrypt old content. Keep recoverable backups of encryption keys separate from the repository.

Changes don't save

Check the editor's wait option, whether the temporary file was saved and closed, and whether Rails reported invalid YAML. Don't copy the temporary plaintext file into the repository as a workaround.

A key was committed or exposed

Treat the secrets accessible with that key as exposed. Revoke or rotate affected provider credentials, replace the encryption key through a controlled process, and update deployments. Removing the key from the latest Git commit does not remove it from history or from existing clones. Re-encrypting current content alone does not protect secrets in older encrypted revisions that the exposed key can still unlock.

Before your next commit, verify the ignore rules and tracked files:

# These should be ignored, not committed.
git check-ignore config/master.key config/credentials/production.key

# Investigate any key files this prints: they are already tracked.
git ls-files config/master.key 'config/credentials/*.key'

A useful baseline for .gitignore is:

/config/master.key
/config/credentials/*.key

Ignore rules don't untrack a file that Git already knows about. If a key was committed, address the exposure as well as the repository cleanup.

Keep the workflow simple

The routine is straightforward: edit through Rails, read through Rails.application.credentials, commit the encrypted file, and protect the matching key separately.

When something breaks, verify the selected file and key before changing either. That small habit makes credentials much easier to reason about, especially once development, test, and production stop sharing the same settings.

Link this article
Est. reading time: 11 minutes
Stats: 93,789 views

Categories

Collection

Part of the Ruby on Rails collection

Products and courses