Rails Credentials and Secrets in 2026: Secure Environment Variables Management

Master Rails 8 credentials system for secure secret management. Learn encrypted credentials, environment-specific keys, deployment strategies, and best practices for API keys and database passwords.

Rails credentials and secrets management with secure vault concept

Rails 8 credentials provide a secure, encrypted storage system for sensitive configuration data like API keys, database passwords, and third-party service tokens. Unlike environment variables scattered across deployment configurations, credentials live in version control as encrypted files, ensuring consistency across environments while maintaining security.

Quick Overview

Rails credentials store secrets in config/credentials.yml.enc, encrypted with a master key in config/master.key. The master key never enters version control—only the encrypted file does. This approach eliminates environment variable sprawl and prevents accidental secret exposure in logs or error messages.

Understanding the Rails Credentials Architecture

The credentials system introduced in Rails 5.2 and refined through Rails 8 consists of three components: the encrypted credentials file, the master key, and the Rails credentials API for accessing values at runtime.

The encrypted file (config/credentials.yml.enc) contains YAML-formatted secrets encrypted using AES-256-GCM. The master key (config/master.key) decrypts this file. Rails automatically loads credentials on boot, making them available throughout the application via Rails.application.credentials.

ruby
# config/credentials.yml.enc (decrypted view)
# Access via: Rails.application.credentials

aws:
  access_key_id: AKIAIOSFODNN7EXAMPLE
  secret_access_key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
  region: us-east-1

stripe:
  publishable_key: pk_live_51ABC...
  secret_key: sk_live_51ABC...
  webhook_secret: whsec_...

database:
  password: production_db_password_here

This structure groups related secrets logically. Access nested values using method chaining: Rails.application.credentials.aws.access_key_id returns the AWS key directly.

Editing Credentials with the Rails CLI

The rails credentials:edit command decrypts the credentials file, opens it in an editor, and re-encrypts on save. The editor respects the EDITOR environment variable.

bash
# Edit credentials with default editor
EDITOR="code --wait" rails credentials:edit

# Edit environment-specific credentials (Rails 6+)
EDITOR="vim" rails credentials:edit --environment production

# Show current credentials (read-only)
rails credentials:show

# Show environment-specific credentials
rails credentials:show --environment staging

For teams using VS Code, the --wait flag ensures Rails waits for the editor to close before re-encrypting. Without it, Rails immediately re-encrypts an empty file.

Editor Configuration

Set EDITOR permanently in shell configuration (.bashrc, .zshrc) to avoid specifying it each time. For GUI editors, always include wait flags: code --wait for VS Code, subl -w for Sublime Text.

Environment-Specific Credentials for Multi-Stage Deployments

Production, staging, and development environments often require different API keys and database configurations. Rails supports environment-specific credentials files, each with its own master key.

bash
# Create production credentials
rails credentials:edit --environment production
# Creates: config/credentials/production.yml.enc
# Creates: config/credentials/production.key

# Create staging credentials
rails credentials:edit --environment staging
# Creates: config/credentials/staging.yml.enc
# Creates: config/credentials/staging.key

Rails loads credentials based on RAILS_ENV. In production, it reads config/credentials/production.yml.enc using config/credentials/production.key. This separation ensures developers cannot accidentally access production secrets.

ruby
# config/environments/production.rb
# Rails automatically loads production credentials when RAILS_ENV=production

# Fallback chain: environment-specific -> default credentials
config.require_master_key = true

The require_master_key setting raises an error on boot if the master key is missing, preventing silent failures in production.

Accessing Credentials in Application Code

Rails provides multiple patterns for accessing credentials depending on the use case. Direct access works for simple lookups, while the dig method handles nested structures safely.

ruby
# app/services/payment_service.rb
class PaymentService
  def initialize
    # Direct access - raises if key missing
    @api_key = Rails.application.credentials.stripe.secret_key
    
    # Safe access with dig - returns nil if path missing
    @webhook_secret = Rails.application.credentials.dig(:stripe, :webhook_secret)
    
    # With default fallback
    @timeout = Rails.application.credentials.dig(:stripe, :timeout) || 30
  end
  
  def process_payment(amount:, customer_id:)
    Stripe::PaymentIntent.create(
      amount: amount,
      customer: customer_id,
      api_key: @api_key
    )
  end
end

For credentials used in initializers, access them during configuration:

ruby
# config/initializers/stripe.rb
Stripe.api_key = Rails.application.credentials.dig(:stripe, :secret_key)
Stripe.webhook_secret = Rails.application.credentials.dig(:stripe, :webhook_secret)

# Fail fast if critical credentials missing
unless Stripe.api_key
  raise "Stripe API key not configured in credentials"
end

This pattern surfaces configuration errors at boot time rather than during runtime requests.

Ready to ace your Ruby on Rails interviews?

Practice with our interactive simulators, flashcards, and technical tests.

Integrating Credentials with Third-Party Services

Most Rails applications integrate with external services requiring authentication. Structure credentials to mirror service organization and validate presence at initialization.

ruby
# config/credentials.yml.enc
sendgrid:
  api_key: SG.xxxxxxxx
  
twilio:
  account_sid: ACxxxxxxxx
  auth_token: xxxxxxxx
  phone_number: "+15551234567"

redis:
  url: redis://:password@redis.example.com:6379/0

sentry:
  dsn: https://key@sentry.io/project

Configure services in initializers using credentials:

ruby
# config/initializers/sentry.rb
Sentry.init do |config|
  config.dsn = Rails.application.credentials.dig(:sentry, :dsn)
  config.environment = Rails.env
  config.traces_sample_rate = Rails.env.production? ? 0.1 : 1.0
end

# config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
  config.redis = { url: Rails.application.credentials.dig(:redis, :url) }
end

Sidekiq.configure_client do |config|
  config.redis = { url: Rails.application.credentials.dig(:redis, :url) }
end

This approach centralizes secret management while maintaining clear service boundaries.

Deploying with Encrypted Credentials

Deployment platforms need access to the master key to decrypt credentials at runtime. The key can be provided via environment variable or file, depending on the platform.

bash
# Set master key via environment variable
# Rails checks RAILS_MASTER_KEY before looking for master.key file
export RAILS_MASTER_KEY="abc123def456..."

# For environment-specific credentials
export RAILS_MASTER_KEY_PRODUCTION="xyz789..."

For containerized deployments, inject the master key at runtime:

dockerfile
# Dockerfile
FROM ruby:3.3-slim

WORKDIR /app
COPY Gemfile Gemfile.lock ./
RUN bundle install

COPY . .

# Do NOT copy master.key into image
# Master key provided at runtime via RAILS_MASTER_KEY

CMD ["rails", "server", "-b", "0.0.0.0"]
yaml
# docker-compose.yml
services:
  web:
    build: .
    environment:
      - RAILS_ENV=production
      - RAILS_MASTER_KEY=${RAILS_MASTER_KEY}
    ports:
      - "3000:3000"

Major platforms have specific patterns for secret injection. Heroku uses config vars, AWS uses Secrets Manager or Parameter Store for the master key itself.

CI/CD Pipelines

Store the master key in CI/CD secret storage (GitHub Secrets, GitLab CI Variables). The credentials file commits to the repository normally—only the key needs secure handling.

Rotating Credentials and Key Management

Regular rotation of secrets reduces exposure from potential leaks. Rails credentials support rotation by editing the file and redeploying.

bash
# Rotate a specific secret
EDITOR="vim" rails credentials:edit
# Update the secret value, save, and close

# Verify the change
rails credentials:show | grep stripe

For master key rotation (rarely needed), create new credentials:

bash
# Backup current credentials
rails credentials:show > /tmp/credentials_backup.yml

# Remove old encrypted file and key
rm config/credentials.yml.enc config/master.key

# Create new credentials with new key
rails credentials:edit
# Paste contents from backup, modify as needed

# Update RAILS_MASTER_KEY in all environments

Automate rotation reminders using credential metadata:

ruby
# config/credentials.yml.enc
_metadata:
  rotated_at: "2026-07-01"
  
aws:
  access_key_id: AKIAIOSFODNN7EXAMPLE
  # ... rest of credentials

A scheduled job can check rotation dates and alert when secrets approach expiration.

Testing with Credentials

Test environments need credentials access without exposing production secrets. Rails loads config/credentials/test.yml.enc when RAILS_ENV=test, allowing isolated test credentials.

bash
# Create test credentials
rails credentials:edit --environment test
ruby
# config/credentials/test.yml.enc
stripe:
  secret_key: sk_test_xxx
  publishable_key: pk_test_xxx

aws:
  access_key_id: test_key
  secret_access_key: test_secret
  region: us-east-1

For unit tests mocking external services, stub credentials directly:

ruby
# spec/services/payment_service_spec.rb
require "rails_helper"

RSpec.describe PaymentService do
  before do
    # Stub credentials for isolated testing
    allow(Rails.application.credentials).to receive(:dig)
      .with(:stripe, :secret_key)
      .and_return("sk_test_mock")
  end
  
  describe "#process_payment" do
    it "creates a payment intent" do
      service = described_class.new
      # Test implementation
    end
  end
end

This approach isolates tests from actual credential values while verifying credential access patterns. For more on testing strategies, see the guide on Rails testing with RSpec.

Credentials vs Environment Variables: When to Use Each

Rails credentials excel for secrets that rarely change and benefit from version control tracking. Environment variables suit values that change per deployment or require dynamic updates without redeploy.

| Aspect | Credentials | Environment Variables | |--------|-------------|----------------------| | Storage | Encrypted in repo | External config | | Version Control | Yes (encrypted) | No | | Rotation | Requires redeploy | Runtime update possible | | Audit Trail | Git history | Platform-dependent | | Team Access | Anyone with master key | Platform RBAC | | Best For | API keys, DB passwords | Feature flags, URLs |

Hybrid approaches work well. Store sensitive secrets in credentials, use environment variables for non-sensitive configuration:

ruby
# config/application.rb
config.app_domain = ENV.fetch("APP_DOMAIN", "localhost:3000")
config.redis_url = Rails.application.credentials.dig(:redis, :url) || ENV["REDIS_URL"]

This pattern allows flexible deployment while maintaining security for sensitive values. Understanding authentication and authorization patterns helps determine which secrets need the strongest protection.

Conclusion

  • Store all sensitive secrets in config/credentials.yml.enc, never in environment variables or plain text files
  • Use environment-specific credentials (--environment production) for multi-stage deployments with separate master keys
  • Access credentials via Rails.application.credentials.dig for safe nested lookups with nil fallback
  • Inject RAILS_MASTER_KEY at runtime in containerized deployments—never bake the key into images
  • Create test-specific credentials to isolate test suites from production secrets
  • Rotate secrets by editing credentials and redeploying; track rotation dates in credential metadata
  • Combine credentials for secrets with environment variables for non-sensitive, frequently-changing configuration

Start practicing!

Test your knowledge with our interview simulators and technical tests.

Tags

#ruby-on-rails
#security
#credentials
#secrets-management
#devops

Share

Related articles