Project Files

Written & tested for accuracy by a developer (not AI) · Updated November 2026

What Is a .env File?

What is a .env file. Explains the hidden file that keeps API keys and settings out of project code. Why Git ignores it. How to use the .env file on a Mac.

A .env file holds API keys, passwords, and settings that a project needs, in a central file, apart from project code, to help keep credentials private. You'll find the file, or a similar .env.example file, in many projects.

Experienced developers use a .env file to separate secrets from code. With a secrets file, code can be shared, pushed to GitHub, or handed to an AI agent, while the real keys stay on the developer's Mac. Projects often contain a corresponding .env.example to show the keys the project needs, without the real credentials. That way you can share a project without giving away a key that someone else could use to run up the bill with paid services.

What's in a Project Folder? explains the other files and folders you'll find beside a .env file in a project. Keeping secrets out of your code is one step in the full Mac setup roadmap.

Security limits

The .env file is plain text on your Mac. You can tell Git to ignore it, but any program you run can read it. Anyone who signs in to your Mac account can read it too. A .env file is fine for credentials such as API keys that you want to keep from strangers, as long as only you, or people you trust, use your Mac. If other people have accounts on your Mac, run chmod 600 .env so that only your account can read the file.

Use the macOS keychain for secrets that need more protection, such as banking passwords. A terminal window can read an API key from the keychain instead of a file. Here's an example. Add the key once with this command, which asks you to type the key:

$ security add-generic-password -a "$USER" -s weather -w

Then add this line to ~/.zshrc:

export WEATHER_API_KEY=$(security find-generic-password -s weather -w)

With this approach, each new terminal window reads the key from the keychain and sets it as an ordinary environment variable. The key is never saved in a plain-text file, so it can't be committed to Git or found by a program that searches your files. However, it isn't hidden from the programs you run. They can read the variable, or run the same security command without a password prompt.

You trade convenience for security. The .env file provides a good balance of convenience and security for low-risk credentials, such as an API key with a spending limit. For higher-risk credentials, reading them from the macOS keychain is safer, though less convenient. Ultimately, keep the highest-risk credentials, such as a cryptocurrency wallet's private keys or recovery phrase, off your computer entirely. Never use them programmatically.

What goes in a .env file

The filename is a dot followed by "env", short for environment. The file should have one NAME=value setting on each line, and lines that start with # are comments. Here's an example, with made-up values:

# Keys for this project (never commit this file)
WEATHER_API_KEY=your-key-here
DATABASE_URL=postgres://localhost/myapp
DEBUG=true

To read values from a .env file, a program needs dedicated code to read environment variables from a file, or a software package library called a "loader," to read the file when the program starts. The loader turns each line into an environment variable, a named value that the program can look up. Common loaders include dotenv for JavaScript, python-dotenv for Python, Node's --env-file option, frameworks such as Next.js and Vite, and the mise version manager (Mise Configuration shows how). The format has no formal standard, so loaders differ in small ways, such as how they treat quotes.

The idea is to write program code that can be shared between your Mac and a live server without including hard-coded credentials in the program code.

Shell configuration overrides a .env file

An environment variable that is set in your shell takes priority over the same named variable in a .env file. If you change a value in .env and nothing happens, check whether you already set that name in a shell configuration file or with a typed command.

Add .env files to .gitignore

Git is the save-as-you-go version control system that most developers use. Any snapshot of code committed to Git gets saved in Git history within the project. If you use GitHub for collaboration and offsite storage, the project's code and history will get periodically pushed to a GitHub remote repository. You must be careful to never allow secrets to be stored in Git or pushed to GitHub.

A .env file should never go into Git. Add a line in the project's .gitignore to keep it out. The article What Is a .gitignore File? shows an example, containing these three lines:

.env
.env.*
!.env.example

The first two lines exclude .env and every file whose name starts with .env. from Git. The ! line allows .env.example, so an example file is committed.

If you push a .env file by mistake, anyone who reads the repository can find your credentials. Deleting the file doesn't remove it from the project's history after a commit. If your Git repository contains credentials, you should log in to each service that issued a key, revoke the key, and make a new one.

GitHub has a push protection feature (on by default) which blocks pushes to public repositories that contain secrets it recognizes, but it doesn't know every kind of key, so set up .gitignore and don't count on GitHub's filtering.

.env.example and other .env files

Projects often have more than one related .env file. Here's what each name means:

  • .env.example or .env.sample - an example with the names of the settings and blank or made-up values. It tells you, or an AI agent, which keys to supply.
  • .env.local - your own Mac's settings, which override .env. It should never be committed.
  • .env.development, .env.production, and .env.test - settings for different environments, used by a framework such as Next.js or Vite.

When a project has a .env.example, copy it to .env and fill in your own values. Use the cp command to copy and rename a file:

$ cp .env.example .env

How to create a .env file

Use a code editor to add .env variables, since a .env file has to be plain text.

You can't create a .env file in the Finder. If you rename a file to .env, the Finder shows a message that names beginning with a dot "are reserved for the system". The file keeps its old name.

Use a terminal application instead. Open a terminal application, go to your project's top folder with cd, and run the touch command, which creates an empty file:

$ touch .env

The ls command won't list the new file, because its name starts with a dot. Use ls -a to see it:

$ ls -a

To see it in the Finder, press Command + Shift + . (period) in the project's Finder window. The .env file appears, dimmed. The article Show Hidden Files on Mac describes other convenient ways to see hidden files.

Differences between a project's .env and home ~/.env

If you used one of my "Set Up Your Mac" apps, the app set up example shell configuration files in your home directory.

Look for ~/.env in your home directory. With my "Set Up Your Mac" shell configuration files, the ~/.zshrc file loads ~/.env each time you open a terminal window ("Set Up Your Mac" explains how).

You can have a ~/.env file in your home directory and a .env inside a project folder.

  • ~/.env - contains environment variables for every program you start from a terminal window. It is loaded by your shell configuration file, ~/.zshrc (the article .zshrc or .zprofile explains what that file does).
  • A project's .env - is only read by a project's code that reads environment variables from a file.

I recommend keeping API keys out of a project folder. Put them in the home directory ~/.env instead, as my starter projects show (Starter Projects). That way, they can be shared among projects. They also stay out of a project's Git repository and off GitHub (most people never save an entire home directory to Git).

Your ~/.zshrc configuration can load ~/.env into the shell, so a key set there is already an environment variable when a project starts. Most loaders leave an existing variable alone, so the ~/.env value takes precedence over the same name in a project's .env.

AI agents and .env files

A .gitignore file keeps a .env out of Git, but an AI agent working in your project can still read it. Anthropic Claude skips files that Git ignores when it searches, but it can open a file by its path. To stop it reading .env files, Anthropic's documentation gives this example for Claude's settings file, ~/.claude/settings.json:

{
  "permissions": {
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)"
    ]
  }
}

The same documentation warns that these rules don't stop a script that opens the file itself, and that only Claude's sandbox blocks that. ChatGPT Codex's permission settings can also deny reads of files that match a pattern. To stop an AI agent changing a .env file, a hook can block the edit (see the article Hooks for Claude Code and Codex).

A .env.example file helps an AI agent. The AI agent can learn which keys a project needs without seeing a real one.

An additional note of caution: Never paste a key into an AI chat because it gets saved to a local chat transcript and sent to the AI company's servers.

Continue setting up your Mac

Don't miss the full visual roadmap and checklist that shows how to set up a Mac for software development, with all the essential tools and settings you might not yet know about.