Docker's build context includes everything in your directory - and I mean EVERYTHING. Your source code, sure, but also that 300MB .git
folder, the node_modules
directory that's bigger than Windows 95, and every random file you've accumulated over months of development. Docker's "upload everything" mentality is about as helpful as a screen door on a submarine.
What Actually Gets Sent
Docker's build context includes everything in the build directory by default:
- Your source code (good)
- Git history in
.git/
(usually unnecessary - can be 100MB+) - Node.js dependencies in
node_modules/
(often 200MB+) - Build artifacts, logs, temporary files
- IDE files, OS files like
.DS_Store
- Previous Docker layers or images saved locally
Docker sends the entire build context to the daemon before it even looks at your Dockerfile. So that 1GB of crap gets uploaded even if your Dockerfile only copies a single 5MB file. Yeah, it's as stupid as it sounds.
The Real-World Impact
I've seen this nightmare personally: that classic Stack Overflow case where someone's 1GB RPM file turned into a 3.5GB context transfer because Docker uploaded their entire project history. I once had a build context that included our entire database backup because some genius put it in the project root. That took down prod for 2 hours while we waited for a 4GB context to upload over our shitty corporate VPN that maxed out at 10Mbps on a good day.
Real cost of massive contexts (learned this the hard way):
- Build times that kill productivity (10 minutes for a 3GB context, developers just give up and go get coffee)
- AWS charges for compute time even when builds fail (our monthly bill doubled because of these massive context transfers)
- Memory usage spikes crash Docker daemon (OOMKilled on 8GB instances, had to upgrade to 16GB)
- CI/CD pipelines become unreliable as fuck (GitHub Actions failed 60% of the time, team lost confidence in deployments)
Why This Happens More Often Now
Modern development makes this worse:
- Monorepos: Everything in one massive repo
- Microservices: Dockerfile at root pulling the entire universe
- Generated files: Webpack vomits build artifacts everywhere
- Development tools: Test coverage and reports pile up
- Docker-in-Docker: Because we love making things complicated
Docker's "send everything just in case" approach is like packing your entire house when you're going camping for the weekend. Unlike Git which tracks specific files, Docker assumes you might need that random 2-year-old log file buried in your project folder.
The BuildKit vs Legacy Builder Difference
BuildKit is a lifesaver if you're on Docker 18.09+. But good fucking luck if you're stuck on corporate Docker 17.x that your company refuses to upgrade for "security reasons." When BuildKit works (and ops lets you enable it), my builds went from taking forever to finishing before I can even alt-tab to check Slack. We're talking actual 51 seconds down to 0.166 seconds with identical Dockerfiles.
Legacy Docker Build:
$ docker build .
Sending build context to Docker daemon 4.315GB
[... 51 seconds later ...]
Docker BuildKit:
$ DOCKER_BUILDKIT=1 docker build .
[+] Building 0.1s (5/5) FINISHED
[... completes in 0.166 seconds ...]
BuildKit only transfers files actually referenced in the Dockerfile instead of uploading your entire project like some kind of digital hoarder. Finally, some sanity in the Docker build process.
Diagnosing Your Context Size Problem
Check what's being included:
## See files that would be sent (requires ripgrep)
rg -uuu --ignore-file .dockerignore --files . | head -20
## Or with find (slower but available everywhere)
find . -name .dockerignore -exec cat {} \; | grep -v "^#"
ripgrep respects ignore files and is stupidly fast for this kind of debugging.
Monitor context preparation:
## Time context sending specifically
time docker build --no-cache -t test .
The first line of output shows context size. If it's unexpectedly large, you have files being included that shouldn't be.
Common size offenders:
.git/
directory (often 50MB-2GB)node_modules/
(typically 100MB-500MB)- Build outputs (
dist/
,target/
,build/
) - IDE files (
.vscode/
,.idea/
) - OS files (
.DS_Store
,Thumbs.db
) - Log files, temporary files, cache directories
The fix isn't moving files around - it's telling Docker to ignore the shit you don't need using .dockerignore
.
Alright, enough bitching about Docker's stupidity. Let's fix this mess with solutions that actually work in the real world.