Credentials

Learn about credentials in Ruby on Rails development.

Beginner 1 min read

Secrets & Credentials

Securely manage sensitive information like API keys, database passwords, and other secrets in your Rails application using Rails credentials system.

Rails Credentials System

# Edit credentials (Rails 6+)
rails credentials:edit

# Edit credentials for specific environment
rails credentials:edit --environment production
rails credentials:edit --environment staging

# Show credentials (for debugging - be careful!)
rails credentials:show
rails credentials:show --environment production
# config/credentials.yml.enc (encrypted)
# Edit with: rails credentials:edit

secret_key_base: your_secret_key_base_here

database:
  username: myapp_user
  password: super_secure_password

api_keys:
  stripe:
    publishable: pk_test_12345
    secret: sk_test_67890
  aws:
    access_key_id: AKIAIOSFODNN7EXAMPLE
    secret_access_key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
  sendgrid:
    api_key: SG.your_sendgrid_api_key_here

oauth:
  google:
    client_id: your_google_client_id
    client_secret: your_google_client_secret
  github:
    client_id: your_github_client_id
    client_secret: your_github_client_secret

redis:
  url: redis://localhost:6379/0

production:
  database:
    username: prod_user
    password: production_password
# Accessing credentials in your application
class ApplicationController < ActionController::Base
  private
  
  def stripe_secret_key
    Rails.application.credentials.api_keys.stripe.secret
  end
end

# In models
class User < ApplicationRecord
  def send_welcome_email
    # Access nested credentials
    api_key = Rails.application.credentials.api_keys.sendgrid.api_key
    # Use the API key...
  end
end

# In configuration files
# config/database.yml
production:
  adapter: postgresql
  username: <%= Rails.application.credentials.database.username %>
  password: <%= Rails.application.credentials.database.password %>
  
# config/initializers/stripe.rb
Stripe.api_key = Rails.application.credentials.api_keys.stripe.secret
Master Key Security: The config/master.key file must be kept secure and never committed to version control. It's needed to decrypt your credentials.

Environment-Specific Credentials

# Create environment-specific credential files
rails credentials:edit --environment staging
rails credentials:edit --environment production

# This creates:
# config/credentials/staging.yml.enc
# config/credentials/staging.key
# config/credentials/production.yml.enc  
# config/credentials/production.key
# config/credentials/production.yml.enc
secret_key_base: production_secret_key_base

database:
  host: prod-db.example.com
  username: prod_user
  password: production_secure_password

api_keys:
  stripe:
    publishable: pk_live_real_key
    secret: sk_live_real_secret
  aws:
    access_key_id: PRODUCTION_ACCESS_KEY
    secret_access_key: production_secret_access_key

redis:
  url: redis://prod-redis.example.com:6379/0
  password: redis_production_password
# Accessing environment-specific credentials
class PaymentService
  def initialize
    @stripe_key = if Rails.env.production?
      Rails.application.credentials.api_keys.stripe.secret
    else
      Rails.application.credentials.api_keys.stripe.secret
    end
  end
  
  # Or let Rails handle it automatically:
  def stripe_key
    Rails.application.credentials.api_keys.stripe.secret
  end
end

# In initializers - Rails automatically uses environment-specific credentials
# config/initializers/redis.rb
$redis = Redis.new(url: Rails.application.credentials.redis.url)

Legacy Secrets (Rails 5.x)

# config/secrets.yml (deprecated but still supported)
development:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
  api_key: <%= ENV["API_KEY"] %>

test:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
  api_key: <%= ENV["API_KEY"] %>
# Accessing legacy secrets
Rails.application.secrets.secret_key_base
Rails.application.secrets.api_key

# Migration strategy from secrets.yml to credentials
# 1. Move secrets to credentials:
rails credentials:edit

# 2. Add your secrets to the credentials file
# 3. Update your code to use credentials instead:
# Old: Rails.application.secrets.api_key  
# New: Rails.application.credentials.api_key

# 4. Remove config/secrets.yml

Migration from Secrets.yml

  • Rails 5.2+ uses credentials system by default
  • Credentials are more secure than secrets.yml
  • Credentials are encrypted, secrets.yml relied on ENV vars
  • Both systems can coexist during migration

Environment Variables Integration

# Combining credentials with environment variables
# config/initializers/app_config.rb
class AppConfig
  def self.database_url
    ENV['DATABASE_URL'] || Rails.application.credentials.database_url
  end
  
  def self.redis_url
    ENV['REDIS_URL'] || Rails.application.credentials.redis.url
  end
  
  def self.stripe_secret_key
    ENV['STRIPE_SECRET_KEY'] || Rails.application.credentials.api_keys.stripe.secret
  end
end

# Using in application
class PaymentController < ApplicationController
  def process_payment
    Stripe.api_key = AppConfig.stripe_secret_key
    # Process payment...
  end
end
# .env file for development (with dotenv gem)
DATABASE_URL=postgresql://localhost/myapp_development
REDIS_URL=redis://localhost:6379/0
STRIPE_SECRET_KEY=sk_test_development_key
SENDGRID_API_KEY=SG.development_api_key
# Gemfile
group :development, :test do
  gem 'dotenv-rails'
end

# config/application.rb
module MyApp
  class Application < Rails::Application
    # Load environment variables in development/test
    if Rails.env.development? || Rails.env.test?
      Dotenv.load('.env.local', '.env')
    end
  end
end
Pro Tip: Use ENV variables for deployment-specific values and credentials for application-specific secrets.

Security Best Practices

# Credential management in deployment
# 1. Never commit master.key or environment-specific .key files

# 2. Set master key as environment variable in production
export RAILS_MASTER_KEY="your_master_key_here"

# 3. Or create the key file securely on production server
echo "your_master_key_here" > config/master.key
chmod 600 config/master.key

# 4. For environment-specific keys
export RAILS_MASTER_KEY_PRODUCTION="production_master_key"
export RAILS_MASTER_KEY_STAGING="staging_master_key"
# Secure credential access patterns
class SecureConfig
  # Lazy loading credentials
  def self.stripe_secret_key
    @stripe_secret_key ||= Rails.application.credentials.api_keys.stripe.secret
  end
  
  # Fallback for missing credentials
  def self.redis_url
    Rails.application.credentials.redis&.url || 'redis://localhost:6379/0'
  end
  
  # Environment-specific access
  def self.database_password
    credentials = Rails.application.credentials
    
    case Rails.env
    when 'production'
      credentials.database.production.password
    when 'staging'  
      credentials.database.staging.password
    else
      credentials.database.development.password
    end
  end
end
Security Warnings:
  • Never log or output credentials in plain text
  • Don't include master.key in version control
  • Use different credentials for each environment
  • Rotate credentials regularly
  • Limit access to credential files on production servers

Credential Rotation & Management

# Rotating credentials
# 1. Generate new master key
rails credentials:edit --environment production

# 2. Create backup of current credentials  
cp config/credentials/production.yml.enc config/credentials/production.yml.enc.backup

# 3. Update credentials with new values
rails credentials:edit --environment production

# 4. Deploy with new master key
# 5. Test thoroughly
# 6. Remove backup after successful deployment
# Credential validation
# config/initializers/credential_validator.rb
class CredentialValidator
  def self.validate!
    required_credentials = [
      'secret_key_base',
      'database.password',
      'api_keys.stripe.secret'
    ]
    
    required_credentials.each do |path|
      value = path.split('.').reduce(Rails.application.credentials) { |creds, key| 
        creds&.send(key.to_sym) 
      }
      
      if value.nil? || value.empty?
        raise "Missing required credential: #{path}"
      end
    end
  end
end

# In config/application.rb
if Rails.env.production?
  CredentialValidator.validate!
end
# Credential monitoring
# lib/tasks/credentials.rake
namespace :credentials do
  desc "Check credential health"
  task health_check: :environment do
    credentials = Rails.application.credentials
    
    checks = {
      'Secret Key Base' => credentials.secret_key_base.present?,
      'Database Password' => credentials.database&.password.present?,
      'Redis URL' => credentials.redis&.url.present?,
      'Stripe Keys' => credentials.api_keys&.stripe&.secret.present?
    }
    
    checks.each do |name, status|
      puts "#{name}: #{status ? '✅' : '❌'}"
    end
  end
  
  desc "List all credential keys (without values)"
  task list: :environment do
    def print_keys(obj, prefix = '')
      case obj
      when ActiveSupport::EncryptedConfiguration
        obj.config.each { |key, value| print_keys(value, "#{prefix}#{key}.") }
      when Hash
        obj.each { |key, value| print_keys(value, "#{prefix}#{key}.") }
      else
        puts "#{prefix.chomp('.')}"
      end
    end
    
    print_keys(Rails.application.credentials)
  end
end

Best Practices

Use environment-specific credentials: Different secrets for dev/staging/prod
Never commit master keys: Keep them secure and separate from code
Validate credentials on startup: Catch missing secrets early
Use fallback values: Graceful handling of missing credentials
Rotate regularly: Change secrets periodically for security
Monitor access: Track credential usage and potential breaches