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.