A docker ignore file — the .dockerignore at the root of your build context — is your first line of defense against baking secrets, credentials, and unnecessary files into a container image, and most teams write it as an afterthought if at all. The difference between a lean, safe image and one that leaks your .env and entire git history often comes down to a dozen lines in this one file. It costs nothing and prevents a category of incident that is embarrassingly common.
Why the build context is the problem
When you run docker build ., Docker sends everything in that directory — the build context — to the daemon. Every COPY . . in your Dockerfile then risks pulling all of it into a layer. Without a .dockerignore, that sweep can include:
.envfiles and other local secret stores- The
.gitdirectory, which contains your full commit history (including secrets you thought you removed) node_modulesor vendored dependencies you meant to rebuild inside the image- Cloud credential files like
.aws/credentials - CI logs, test artifacts, and editor config
The .git case deserves emphasis. Deleting a secret in the latest commit does nothing if the old commit that introduced it is still in history — and copying .git into an image ships that history to anyone who can pull the image. A .dockerignore cuts it off at the source.
Writing a .dockerignore that earns its keep
The syntax mirrors .gitignore. Start restrictive and add back what you need:
# .dockerignore
.git
.gitignore
.env
.env.*
*.pem
*.key
node_modules
npm-debug.log
.aws
.ssh
Dockerfile
.dockerignore
**/*.test.js
coverage
.vscode
.idea
A few practical notes. Patterns are matched against the build context root. ** matches across directories. You can re-include a file after excluding its parent with !, which is handy when you exclude a whole directory but need one file from it:
config/*
!config/production.json
Order matters for the negation pattern — the ! line must come after the exclusion it overrides.
Verify what actually landed in the image
Trust but verify. After a build, inspect the image contents rather than assuming the ignore file worked:
# list files in the built image
docker run --rm your-image find / -name "*.env" -o -name "*.pem" 2>/dev/null
# or inspect layer by layer
docker history --no-trunc your-image
Better still, run an image scanner in CI. A container-aware SCA scan inspects image layers for embedded secrets and known-vulnerable packages, catching a leaked credential before the image reaches a registry. Combined with a good .dockerignore, you get defense in depth: prevention at build time, detection at scan time.
The certificate questions: docker ignore certificate
A different but frequently searched "ignore" topic is TLS certificate errors when talking to registries — people looking for a docker ignore certificate flag. This is worth addressing precisely, because the easy workaround is a security mistake.
If a docker pull ignore certificate or docker login ignore certificate error appears, it usually means the registry uses a self-signed or internal-CA certificate that your Docker daemon does not trust. The wrong fix is to disable verification globally. The right fix is to trust the specific CA.
For a registry with a private CA, install its certificate where the daemon looks for it:
# Linux: place the CA cert for the specific registry host
sudo mkdir -p /etc/docker/certs.d/registry.internal:5000
sudo cp myca.crt /etc/docker/certs.d/registry.internal:5000/ca.crt
sudo systemctl restart docker
This scopes trust to exactly one registry using exactly one CA — the opposite of blanket-disabling verification. If you are tempted to add a registry to insecure-registries in daemon.json to make an error disappear, understand that you are turning off TLS verification for that host and inviting a man-in-the-middle on your image pulls. Reserve it for isolated local test registries, never production.
A container hygiene checklist
- Commit a
.dockerignoreto every repository that builds an image, and lead with.git,.env, and key/cert patterns. - Prefer explicit
COPY package.json ./style copies over broadCOPY . .where feasible. - Use multi-stage builds so build-time files never reach the final image.
- Scan built images for secrets and vulnerable layers in CI.
- Fix registry certificate trust properly per-host; never disable TLS verification globally.
The Safeguard Academy has more on hardening container build pipelines from context to registry.
FAQ
What is the difference between .dockerignore and .gitignore?
They use similar syntax but serve different purposes. .gitignore controls what git tracks; .dockerignore controls what gets sent to the Docker build context and can be copied into an image. A file can be in git but excluded from images, or vice versa — you generally want both configured.
Will .dockerignore remove a secret already baked into an image?
No. It only affects future builds by keeping files out of the build context. A secret already committed to an existing image layer stays there; you must rebuild without it and rotate the exposed credential.
How do I fix a Docker certificate error without disabling security?
Install the registry's CA certificate under /etc/docker/certs.d/<registry-host>/ca.crt and restart the daemon. This scopes trust to that one registry rather than disabling TLS verification globally, which is what insecure-registries does.
Should I add node_modules to .dockerignore?
Usually yes. You typically want to install dependencies fresh inside the image for the target platform rather than copying a host-built node_modules, which may contain platform-specific binaries or stale packages.