
TL;DR
|
A .gitignore file is a plain text file that tells Git which files and folders to leave out of your repository. You list what you want ignored, one pattern per line, and Git stops offering those files when you stage and commit your work.
If you have ever opened a project and found a file called .gitignore sitting next to the code, this guide explains what it is doing there. It covers how the file works, what usually goes in it, the pattern rules, and how to fix the most common problem: a file that Git keeps tracking even after you ignored it.
Key takeaway: Git reads .gitignore before it decides which new files to show you, and skips anything that matches a pattern in it.
Git is the tool most developers use to track changes to their code. It watches a project folder and notices every new or changed file. Without instructions, it would offer to save everything, including passwords, downloaded libraries, and temporary files your computer creates on its own.
A .gitignore file is that set of instructions. It usually sits in the top folder of a project, and each line describes files to skip. When you run git status to see what has changed, or git add to prepare files for a commit, Git checks each new file against those lines. Matching files stay out of sight.
The file only affects files Git is not tracking yet. Git's documentation describes its purpose as keeping untracked files untracked, so a file that was already committed stays tracked until you untrack it (see common mistakes below). A few other details shape how it behaves:
Key takeaway: ignore anything that is secret, anything that can be rebuilt, and anything specific to one person's machine.
Most entries fall into five groups:
| What to ignore | Why | Example entries |
|---|---|---|
| Secrets and local settings | Passwords and API keys in a repository can be read by anyone with access to it | .env, .env.local, *.pem |
| Installed dependencies | Downloaded libraries are large and can be reinstalled from a lock file | node_modules/, vendor/, .venv/ |
| Build output | Generated files are recreated every time the project is built | dist/, build/, *.pyc |
| Logs and temporary files | They change constantly and are only useful on the machine that made them | *.log, tmp/ |
| Editor and system clutter | Files your editor or operating system creates on its own | .DS_Store, .idea/, .vscode/ |
Here is a short .gitignore for a typical web project. Lines that start with # are comments, which Git skips.
# Secrets and local settings
.env
.env.local
# Dependencies
node_modules/
vendor/
# Build output
dist/
# Logs
*.log
# Editor and operating system files
.DS_Store
.idea/
.vscode/You rarely need to write one from scratch. GitHub maintains a public collection of .gitignore templates for most languages and frameworks, and many project starters create one for you.
Key takeaway: yes. Ignore your .env file and commit a .env.example with placeholder values so teammates know which settings the project needs.
A .env file holds settings your app reads when it starts, often including database passwords and API keys. (Our guide What is .env? covers it in full.) Because those values are secret, the .env file is usually the first line in any .gitignore.
A new teammate who clones the project gets no .env file, so they cannot tell which settings the app expects. The common fix is a second file, .env.example, that lists the same names with dummy values. It is safe to commit because nothing in it is real. Each developer copies it to .env and fills in their own values.
# .env.example
DATABASE_URL=your-database-url-here
PAYMENT_API_KEY=your-key-hereIgnoring .env protects you from now on. It does not undo the past: if a secret was ever committed, it stays in the project's history. The safe response is to replace that password or key with a new one, then remove the file from tracking.
Key takeaway: no. .gitignore keeps files out of Git, not out of reach. AI coding agents on your machine can read anything your user account can read.
An AI coding agent such as Cursor, Claude Code, or Codex works inside your project folder, with the same access to files that you have. .gitignore tells Git what to leave out of commits. It doesn't hide files from anything else running on your computer, so an agent can open your .env file, print its values, or paste them into a chat.
Most tools give you a separate way to keep agents away from sensitive files:
Treat these as a safety net, not a guarantee. The safest setup is to keep only development or test keys in your local .env file, and keep production secrets out of your machine entirely.
Key takeaway: a handful of symbols covers almost every case. A trailing slash means folders only, a star matches any characters, and an exclamation mark makes an exception.
| Pattern | What it ignores | Example match |
|---|---|---|
| .env | Any file or folder named .env, in any folder | .env, api/.env |
| /.env | Only the .env in the same folder as the .gitignore | .env, but not api/.env |
| logs/ | Folders named logs, and everything inside them | logs/today.log |
| *.log | Any file ending in .log | error.log, app/debug.log |
| **/temp | A file or folder named temp at any depth | temp, src/old/temp |
| docs/**/*.pdf | PDF files anywhere under docs | docs/guides/setup.pdf |
| !keep.log | An exception: do not ignore this file, even if an earlier line did | keep.log stays tracked |
| # note | A comment, which Git skips | Nothing |
Two rules trip people up most often. First, when two lines disagree, the later one wins, so put exceptions after the rule they override. Second, you cannot make an exception for a file inside a folder you have ignored entirely, because Git never looks inside that folder. Ignore the folder's contents (logs/*) instead of the folder itself if you need exceptions inside it.
Key takeaway: if Git still tracks a file you ignored, it was committed first. Untrack it with git rm --cached and commit the change.
This is usually why a .gitignore seems to be broken. Git only ignores files it is not already tracking. To stop tracking a file but keep your local copy, run:
git rm --cached .env
git commit -m "Stop tracking .env"For a whole folder, add -r, as in git rm -r --cached node_modules. Your files stay on your computer. They disappear from the repository from the next commit onward, though they remain in its history.
To see which line, in which file, is ignoring a path, run git check-ignore -v followed by the path. If nothing prints, no rule matches it.
There is one catch: by default, the command skips files Git already tracks, so it prints nothing for them even when a rule matches. Add --no-index to check one of those:
git check-ignore -v --no-index .env
The file must be called exactly .gitignore, with the leading dot and no extension. Notepad on Windows, for example, can save it as .gitignore.txt. Patterns are also read relative to the folder the .gitignore sits in.
Files only your setup creates, such as one editor's settings folder, are better kept in your global ignore file. That keeps the shared .gitignore focused on what matters to the whole project.
Key takeaway: every language leaves its own trail of installed packages and build files. Ignore those, and keep the files that describe what to install.
The secrets and editor clutter from earlier apply to every project. What else to ignore depends on the language, because each one installs packages and builds files in its own folders:
Language | Add to .gitignore | Keep in Git |
| PHP | vendor/ | composer.json and composer.lock |
| Node.js | node_modules/, plus build folders such as dist/ or .next/ for Next.js | package.json and your lock file, such as package-lock.json |
| Python | __pycache__/, *.pyc, and virtual environment folders such as .venv/ | pyproject.toml and your lock file, such as uv.lock |
| Ruby | .bundle/ and vendor/bundle/, plus log/ and tmp/ in Rails apps | Gemfile and Gemfile.lock |
| Java | target/ for Maven, or build/ and .gradle/ for Gradle | pom.xml or build.gradle |
| .NET | bin/ and obj/ | Project files such as .csproj |
The pattern is the same in every row. Ignore what can be rebuilt, and commit the files that describe how to rebuild it, including the lock file. Our guide What is a lock file? explains why the lock file belongs in Git.
Many frameworks create a starter .gitignore for you when you start a new project, Laravel and Rails among them, so check what's already there before adding lines. For anything missing, GitHub's collection of .gitignore templates has a ready-made file for most languages and frameworks.
Key takeaway: on Upsun Cloud, secrets live in project or environment-level variables, not in files, so your .env can stay ignored and your app still gets the values it needs.
Upsun Cloud builds and deploys your app from your Git repository, so an ignored .env file never reaches it.
The real passwords come from variables you set in Upsun Cloud itself, through the Console or the command line. Two levels matter here:
A few options make this work in practice. Marking a variable as sensitive hides its value in the Console and in command line output, while your app can still read it. A production-only secret can be set so that other environments do not inherit it. And giving a variable the env: prefix exposes it to your app as an ordinary environment variable, so code that already reads its settings from the environment needs no changes.
For example, this command stores a production-only payment key that stays hidden and is not passed down to other environments:
upsun variable:create --level environment -e main --name env:PAYMENT_API_KEY --value your-key-here --sensitive true --inheritable false
Upsun's documentation also points out that values you set in the project's own configuration file are committed to Git, which makes them a poor fit for secrets. Keep those for settings that are safe to share. So .gitignore keeps secrets out of your code, and Upsun Cloud variables deliver them to the running app. See setting variables in the Upsun docs for every option.
Upsun Cloud does not read .env files at all. It does read a different file, .environment, from your app's root folder. This file runs as a script when your app's container starts, and it can set environment variables for the app. Projects set up with upsun project:init may already have one, for example to pass database details to the names a framework expects.
Despite the similar name, .environment must be committed so Upsun Cloud can use it. Check that no pattern in your .gitignore, such as .env*, catches it by accident. Because it is committed, keep secrets out of it too: it should point to variables, not contain passwords.
Upsun Cloud builds every environment from your Git repository and keeps secrets in variables, so a clean .gitignore is all your repository needs. You can deploy on Upsun Cloud free for 15 days, with no card needed, or see how Git-driven automation works.
It tells Git which files and folders to leave out of a repository. Teams use it to keep secrets, installed libraries, build output, and machine-specific files out of shared code.
Usually in the top folder of the project, next to the hidden .git folder. You can add more .gitignore files in subfolders, and each one applies to its own folder and everything below it.
Yes. Committing it means everyone working on the project ignores the same files.
The file was most likely committed before you added the rule. Run git rm --cached on it and commit. If that is not the cause, run git check-ignore -v with the file path to see which rule, if any, applies.
No. Ignored files stay on your computer. Git simply stops offering them for commits.
No. It only controls what Git commits. Use your coding tool's ignore or permission settings, such as a .cursorignore file in Cursor or deny rules in Claude Code, and keep production secrets out of your local .env.
No. Anything committed stays in the project's history. Replace the exposed password or key with a new one, then untrack the file.
Both use the same pattern rules. The .gitignore file is shared with everyone through the repository, while .git/info/exclude stays on your machine only.