Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes

Use .dockerignore to reduce build context

Use .dockerignore to reduce build context in Docker. Learn how to exclude unnecessary files from the Docker build context, speeding up builds, reducing image size, and improving security — with a hands-on exercise.

Focus: use .dockerignore to reduce build context

Sponsored

You docker build a project, and every time you sneeze, the build context uploads your entire home directory — megabytes of .git, node_modules, and raw footage from last year's vacation. That's not just slow; it's a security liability and a waste of CI minutes. The fix is a single text file: .dockerignore. This lesson shows you exactly how to use .dockerignore to reduce build context, why it matters, and the traps to avoid.

The problem this lesson solves

Every docker build command sends a build context — the directory you specify at the end of the command — to the Docker daemon. If you run docker build . in a project that contains a 300 MB node_modules folder, Docker copies every file in that directory over the network to the daemon (even if your Dockerfile later deletes or ignores them).

The result is painful: - Slow builds — uploading unused data takes seconds to minutes. - Large images — the build context is cached, but bloated layers stick around. - Security risks — secrets, .env files, or SSH keys in the context can end up in intermediate layers of the image. - CI cost — repeated massive uploads burn bandwidth and runner time.

Without a .dockerignore, the Docker CLI and daemon have no way to know which files are irrelevant. You need a declarative filter — just like .gitignore — to tell Docker: “Don’t bother sending these.”

Core concept / mental model

Think of .dockerignore as a gatekeeper for the build context. When you run docker build, the Docker CLI reads .dockerignore from the build context root, applies the patterns to the filesystem, and sends only the files that match none of the ignore rules to the daemon.

Mental model: .dockerignore is to docker build what .gitignore is to git add. It prevents unwanted files from entering the staging area (build context).

The daemon never sees the ignored files. They don't appear in layers, can't be copied with COPY ., and don't bloat the cache. The result: a lean, fast, secure build pipeline.

How the Docker CLI processes ignore rules

  1. Read the file named .dockerignore in the root of the build context (where you run docker build).
  2. If no .dockerignore exists, the entire context directory is sent (minus some built-in exclusions like .dockerignore itself on newer clients).
  3. The daemon receives only the filtered files.
  4. The Dockerfile then operates on that reduced set.

How it works step by step

  1. Create a .dockerignore file in the same directory as your Dockerfile. If your build context is /app, the file must be /app/.dockerignore.
  2. Write exclusion patterns — one per line. Patterns use the standard Go filepath.Match syntax, with a few Docker-specific twists like ** for recursive matching.
  3. Order matters: later patterns override earlier ones. You can use ! to re-include files that would otherwise be ignored.
  4. Test it — use docker build -f - . 2>/dev/null | grep 'context' or inspect the sent context size with docker build --no-cache --progress=plain . and look for => [internal] load build context — it shows the total transferred bytes.

Pattern syntax quick reference

Pattern Meaning
node_modules/ Ignore any directory named node_modules at any level
*.log Ignore all .log files in the root of the context
**/.git Ignore .git directories recursively
!important.log Re-include important.log even if *.log is ignored
secret/*.pem Ignore all .pem files in a secret directory at root
# comment Lines starting with # are comments
empty line Blank lines are ignored

Pro tip: Always include .git and node_modules if they exist in the context. Even if you don't copy them in the Dockerfile, they contribute to the context size and can leak repository metadata.

Hands-on walkthrough

Let's create a minimal Node.js project and see the before/after with .dockerignore.

Step 1: Create a sample project

mkdir /tmp/dockerignore-demo
cd /tmp/dockerignore-demo

# Create some junk to simulate real bloat
mkdir node_modules .git
for i in $(seq 1 10); do
  dd if=/dev/urandom of=node_modules/module_$i.dat bs=1M count=5 2>/dev/null
done
for i in $(seq 1 5); do
  dd if=/dev/urandom of=.git/obj_$i.dat bs=1M count=2 2>/dev/null
done

# Create a real file
echo 'console.log("hello")' > index.js
echo 'FROM node:18-alpine
COPY . /app
CMD ["node", "/app/index.js"]' > Dockerfile

Check the context size without .dockerignore:

docker build --no-cache --progress=plain . 2>&1 | grep -i context

Expected output (size will vary, but you'll see ~70 MB):

#12 [internal] load build context
#12 transferring context: 70.15MB 0.4s done

That's 70 MB of unnecessary data — the daemon received node_modules and .git even though the Dockerfile never uses them.

Step 2: Add .dockerignore

echo 'node_modules/
.git/
*.log
' > .dockerignore

Now rebuild:

docker build --no-cache --progress=plain . 2>&1 | grep -i context

Expected output:

#12 [internal] load build context
#12 transferring context: 108B 0.0s done

We dropped from 70 MB to ~108 bytes — a 99.999% reduction. Builds now take milliseconds instead of seconds, and no secrets or git history leak into the image layers.

Step 3: Using negation to keep specific files

Suppose you want to keep one .log file for debugging, but ignore all others:

echo '*.log
!important.log
' > .dockerignore
echo 'this is important' > important.log
echo 'debug output' > debug.log

Build again and check context — important.log is included, debug.log is not.

docker build --no-cache --progress=plain . 2>&1 | grep -i 'context transferred'

You'll see the context includes important.log (size depends on file).

Compare options / when to choose what

Sometimes you have multiple ways to reduce build context. Here's when .dockerignore wins versus alternatives.

Approach What it does When to use
.dockerignore Excludes files before the build starts Always — it's the first defense and works with any Dockerfile
COPY --chown targeting only needed dirs Copies only specific subdirectories inside Dockerfile When you need fine-grained control over which directories appear in layers (but context still includes parent junk unless ignored)
Multi-stage builds Discard intermediate stages to shrink final image For production images, but does NOT reduce context size during build
.gitignore + build scripts Sometimes used in CI to clean before build Fragile — Docker daemon never sees the cleaned state; .dockerignore is explicit and portable

Winner: Combine .dockerignore with multi-stage builds for both fast builds and small images. Never skip .dockerignore.

Troubleshooting & edge cases

Mistake 1: Ignoring directories that your Dockerfile actually needs

If you ignore a directory that appears in a COPY . instruction, Docker will NOT copy it, and your build may fail or produce an incomplete image.

Fix: Use ! negations to re-include specific sub-paths or files. Test by building with --progress=plain and watch for missing files.

Mistake 2: Pattern not working because of trailing slashes

node_modules (without slash) matches anywhere — file or directory. node_modules/ (with trailing slash) matches only directories. Use the slash form when you specifically want directories.

Mistake 3: Forgetting to add .dockerignore to version control

Your colleagues, CI, and future you won't have it. Always commit .dockerignore into your repository alongside the Dockerfile.

Edge case: Context is not the Dockerfile location

If you use docker build -f /path/to/Dockerfile ., the .dockerignore must be in the build context root (.), not next to the Dockerfile.

Edge case: Symlinks inside the context

Docker resolves symlinks before checking ignore patterns. If a symlink points outside the context, it can leak files. Better to keep symlinks inside the context or ignore them explicitly.

What you learned & what's next

You now understand how to use .dockerignore to reduce build context: - It filters files before the build context is sent to the Docker daemon. - Patterns follow .gitignore-like syntax with negation support. - It directly improves build speed, reduces image size (indirectly), and eliminates security leakage. - You tested before/after with concrete numbers (70 MB → 108 B).

Next steps: Now that your builds are fast and lean, the next lesson in the track is about optimizing Dockerfile caching — ordering layers and using --cache-from to reuse previously built layers. Keep your .dockerignore handy; even with perfect caching, sending unneeded files wastes time.

Final check: Before every build, ask yourself: “Does the daemon need my entire project history, dependencies, and personal notes?” No. Add .dockerignore and build with confidence.

Practice recap

Create a new project with a Dockerfile and a few large test files. Build without .dockerignore and note the context size. Then add .dockerignore that excludes test data and tmp/, rebuild, and compare the transferred size. Try using negation to include one specific test file. This hands-on exercise will solidify your understanding of context filtering.

Common mistakes

  • Forgetting to add .dockerignore to version control — CI and teammates won't benefit from faster builds.
  • Ignoring a directory that the Dockerfile actually copies with COPY . — the build will silently miss those files.
  • Using /node_modules (absolute pattern) instead of node_modules/ — absolute patterns are rarely correct for build contexts.
  • Not testing the pattern — run docker build --no-cache --progress=plain and check the context transferred size.

Variations

  1. Use .dockerignore with pattern negation (!) to allow specific files in otherwise ignored directories.
  2. Combine .dockerignore with multi-stage builds to keep context lean and final images small.
  3. In CI, you can automatically generate .dockerignore from .gitignore using tools like dockerignore-generator.

Real-world use cases

  • Node.js project with huge node_modules — reducing context from 200 MB to ~10 KB, cutting build time from 45s to 2s.
  • Python monorepo with .venv, __pycache__, and .git — preventing secret leakage and securing build layers.
  • CI pipeline for a microservice where every second counts — using .dockerignore to keep context under 1 MB speeds up parallel builds.

Key takeaways

  • .dockerignore is a declarative filter that prevents unwanted files from entering the build context.
  • Always exclude .git, node_modules, __pycache__, and other auto-generated or sensitive directories.
  • A single .dockerignore can reduce context size by 99%, dramatically speeding up builds.
  • Use ! negation to re-include specific files that would otherwise be ignored.
  • Test your .dockerignore with --progress=plain to see the actual context size transferred.
  • Commit .dockerignore alongside Dockerfile for consistent builds across all environments.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.