What is a .env File
and How to Use It Safely
The right way to keep API keys, database passwords and secrets out of your source code — and out of Git.
What is a .env file and how to use it safely is one of those fundamentals that every developer needs but few resources explain clearly. The short version: a .env file is where you store secrets — API keys, database passwords, tokens — so they live outside your code and never end up in Git. Here's the complete guide.
What is a .env File?
A .env file is a plain text file containing key-value pairs, one per line. Each line defines an environment variable that your application reads at runtime:
# .env — never commit this file
DATABASE_URL=postgresql://user:password@localhost:5432/myapp
STRIPE_SECRET_KEY=sk_live_abc123xyz
OPENAI_API_KEY=sk-proj-abc123
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
SENDGRID_API_KEY=SG.abc123
DEBUG=false
APP_ENV=production
Your application loads this file on startup and the values become available as environment variables — the same way variables set in your shell or on your hosting platform are available. The key insight is that secrets live in the file, not in your code.
The Problem It Solves
Without a .env file, developers often do this:
# Hardcoded in source — terrible
import openai
openai.api_key = "sk-proj-abc123"
# In a config file — also bad
config = {
"db_password": "hunter2",
"stripe_key": "sk_live_xyz"
}
# Read from environment
import os
import openai
openai.api_key = os.getenv("OPENAI_API_KEY")
db_url = os.getenv("DATABASE_URL")
Hardcoded secrets end up in Git. Once they're in Git history, they're there permanently — even if you delete the file later. And if your repo is public, bots will find the key within seconds of the push.
How to Use .env Files by Language/Framework
Python
pip install python-dotenv
from dotenv import load_dotenv
import os
load_dotenv() # reads .env from current directory
api_key = os.getenv("OPENAI_API_KEY")
db_url = os.getenv("DATABASE_URL")
Node.js
npm install dotenv
// At the very top of your entry file
require('dotenv').config();
const apiKey = process.env.STRIPE_SECRET_KEY;
const dbUrl = process.env.DATABASE_URL;
Django
pip install django-environ
# settings.py
import environ
env = environ.Env()
environ.Env.read_env() # reads .env
SECRET_KEY = env('DJANGO_SECRET_KEY')
DATABASE_URL = env('DATABASE_URL')
PHP
composer require vlucas/phpdotenv
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
$dbPassword = $_ENV['DB_PASSWORD'];
$stripeKey = $_ENV['STRIPE_SECRET_KEY'];
Ruby on Rails
Rails loads .env automatically in development via the dotenv-rails gem:
# Gemfile
gem 'dotenv-rails', groups: [:development, :test]
The .gitignore Rule — Non-Negotiable
Your .env file must be in .gitignore. Add this before your first commit:
# .gitignore
.env
.env.*
.env.local
.env.*.local
!.env.example
The ! before .env.example means "exclude from the exclusion" — it allows the example file to be committed while blocking all real .env files.
Already committed .env by accident?
Adding to .gitignore after the fact stops future commits but does not remove the file from history. See How to Remove API Keys From Git History for the full fix. Revoke any exposed secrets immediately.
The .env.example Pattern
Always create a .env.example file alongside your .env. It shows other developers what variables are needed, without exposing real values:
# .env.example — safe to commit, no real secrets
DATABASE_URL=postgresql://user:password@localhost:5432/myapp_dev
STRIPE_SECRET_KEY=sk_live_your_key_here
OPENAI_API_KEY=sk-proj-your_key_here
AWS_ACCESS_KEY_ID=your_access_key_id
DEBUG=true
APP_ENV=development
When a new developer clones the repo they run cp .env.example .env and fill in their own values.
.env Best Practices
| Practice | Why |
|---|---|
Never commit .env | Secrets in Git are permanent — even deleted files remain in history |
Always create .env.example | Documents what variables are needed without exposing values |
| Use different values per environment | Dev, staging and production should have separate keys with separate permissions |
| Never log env var values | Logs containing secrets are a data breach waiting to happen — sanitize before sharing |
| Use a secrets manager in production | AWS Secrets Manager, Vercel env vars, Railway, Heroku config vars — not a .env file on the server |
| Validate on startup | Use env.required() patterns to fail fast if a required variable is missing, rather than failing silently later |
Production: Don't Use a .env File on the Server
A .env file is a development convenience. In production, use your platform's built-in environment variable management — it's more secure, auditable, and doesn't require deploying a secrets file:
If a Secret Ends Up in Your Logs
Environment variables can leak into application logs — database connection strings appear in ORM errors, API keys appear in failed request logs, and full .env contents can be printed by misconfigured debug modes. Before sharing any log with a colleague, StackOverflow or an AI assistant, run it through a sanitizer.
Sanitize Logs Before Sharing
Strip API keys, database URLs and credentials from logs in seconds. 100% client-side.
Open Log Sanitizer — Free →FAQ
What is a .env file? +
A plain text file containing environment variable declarations — key-value pairs like API_KEY=abc123. It keeps secrets out of source code so they are never committed to Git. Loaded at runtime by a library like python-dotenv or dotenv for Node.
Should I commit my .env file to Git? +
Never. Add .env to .gitignore before your first commit. If you accidentally commit it, see How to Remove API Keys From Git History. Commit a .env.example with placeholder values instead.
How do I load a .env file in Python? +
Install python-dotenv with pip, then call load_dotenv() at the top of your app. Access values with os.getenv('VARIABLE_NAME').
What is the difference between .env and .env.example? +
.env contains your real secrets and must never be committed. .env.example is a template with placeholder values — it is safe to commit and documents what variables other developers need to configure.
Can API keys in .env files end up in logs? +
Yes. ORM errors log database connection strings (which include passwords). Failed API requests log the URL with auth headers. Django debug mode can print full environment variables. Always sanitize logs before sharing them — use the Log Sanitizer before pasting logs into ChatGPT or StackOverflow.