Environment Variables with dotenv-rails
dotenv-rails loads environment variables from .env files into ENV when your app boots. It's the standard way to manage local config values or manually exporting variables in your shell.
Install
Add this line to the application's Gemfile and run bundle install:
# Gemfile
gem "dotenv-rails", groups: [:development, :test]
bundle install
That's all the setup needed. dotenv-rails loads automatically when your Rails app boots, before any initializers run.
Create a .env File
Create a .env file in your project root:
# .env
DATABASE_URL=postgres://localhost/myapp_development
PAYMENT_API_KEY=test_abc123
LOG_LEVEL=debug
Variables are available in your app via ENV:
ENV["DATABASE_URL"] # => "postgres://localhost/myapp_development"
ENV.fetch("PAYMENT_API_KEY") # => "test_abc123"
File Loading Priority
dotenv-rails loads multiple files depending on the current RAILS_ENV. The order from highest to lowest priority:
.env.development.local
.env.local
.env.development
.env
The first file that defines a variable wins. So .env.development.local overrides everything, and .env is the base fallback.
Here's what each file is for:
.env — base config shared across all environments
.env.development — development-specific values
.env.local — personal overrides, not scoped to any environment
.env.development.local — personal overrides scoped to development only
In practice, most projects only need .env and .env.local.
.gitignore
Always add your local override files to .gitignore:
# .gitignore
.env.local
.env.*.local
Whether to commit .env itself depends on what's in it. If it only contains non-sensitive defaults (localhost URLs, dev-mode feature flags), committing it makes onboarding easier for other developers.
.env.example as a Template
A common pattern is to commit a .env.example with all the required keys but no real values. New team members copy it and fill in their own values:
# .env.example
DATABASE_URL=
REDIS_URL=
PAYMENT_API_KEY=
LOG_LEVEL=debug
cp .env.example .env.local
dotenv-rails in Test
Since v3.0, dotenv-rails automatically restores ENV after each test. This means you can modify ENV in a test without worrying about leaking state to other tests. It works with both Minitest and RSpec out of the box.
To disable it:
config.dotenv.autorestore = false
Production
Use platform's native secret management: GCP Secret Manager, AWS Secrets Manager, Kubernetes secrets, etc.