Skip to main content

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/environments/test.rb
config.dotenv.autorestore = false

Production

Use platform's native secret management: GCP Secret Manager, AWS Secrets Manager, Kubernetes secrets, etc.

References