What Is a .dockerignore File? Why Docker Builds More Than You Think
Learn what a .dockerignore file is, what happens if you don't have one, how Docker builds using a build context, and why excluding unnecessary files improves security, performance and reproducibility.
A .dockerignore file tells Docker which files and directories to leave
out of a build context.
That matters because docker build does not treat every file in your
working directory as automatically available forever. Docker first
determines the build context and applies ignore rules to it. Files
excluded by .dockerignore are unavailable to COPY and ADD
instructions that read from that context.
project directory
↓
.dockerignore rules
↓
build context
↓
Dockerfile instructions
For a large repository, excluding dependencies, Git history, logs, build output, and local secrets can substantially reduce the amount of irrelevant data involved in a build.
The Build Context Is What Docker Can See
Suppose a project contains:
project/
├── src/
├── node_modules/
├── .git/
├── .env
├── logs/
├── Dockerfile
└── package.json
A .dockerignore might contain:
node_modules/
.git/
.env
logs/
Those paths are excluded from the context available to ordinary COPY and ADD operations. A later instruction such as:
COPY . .
therefore copies only files that survived the ignore rules.
This is the central idea behind .dockerignore: control the inputs
available to the build. It is more precise than thinking of the file
merely as a way to make images smaller.
Docker’s build context documentation describes the current context and ignore-file behavior in detail.
Smaller Contexts Can Make Builds Faster
Local dependency directories can contain tens of thousands of files. Git history, test reports, caches, and generated output can add hundreds of megabytes more.
If those files are irrelevant to the build, including them creates work with no useful result. Docker or the selected builder may need to walk, checksum, transfer, or otherwise account for context data before and during the build.
Excluding irrelevant paths reduces that input.
The effect depends on the builder and where it runs. Modern BuildKit can avoid transferring some unused files in ways older explanations of Docker builds do not capture, so it is better to think in terms of keeping the build context deliberate rather than assuming every build always uploads one giant archive to a daemon.
.dockerignore Does Not Automatically Make the Image Smaller
Build-context size and image size are different things.
Suppose node_modules/ exists locally but the Dockerfile copies only:
COPY package.json package-lock.json ./
RUN npm ci
COPY src/ ./src/
Local node_modules/ is never copied into the image. Ignoring it can
still reduce unnecessary context processing, but it may not change the
final image size.
Now consider:
COPY . .
If node_modules/ remains in the context, that broad copy can put it
into the image. Excluding the directory can then affect both the context
and the resulting image.
The useful question is therefore: does this file need to be available to this build?
Ignore Sensitive Local Files Before the Build
Development directories often contain .env files, credentials, private
keys, cloud configuration, and other data that should not become build
inputs.
A .dockerignore can keep those files out of the context:
.env
.env.*
*.pem
This reduces the chance that a broad COPY instruction or an unexpected
build step exposes them.
It is still only one protection. Secrets that the build genuinely needs should use an appropriate secret-management mechanism rather than being copied into the context and deleted later. Docker BuildKit, for example, supports secret mounts for build-time secrets.
Patterns Decide What Stays Out
Each line in .dockerignore is a pattern. Common rules include:
node_modules/
.git/
coverage/
dist/
*.log
.env
.vscode/
.idea/
Docker also supports exceptions with !:
*.md
!README.md
The second rule makes README.md available again after the broader
Markdown rule excluded matching files. Rule order matters when
exclusions and exceptions overlap.
Docker’s matching behavior has details beyond ordinary shell globs, so unusual patterns are worth checking against the official syntax documentation.
A Practical File Should Match the Dockerfile
There is no universal .dockerignore that should be copied into every
repository. The file has to agree with what the Dockerfile actually
needs.
For a typical Node.js project, a starting point might be:
node_modules/
.git/
coverage/
dist/
*.log
.env
.vscode/
.idea/
If the Dockerfile contains:
COPY package.json package-lock.json ./
both package files must remain in the context. If an ignore rule removes one of them, the build cannot copy it.
The same principle applies to generated files. If the container build
creates dist/ itself, excluding the local copy is sensible. If the
Dockerfile expects a prebuilt dist/, excluding it breaks the build.
The goal is not the smallest possible context. It is the smallest context that still contains the required inputs.
.dockerignore and .gitignore Control Different Boundaries
The files look similar but serve different tools.
| File | Controls |
|---|---|
.dockerignore | Files available in a Docker build context |
.gitignore | Untracked files Git normally leaves out of version control |
A file ignored by Git can still enter a Docker build context. A file ignored by Docker can still be committed to Git.
That distinction is especially important for secrets. Putting .env in
.gitignore helps prevent an untracked local file from being added to
Git accidentally; it does not by itself prevent docker build from
seeing the file.
Dockerfile-Specific Ignore Files Are Also Supported
Projects with several Dockerfiles can provide rules for each one. Docker
supports files named after the Dockerfile with the .dockerignore
suffix:
Dockerfile
.dockerignore
build.Dockerfile
build.Dockerfile.dockerignore
test.Dockerfile
test.Dockerfile.dockerignore
When a Dockerfile-specific ignore file is used, its rules take
precedence over the root .dockerignore for that Dockerfile. This is
useful when production, test, and build images genuinely need different
context inputs.
Treat the Context as an Input Boundary
A good .dockerignore is usually short because most projects have only
a few categories of local files that should never participate in a
container build: dependencies installed during the build, repository
metadata, editor state, logs, temporary output, local secrets, and
generated artifacts that the build recreates.
Review the file when the Dockerfile or project layout changes. An ignore rule that was correct six months ago can become wrong after a build starts depending on a new file.
The simplest test remains the most useful one: if Docker cannot see this path, can the build still produce the intended image? If yes, excluding it keeps the build input smaller and more deliberate.
More Articles Like This
What Is YAML? How YAML Configuration Files Work

Cron Expressions Explained (With Examples You'll Actually Use)

Base64 vs Hex Encoding: When to Use Each

JSON Schema vs TypeScript Types: Do You Need Both?

Regex vs Parsing: When Pattern Matching Stops Being Enough
