Learning objectives
After reading this article you will be able to:
- Write a .env file with the correct syntax
- Load a .env file in Node.js and Python
- Keep the file out of Git and configure values safely in production
Every project has values that do not belong in the code: a bot token, a database password, an API key. Typing them into the terminal on every run gets old, and hard-coding them is dangerous. The .env file is the common answer: a text file next to your project, read at startup.
In practice, it is only a way to fill in environment variables during development. The operating system does not read the file. The runtime (Node.js) or a library (python-dotenv) does, and copies the pairs into the process.
What is the syntax of a .env file?#
One variable per line, as NAME=value. There is no single official standard, but these rules hold in both Node.js and python-dotenv:
DEBUG=false # comment
DISCORD_TOKEN=paste_the_token_here
PORT=3000
DATABASE_URL="postgresql://user:password@localhost:5432/mydb" # trailing comment
PHRASE='a # inside quotes does not start a comment'
PRIVATE_KEY="line 1
line 2"
export NODE_ENV=development- Names use letters, digits and underscores, must not start with a digit, and are usually UPPERCASE. In Node.js,
my-varand1_VARare invalid. - Quotes are optional, but needed when the value contains
#or spans several lines. Quoted values can run over multiple lines. - Everything is a string.
PORT=3000arrives as"3000"andDEBUG=falsearrives as"false", which is truthy in JavaScript and Python. Convert in your code. exportat the start of a line is accepted and ignored, so the same file can be loaded withsource .envin a shell.
Watch out for $ in values too. Next.js expands $OTHER_VAR inside .env files and needs \$ for a literal dollar sign. python-dotenv only expands the ${VAR} form.
How do you load a .env file in Node.js?#
Recent Node.js versions have built-in support, no package needed, and the --env-file flag is no longer experimental:
node --env-file=.env index.js// index.js
const token = process.env.DISCORD_TOKEN;
if (!token) throw new Error("DISCORD_TOKEN is not set");
const port = Number(process.env.PORT ?? 3000);
console.log(`Starting on port ${port}`);If the file does not exist, --env-file throws an error. Use --env-file-if-exists=.env, also available in recent Node.js versions, when the file is optional, for example in production. You can also load it from code with process.loadEnvFile('.env'), and util.parseEnv() only parses text without touching process.env.
You can pass several files. Later files override earlier ones, but a variable already set in the environment always beats the file:
node --env-file=.env --env-file=.env.local index.jsHow do you load a .env file in Python?#
Use the python-dotenv package:
pip install python-dotenvimport os
from dotenv import load_dotenv
load_dotenv() # looks for .env in the script's folder or above
token = os.environ["DISCORD_TOKEN"] # KeyError if missing
port = int(os.getenv("PORT", "3000"))
print(f"Starting on port {port}")By default load_dotenv() does not replace variables that already exist in the environment; load_dotenv(override=True) does the opposite. To read values without changing os.environ, use dotenv_values(".env"), which returns a dictionary.
Other frameworks may read the file on their own. Next.js loads .env, .env.local and per-NODE_ENV variants, and only exposes variables prefixed with NEXT_PUBLIC_ to the browser, baked into the JavaScript at build time. In Docker Compose, a service's env_file key loads the file's variables into the container's environment.
How do you keep .env secrets from leaking?#
The real risk of a .env file is ending up in the repository. Do this before your first commit:
- Create a
.gitignorewith the line.env(and.env.*if you use variants, allowing only.env.example). - Commit a
.env.examplewith names and fake values, so people who clone the repo know what to fill in:
DISCORD_TOKEN=
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
PORT=3000- Check with
git statusthat.envdoes not show up as a new file. - Never print values in logs or paste them into error screenshots.
If a secret was already committed, deleting the file does not help, because history keeps the content. Generate a new key and revoke the old one. The steps are in API key exposed on GitHub, and for bots see the Discord bot token.
What are the common .env mistakes?#
The variable is undefined. The file was not loaded (missing --env-file or load_dotenv()), it sits in another folder (the --env-file path is relative to the current directory, not the script), or the name has a typo.
I changed the value and nothing changed. The process reads the file only at startup: restart it. If the variable also exists in the environment, it beats the file. Run echo $DISCORD_TOKEN (or $env:DISCORD_TOKEN in PowerShell) to check.
The value was cut off at #. Wrap the value in quotes.
PORT does not work as a number. It is a string. Convert with Number() or int().
A front-end variable shows up empty. In frameworks like Next.js, only variables prefixed with NEXT_PUBLIC_ reach the browser, and the value is fixed at build time. Never use that prefix for secrets.
How to do this on Square Cloud#
On Square Cloud, environment variables are configured per application, in the dashboard, through the API, the CLI or the VS Code extension. Your code keeps reading process.env or os.environ as usual.
If you already have a .env ready, the dashboard imports the file and fills in the variables. You can also export everything back as KEY=VALUE. A change takes effect on the application's next restart, with no rebuild. Inside the application, the SQUARECLOUD_APP_ID variable gives you its ID at runtime.
That way the .env stays on your computer, listed in .gitignore, and production values are configured on the application itself. See the details in environment variables on Square Cloud.
Deploy
Push from GitHub, the CLI or a ZIP and go live in seconds
FAQs
Does the file have to be named exactly .env?
No. The name .env is a convention. Node.js accepts any path in --env-file=file and python-dotenv accepts any path in load_dotenv('file'). Frameworks such as Next.js look for specific names, like .env.local.
Can I commit my .env file to Git?
Not if it holds secrets. Add .env to .gitignore and commit only a .env.example with variable names and fake values. If a secret was already committed, rotate the key: deleting the file later does not remove it from history.
Does .env override a variable that already exists in the system?
By default, no. With Node.js --env-file and python-dotenv load_dotenv(), the value already in the environment wins over the file. In python-dotenv, override=True flips that.
Do I need a .env file in production?
Not necessarily. In production the platform usually has its own environment variables setting, and your code reads the same process.env or os.environ. The .env file is mostly a convenience for local development.
More in Deploy
See all 33 articles- What are environment variables?Environment variables are key-value pairs the operating system hands to a process at startup. Learn to read them in Node.js and Python and keep tokens out of code.
- What is a deployment?A deployment takes a version of your code to a server, prepares it and starts it. See the stages, the methods and how to roll back when it goes wrong.
- How much RAM and CPU does my app need?Size RAM and CPU by measuring your app's real peak and adding about 30% headroom. Learn how to measure in Node.js, Python and Java and what to do if short.
- Automatic deploys with GitHub ActionsAutomatic deploys with GitHub Actions ship your code to the server on every push. See the workflow step by step, secrets, tests before deploy and common errors.