Environment variables are how configuration reaches your application. They also determine what happens to a secret the moment you leak one, so this page covers both.
Setting them on the service
Environment variables live on the Startup tab of your service, alongside the startup command. Define them there rather than in a file, and they are available to your process automatically.
NODE_ENV=production
PORT=3000
DATABASE_URL=postgres://user:pass@host:5432/db
DISCORD_TOKEN=...
Read them with your runtime's convention:
const token = process.env.DISCORD_TOKEN; // Node.js
token = os.environ["DISCORD_TOKEN"] # Python
token := os.Getenv("DISCORD_TOKEN") # Go
Always provide a fallback or validate at startup. A missing variable should produce a clear error at boot, not an undefined discovered mid-request.
The difference that catches everyone
In shell syntax export DISCORD_TOKEN=... sets the variable in the shell. A startup command is not a shell script — it is a single command run directly. Writing export there produces either an error or a command that does not do what you meant.
Put the assignment in the variables field, and leave the startup command as just the command:
# Wrong — the startup command is not a shell
export DISCORD_TOKEN=abc123 && node index.js
# Right — DISCORD_TOKEN is defined as a variable on the service
node index.js
If you genuinely need chaining inside the startup command, that is a shell — write it into a script file and execute that instead, which is far easier to read than a one-line chain.
Never commit secrets
- Not in the repository. Add
.envto.gitignoreand keep a committed.env.examplelisting variable names with empty values. - Not in the image. Anything in a layer is permanent and readable by anyone who pulls the image, including layers a later stage appears to have overwritten. Declare variables in the image; supply values on the service.
- Not in the console. Printing a token to the log writes it to somewhere far more people can read than the code does.
Rotating a leaked secret
If a token is exposed, revoking and reissuing it is the only real remedy — cleaning it out of the code does not un-expose it. Rotate the credential at its provider, update the variable on the service, and restart.
Order matters: issue the new credential first, then update and restart. Revoking first causes downtime between the revocation and the restart, and if the restart fails you have an outage instead of a rotation.
Defaults and validation
Parse and validate once at startup, then pass the parsed value through your app. Reading process.env throughout the codebase makes every read site a place the variable could be wrong or missing.
const config = {
token: requireEnv("DISCORD_TOKEN"),
port: Number(requireEnv("PORT")),
};
function requireEnv(name) {
const value = process.env[name];
if (!value) {
throw new Error(`Missing required environment variable: ${name}`);
}
return value;
}
Failing at boot with one clear message beats starting successfully and erroring on every request.