Most Docker setups are shipping 500MB of garbage to the daemon. You’re not alone—this is the default state of things. The culprit? You don’t have a .dockerignore file, or it’s terrible.
Here’s what happens without one: docker build . sends your entire build context to the Docker daemon. That means every .git folder, node_modules, test files, markdown docs, IDE config, CI logs, and random junk you’ve accumulated. This context balloons your build, slows down the transfer to your daemon, and—here’s the kicker—can expose secrets if you’re not careful.
Why This Matters
Let’s say you have a 200MB node_modules folder, a 300MB .git history, and some .env files lying around. When you run docker build, the daemon receives all of it. Even though your Dockerfile only copies COPY package.json . and maybe COPY src ./src, Docker still has to parse the entire context to figure out what changed for layer caching. Slow builds. Wasted bandwidth.
Worse: if you’re running this in CI, you’re uploading secrets and sensitive config to the build machine every single time.
The Fix: A Bulletproof .dockerignore
Create a .dockerignore file in your project root (same level as your Dockerfile) and copy this in:
# Version control.git.gitignore.gitattributes
# Node.js (if you use it)node_modulesnpm-debug.logyarn-error.logpackage-lock.jsonyarn.lock
# Python__pycache__*.pyc*.pyo*.pyd.Pythonenv/venv/pip-log.txt
# Environment and secrets.env.env.local.env.*.local.aws.ssh
# IDE and editor garbage.vscode.idea*.swp*.swo*~.DS_Store.sublime-project.sublime-workspace
# Build artifacts and temp filesbuild/dist/*.o*.a*.so.cachetmp/temp/
# CI/CD.github.gitlab-ci.yml.circleciJenkinsfile
# Documentationdocs/README.mdCHANGELOG.mdLICENSE*.md
# Testingtest/tests/.test.coveragecoverage/htmlcov/
# DockerDockerfiledocker-compose.ymldocker-compose.*.yml.dockerignoreThis is a kitchen-sink version. You won’t use all of it, but it covers most languages and patterns. Trim it to your project.
How to Customize It
Start with the template above, then remove lines for things you do need in the container. For example:
Python web app — You might want docs in the image, so remove docs/ and README.md:
# Remove these lines from the template# README.mdNode.js app — You probably want to keep .git out but can remove the Python stuff:
# Remove from template# __pycache__# *.pyc# etc.Go binary — Most Go apps need almost nothing:
.git.gitignore.env.env.local.vscode.idea.DS_Storetest/tests/README.mddocs/Dockerfiledocker-compose.ymlReal-World Impact
Let’s quantify this. Suppose you have:
.git: 150MBnode_modules: 400MB.envand secrets files: 5MB- Misc docs and config: 45MB
Without .dockerignore: Docker daemon receives 600MB. Layer caching checks everything. Build takes 15 seconds to send context alone.
With .dockerignore: Docker daemon receives 50MB (just your source). Build context sent in 1 second. Layer caching is faster. Same build, 10x quicker.
And you’re not exposing .env or .aws credentials to the build machine.
Pro Tips
- Test your .dockerignore: Run
docker build --no-cacheand watch the output. It’ll tell you what’s being sent:
$ docker build -t myapp:v1 .Sending build context to Docker daemon 2.048kB # ← This should be small-
Use .dockerignore in your CI: If
.dockerignoreisn’t checked in (and it should be), CI machines won’t respect it. Check it in. -
Don’t ignore what you COPY: If your Dockerfile does
COPY . ., make sure you’re not ignoring files you actually need. -
Secrets should never be in the build: Use build arguments or secret mounts instead of relying on .dockerignore:
FROM node:18WORKDIR /appCOPY package.json .RUN --mount=type=secret,id=npm npm ciCOPY . .RUN npm run buildCMD ["npm", "start"]Build with: docker build --secret npm=~/.npmrc .
Checklist
- Create
.dockerignorein your project root - Add version control (
.git,.gitignore) - Add language-specific ignores (node_modules, venv, pycache)
- Add secrets (
.env,.aws,.ssh) - Add IDE/editor junk (.vscode, .idea, .DS_Store)
- Test:
docker buildshould report small context size - Check
.dockerignoreinto git
A .dockerignore file takes 2 minutes to write once and saves you thousands of seconds over your project’s lifetime. Your future self will thank you.
Verify Your Context Size
After setting up .dockerignore, confirm the build context is actually smaller:
# Before and after comparisondocker build --no-cache . 2>&1 | grep "Sending build context"You should see something like:
Sending build context to Docker daemon 1.234kBIf it’s still showing megabytes and you expected kilobytes, something’s not in your .dockerignore. The --no-cache flag ensures you’re not seeing a cached result.