A .gitignore That Actually Works for a Mixed Node + Python + Docker Repo

By Sarfaraz Khan ·

A repo that has a Node frontend, a Python service, and a Dockerfile tends to accumulate its .gitignore the same way most repos do: someone pastes in a Node template on day one, and every other stack's junk gets added reactively, one accidental commit at a time, whenever someone notices __pycache__ or a stray .env sitting in a diff. Let's do it properly once instead.

Node's side of the repo

The obvious one is node_modules/ — fully reproducible from package.json and the lockfile, and routinely tens of thousands of files. Less obvious, but just as important: npm-debug.log* and yarn-error.log* (crash logs npm/yarn write locally), and .env/.env.local if the frontend reads any build-time environment variables. If your frontend build outputs to dist/ or build/, that's generated output too — commit the source, not the bundle.

Python's side of the repo

__pycache__/ and *.pyc are Python's compiled bytecode cache — regenerated automatically and tied to the interpreter version that created them, so they're both useless to commit and actively misleading if they go stale. If the service uses a virtual environment, venv/ or .venv/ should never be tracked either — it's a full copy of an interpreter plus every installed package; anyone cloning the repo should recreate it from requirements.txt or pyproject.toml, not inherit yours.

Docker and the secrets that hide near it

docker-compose.override.yml is meant to differ per developer machine (local port mappings, mounted volumes) — it shouldn't be tracked, only docker-compose.yml itself. And this is the point in a mixed-stack repo where .env files multiply: a root .env for Docker Compose, a service-level .env for the Python app, maybe another for the Node app's build. Every single one of them needs to be excluded, and it's worth actually checking each one rather than assuming your Node template's single .env line already covers a services/api/.env sitting three directories deep — depending on how the pattern is written, it might not.

That's also exactly the kind of mistake that's cheap to catch before a commit and expensive after one: paste a .env file into an .env File Validator and it'll flag values that look like accidentally-committed secrets, on top of catching missing keys and malformed lines — worth doing on each service's env file once, not just trusting the .gitignore to have caught it.

Building it without hand-merging three templates

The tedious part of a mixed-stack .gitignore was never any single stack's list — it's merging three or four of them without ending up with duplicate # Logs sections and conflicting comments. A .gitignore Generator does exactly that merge: pick Node, Python, and Docker, and it de-duplicates overlapping patterns and keeps the file in per-stack sections instead of one undifferentiated wall of globs you have to re-read every time you wonder "wait, why is this here?"

One last thing, since it's easy to forget once the .gitignore is sorted: if the repo doesn't already explain to a new clone which .env.example to copy where, or which service needs its own virtualenv versus which one runs in Docker only, that's worth writing down. A README Generator gets the skeleton — install steps, tech stack, usage — in place in a couple of minutes, which is usually enough to stop "how do I even run this locally" from being a recurring Slack message.

Tools mentioned in this post