What is a .env file? Format, examples, and keeping it private.
A .env file is a plain-text list of KEY=value settings that an application reads when it starts — above all its secrets: database passwords, API keys, signing keys. It keeps them out of the source code. It doesn’t keep them out of reach: if the file sits where a web server can serve it, anyone can download it.
- What is a .env file?
- Env file examples
- Syntax rules
- .env, .env.local, .env.production…
- Creating and opening one
- Is a .env file secure?
- How .env files end up public
What is a .env file?
A .env file (said “dot env”) is a text file of environment variables, one KEY=value per line, kept in a project’s root folder. When the app starts, a library reads the file and puts each value into the process environment, where the code reads it as process.env.KEY, os.getenv("KEY") or env('KEY').
The point is to keep configuration that changes between machines — your laptop, staging, the live server — out of the code. That idea comes from the Twelve-Factor App: store config in the environment. A .env file is the convenient way to fill that environment, especially in development.
The whole file name is .env: there is no name before the dot and no other extension. Because it starts with a dot, Linux and macOS hide it — ls -a shows it, and so does Cmd+Shift+. in a Finder window.
Env file examples
A typical file, with comments:
# Database
DB_HOST=127.0.0.1
DB_DATABASE=shop
DB_USERNAME=shop
DB_PASSWORD=change-me
# Services
MAIL_PASSWORD=change-me
STRIPE_SECRET=change-me
APP_KEY=change-me
Node.js
# .env
PORT=3000
DATABASE_URL=postgres://app:change-me@localhost:5432/app
SESSION_SECRET=change-me
# Node.js 20.6 and later read it without a package:
node --env-file=.env server.js
// Or with the dotenv package, as the first line of the app:
require('dotenv').config();
console.log(process.env.PORT); // "3000"
From Node 20.12 there is also process.loadEnvFile(), which loads .env from inside the code.
Python
# pip install python-dotenv
import os
from dotenv import load_dotenv
load_dotenv() # finds the nearest .env and loads it
db_url = os.getenv("DATABASE_URL")
Laravel and PHP
APP_NAME=Shop
APP_ENV=production
APP_KEY=base64:generated-by-php-artisan-key-generate
APP_DEBUG=false
APP_URL=https://shop.example.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=shop
DB_USERNAME=shop
DB_PASSWORD=change-me
Laravel loads it with the vlucas/phpdotenv library, which plain PHP projects can use too. Read values with env('DB_HOST') inside the files in config/ only: after php artisan config:cache the .env file is no longer loaded, and env() calls elsewhere stop seeing its values. What to do if a Laravel .env leaks: Laravel .env exposed.
Docker Compose
# .env, next to compose.yaml
APP_TAG=1.4.2
POSTGRES_PASSWORD=change-me
# compose.yaml
services:
app:
image: example/app:${APP_TAG}
env_file: .env
db:
image: postgres:17
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
Compose uses the file twice here: it fills in ${…} references inside compose.yaml, and env_file: passes every value in it into the app container. Keep the file out of the image itself with a .dockerignore.
.env.example
The one env file that belongs in git: a template listing every variable the app needs, with empty or placeholder values. A new developer copies it and fills it in.
# .env.example — copy to .env and fill in. No real values here.
DATABASE_URL=
SESSION_SECRET=
MAIL_PASSWORD=
cp .env.example .env
Syntax rules
# A comment line
APP_NAME=Shop # unquoted; most loaders drop the inline comment
GREETING="Hello, world" # quote values with spaces or a #
RAW='no $expansion here' # single quotes: taken literally
export LOG_LEVEL=info # "export" is accepted, so a shell can source the file
PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----"
- One
KEY=valueper line. Names are upper case with underscores by convention, and start with a letter. - No spaces around
=. Many loaders forgive them, but a shell that sources the file doesn’t:KEY = valueruns a command calledKEY. - Quotes. Single quotes are literal. Double quotes allow escapes such as
\nand, in some loaders,${OTHER_KEY}references to another variable. - Multi-line values in double quotes — a private key, say — work in the dotenv libraries for Node, Python and PHP. Check yours before relying on it.
- Everything is a string.
PORT=3000arrives as"3000", andDEBUG=falseas the string"false", which JavaScript treats as true. Convert values in code. - The real environment usually wins. In dotenv for Node, python-dotenv, phpdotenv and
node --env-file, a variable already set in the environment is not overwritten by the file.
.env, .env.local, .env.production: which file is which
| File | What it’s for | Commit to git? |
|---|---|---|
.env | The main file. In Laravel and most back ends it holds the real secrets; Next.js and Vite also use it for shared, non-secret defaults. | Only if it holds no secrets |
.env.local | Overrides for your own machine. | No |
.env.development, .env.production, .env.test | Values for one environment, picked by the framework’s mode. | Only if they hold no secrets |
.env.production.local and similar | Local overrides for one environment. | No |
.env.example | A template with placeholder values. | Yes |
Which file overrides which differs between frameworks — Next.js and Vite, for instance, rank .env.local and .env.production differently — so check your framework’s documentation. Copies such as .env.bak, .env.save and .env.old are none of these: no framework reads them, and they are a common way secrets leak.
Creating and opening a .env file
- Terminal:
cp .env.example .envif the project has a template, otherwisetouch .env, then edit it like any text file. - VS Code and other editors: create a new file named
.envin the project root. - Windows Notepad: in Save As, set “Save as type” to “All files” and the name to
.env, or Notepad saves it as.env.txt. - Hosting file managers often hide dotfiles; look for a “show hidden files” option.
Is a .env file secure?
No more than any other text file. It isn’t encrypted; it keeps secrets out of the code, not out of reach. What protects it is where it lives and who can read it:
- Keep it outside the web root: point the document root at the app’s public folder, with
.envone level above. - Make it readable only by the user the app runs as, for example
chmod 600 .envwith that user as owner. - List it in
.gitignoreand.dockerignore. If it was ever committed,git rm --cached .envstops tracking it, but history keeps the old copy — rotate what was in it. - Block dotfiles and backup copies in the web server anyway: tested rules for nginx, Apache and Caddy.
- In production, prefer the platform’s environment variables or a secret manager to a file. To ship config with the code, encrypt it:
php artisan env:encryptin Laravel, or tools such as SOPS.
How .env files end up public
- The project folder is the web root. The web server then serves everything in it,
.envincluded — neither nginx nor Apache hides it by default. On Laravel: fix the document root. - Backup and editor copies —
.env.bak,.env.save,.env.old— left next to the original and sent as plain text. - Committed to git, then pushed to a public repository or deployed with an exposed .git directory.
- Copied into a build — a front-end bundle (see public env variables), or a Docker image built with
COPY . .and no.dockerignore.
Whichever way it got out, one file holds every key at once. If yours is exposed, here’s what to do now.
Check yours
DotenvScan requests /.env, its .production, .local and backup variants, and .env files in /api/ and /backend/, and tells you whether any of them downloads. To inventory every .env and config file across a whole Linux server, use ServerSecretVault’s scan.