.gitignore — The Missing File That Exposed Production Secrets
A developer forgot to add .env to .gitignore.
20+ years shipping production infrastructure and CI/CD at scale. Lessons pulled from things that broke in production.
- ✓Basic programming fundamentals (variables, functions, control flow)
- ✓No prior devops experience needed — we start from first principles
Create a .gitignore file in your repo root. Add patterns: *.log (all log files), .env (environment file), node_modules/ (directory). Use git check-ignore -v to debug why a file is being ignored.
Gitignore is like a 'do not pack' list when moving houses. You tell the movers (Git) which boxes to ignore: 'Don't pack the trash (log files), don't pack the safe (secrets files), don't pack the pantry (generated files).' Without this list, Git tries to pack everything it finds, including things you never wanted in the moving truck.
| Chrome | Firefox | Safari | Edge |
|---|---|---|---|
| ✓ | ✓ | ✓ | ✓ |
The most common Git mistake is committing something sensitive — API keys, database passwords, .env files, or large binary files. .gitignore is the first line of defense. But it's often added reactively, after the damage is done. A single commit with a .env file means the secrets are in the repository history forever, even if you delete the file in a later commit. This guide covers gitignore patterns, the global gitignore, and the incident where a missing .env entry exposed production API keys within hours of the commit.
Gitignore Patterns: Syntax and Best Practices
.gitignore uses glob patterns: *.log ignores all files ending in .log. .env ignores .env in all directories (no leading /). /config.env ignores only the file at root. build/ ignores a build directory at any level.
Negation: !important.log overrides a previous *.log ignore. Comments: # This is a comment. Empty lines are whitespace.
Key patterns for every project: .env, .log, node_modules/, .pyc, __pycache__/, .DS_Store, dist/, build/, .idea/, .pem, .key.
git check-ignore -v <file> shows which pattern caused a file to be ignored and which gitignore file it came from. Use this to debug unexpected behavior.
.gitignore, gitignore won't help. You must first remove it from tracking: git rm --cached <file>. Only then does .gitignore take effect. git rm --cached removes from tracking but keeps the file on disk..gitignore via core.excludesFile on every developer machine. Include .env, .pem, .key, secrets., and IDE folders. This catches the 'I forgot to create a repo-specific gitignore' mistake. On CI, add a step that runs git diff --name-only --cached | grep -E '\.env$|\.pem$|secret' and fails the build if matched. For organizations, deploy a pre-receive hook on the Git server that rejects any push containing files matching .env or secret*..gitignore before the first commit. Use global gitignore via core.excludesFile for personal exclusions across all repos. git check-ignore -v debugs ignore patterns. Pre-commit hooks prevent committing sensitive files. Git history with secrets is permanent — prevention is the only cure..env Committed to GitHub — API Keys Compromised in 2 Hours
git init, wrote code, and committed. The .env file with production API keys was not in .gitignore. The entire file was committed and pushed to a public GitHub repository. Within 2 hours, automated bots had scanned the repo, extracted the keys, and used them to provision cloud resources on the company's account.git added explicitly, it won't be committed. They didn't realize that git add . stages all untracked files in the directory, including .env..env file with production AWS and Stripe API keys. 2. They ran git init, then git add . — which staged everything including .env. 3. They committed with git commit -m 'Initial commit'. 4. Pushed to a public GitHub repository. 5. Within 2 hours, automated credential-scanning bots detected the keys. 6. The bots provisioned EC2 instances for cryptocurrency mining — $15,000 in AWS charges before the keys were rotated. 7. The keys were also used to access production data through the exposed API keys..env file from the current commit (not enough — history still had it). 3. Used git filter-branch to remove .env from all history: git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch .env' --prune-empty --tag-name-filter cat -- --all. 4. Force-pushed the cleaned history (coordinated with team to reset local clones). 5. Added .env to .gitignore immediately. 6. Added a pre-commit hook that checks for patterns like .env, .pem, secret, password in staged files and aborts the commit. 7. Implemented a global .gitignore via core.excludesFile that excludes .env by default for all repos. 8. Added CI step: git diff --name-only --cached | grep -E '\.env|secret|\.pem' and fail if matched.- Always create
.gitignoreBEFORE the first commit. - Never use
git add .without checking what will be staged. - Use pre-commit hooks to scan for sensitive files.
- Set up a global
.gitignorefor common patterns. - Run
git statusbefore every commit to verify what's staged.
*.log to .gitignore — ignores all .log files.env to .gitignore — ignores .env in any directorynode_modules/ to .gitignore — ignores the entire directorygit check-ignore -v <file> — shows which pattern matchesgit config --global core.excludesFile ~/.gitignore_globalKey takeaways
.gitignore BEFORE the first commitgit add . without running git status first.gitignore via core.excludesFile for universal patternsgit check-ignore -v debugs why a file is or isn't ignoredgit filter-branch or git filter-repo to remove secrets from history (last resort)20+ years shipping production infrastructure and CI/CD at scale. Lessons pulled from things that broke in production.
That's Git. Mark it forged?
3 min read · try the examples if you haven't