Learning objectives
After reading this article you will be able to:
- Understand what environment variables are and where they come from
- Read variables in Node.js and Python with defaults and validation
- Keep config and secrets out of code using .env without leaking tokens
Every application needs values that change depending on where it runs: the database address, the port, a bot token, a payment API key. Writing those values straight into the code works on your laptop and becomes a problem at your first deploy.
Environment variables fix that. The code asks the system "what is the value of DATABASE_URL?" and each environment answers with its own. The code stays the same, the configuration changes. It is one of the first concepts to learn before any deploy.
What is an environment variable?#
It is a name-value pair that belongs to a process's environment. When the operating system starts a program, it hands the program a copy of that set. The program can read it at any time, and any child process it starts inherits the same copy.
You already use some without noticing: PATH tells the system where to find executables and HOME points to the user's folder. Yours are the same kind. On Linux/macOS, the first example sets the variable only for that one command; in PowerShell, the second one keeps it set until you close the terminal:
PORT=3000 node index.js$env:PORT = "3000"; node index.jsTwo rules hold in every language. First, every value is text: 3000 arrives as "3000" and false arrives as "false" (which is truthy in many languages). Second, by convention names do not start with a digit and use UPPERCASE with underscores, like API_KEY.
How do you read variables in code?#
In Node.js they live in process.env. In Python, os.environ raises KeyError when a key is missing, while os.getenv returns a default.
// Node.js
const port = Number(process.env.PORT ?? 3000);
const token = process.env.DISCORD_TOKEN;
if (!token) {
throw new Error("Set DISCORD_TOKEN before starting");
}import os
port = int(os.getenv("PORT", "8000"))
token = os.environ["DISCORD_TOKEN"] # KeyError if missingUse a default for what is optional (the port) and fail early for what is required (the token). A clear error at startup costs less than an undefined showing up in production hours later.
Why not put configuration in the code?#
The Twelve-Factor App guide, a classic reference for cloud apps, defines config as everything that varies between deploys: credentials, service addresses, hostnames. Its test is simple: if you could open-source the codebase right now without exposing any credential, config is properly separated.
There are three practical gains:
- Security. A token in code goes into Git, and the history keeps it forever. A Discord bot token or API key pushed to a public repository is visible to anyone, so treat it as compromised.
- One codebase, many environments. The same build runs in development, staging and production, swapping only the variables.
- Changes without code edits. Rotating a password becomes changing a value and restarting, with no new commit.
What is a .env file and how do you use it?#
Typing ten variables by hand on every run gets tiring. A .env file holds one NAME=value per line and is meant for development only:
DISCORD_TOKEN=paste_token_here
DATABASE_URL=postgres://user:password@localhost:5432/app
PORT=3000Node.js reads this format with no library through the --env-file flag:
node --env-file=.env index.jsIf a variable already exists in the environment, it wins over the file's value. In Python, the python-dotenv package does the same with load_dotenv().
The critical point: add .env to .gitignore. For people cloning the project, commit a .env.example with the names and fake values so everyone knows what to set.
How do you handle secrets?#
Environment variables beat hardcoded values, but they are not a vault. A few precautions:
- Do not print values in logs or error messages.
- Give each service only the credentials it needs.
- If a token leaks, revoke it and issue a new one. Deleting the commit is not enough, since the value may already have been copied.
- Never put secrets in variables read by the browser. In front-end frameworks, prefixes like
NEXT_PUBLIC_orVITE_expose the value in the JavaScript sent to visitors: use them only for public data.
When are environment variables not the best option?#
For long, structured configuration such as rule lists or texts, a versioned config file is more readable. For secrets that rotate constantly or need access auditing, a dedicated secrets vault makes more sense. For the vast majority of bots and APIs, though, environment variables are enough.
How to do this on Square Cloud#
On Square Cloud, the .env you use in development becomes your production configuration without copying values one by one: the dashboard imports the file and creates the application's variables, and exports everything back as KEY=VALUE when you need it. You can also set each variable with the CLI (app env set KEY=VALUE), through the API or with the VS Code extension.
Two details follow the rules in this guide. Each application has its own set of variables and cannot read another's, even in the same account, so your bot never sees your API's credentials. And a change takes effect on the next restart, with no need to upload the code again: rotating a password means changing the value and restarting. Variables already exist while dependencies install, and SQUARECLOUD_APP_ID gives you the application's own ID at runtime.
See the walkthrough on the environment variables page and the CLI commands.
Deploy
Push from GitHub, the CLI or a ZIP and go live in seconds
FAQs
Is an environment variable the same as a .env file?
No. An environment variable belongs to the running process. A .env file is just a convenient way to fill them in during development: a library, or Node.js itself, reads the file and sets the variables on the process.
Are environment variables safe for storing passwords?
They are much better than hardcoding a password, because they never land in your repository. Anyone with access to the process or the server dashboard can still read them, so limit access and never print values in logs.
Why does process.env.PORT come back as a string instead of a number?
Every environment variable is a string. If you need a number or boolean, convert it in code, for example Number(process.env.PORT) or int(os.getenv('PORT')).
Do I need to restart my app after changing a variable?
Yes. A process receives its variables when it starts and does not reread them on its own. Restart the app for the new value to take effect.
More in Deploy
See all 33 articles- 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.
- How much does it cost to host a Node.js app?Hosting a Node.js app costs from nothing, with limits, to a few dollars a month. See what drives the price, how much RAM your app uses and how each option charges.