Env files and secrets in local development

Environment variables are how a program receives settings such as ports, URLs, and credentials, and a .env file is a plain text file that a loader copies into that environment at startup. Commit a .env.example that lists every key with a safe value, keep the real .env out of Git, and check the two for drift. A .env file is not a secret manager: it is plain text, it has no audit trail, and it does nothing for a process that is already running. Treat a leaked secret as exposed and rotate it.

Updated

How the pieces fit

An environment variable is a named string that a process receives from the process that started it. It is a copy, so changing it later does not reach a process that is already running. A .env file is a plain text list of KEY=value lines that a library or tool loads into that environment at startup. There is no .env standard. Each loader decides what quotes, comments, and $ references mean, so one file can give different values in Node.js, python-dotenv, and Docker Compose.

A working setup

Three habits cover most teams. Commit a .env.example that lists every variable the code reads, with safe local values and empty secrets. Keep real env files out of Git with an ignore rule that does not hide the example. Check for drift, because the example and the real file move apart as keys are added. The .env and .env.example checklist covers ignore rules that work, onboarding a teammate, and what to do in order when a secret leaks. The env file diff checker compares two pasted files for missing, extra, duplicate, empty, and malformed keys, by name and emptiness only, in your browser.

Reading order

  1. Read the environment variable and .env file entries for the vocabulary.
  2. Work through the checklist, then run the diff checker on your own files.
  3. If an editor or agent config holds a token, read MCP config file locations, because those files are credentials too.

Where a .env file stops being enough

It is plain text that anything running as your user can read. It has no audit trail and no revocation, so you cannot see who holds a copy. Every value is text, and a typo in a name stays silent until the code reads it. For anything shared, audited, or rotated on a schedule, use the secret store your platform or team already runs. Deleting a leaked file does not rotate the key: a later commit that untracks .env leaves the earlier value readable in history.

How Rig handles env files

Rig keeps env profiles as plain files in <project>/.rig/env/, adds an ignore rule for that folder, and masks secret-looking values in its interface. Its env_diff MCP tool compares key names only, and list_env_profiles masks secret-named values, so an agent can see which keys are missing without reading a secret. Profiles are local only. Syncing secrets is a separate step that encrypts them locally and uploads ciphertext. Masked env manager, Secrets vault basics, and Env profiles: client vs personal explain the details.

Limits

The diff checker cannot tell you that a value is correct, current, or a placeholder. The loader comparison in the checklist covers only Node.js 22.22.0, python-dotenv 1.2.3, and Docker Compose v5.6.0, so test your own loader with a file that contains # and $.

Tools in this topic

Guides in this topic

Glossary terms in this topic

Knowledge base articles in this topic

Blog posts in this topic

Frequently asked questions

Which env files belong in Git?

Commit .env.example, which holds every key name, safe local values, empty secrets, and a comment per key. Keep the real files out of Git: .env, .env.local, .env.production, and any copy. The ignore rule must not hide the example, because a pattern such as .env.* also matches .env.example unless you re-include it with a line that starts with an exclamation mark.

Why did my change to .env not reach the running app?

A loader reads the file once, at startup, and a process receives a copy of its environment when it starts. Editing the file changes nothing for a process that is already running, so restart it. Check your shell too: in a local test, Node.js and python-dotenv both kept a value already exported in the shell over the file’s value, while Docker Compose used the file’s.

What should I do if I committed a secret?

Treat it as exposed. Issue a new credential, deploy it, then revoke the old one, unless you can see the old one being misused, in which case revoke it first. Read the provider’s access log for use between exposure and revocation, then stop tracking the file and add the ignore rule. Removing the file or rewriting history does not make the old value safe, because copies may exist. Rotation does.

Does one .env file mean the same thing to every tool?

No, because there is no .env standard. In a local run, Node.js 22.22.0 with --env-file left ${HOST_NAME} as literal text in a value, while python-dotenv 1.2.3 expanded it, and Node.js cut an unquoted value at a # sign. Quote any value that contains #, and do not rely on ${NAME} expansion unless every tool that reads the file supports it.

See how Rig fits in

Rig is a Mac app that starts, stops, and watches the processes in a project from one window. The knowledge base explains each part in plain language.

Related

Section links

https://userig.app/learn/env-files-and-secrets