Myth: if a later Dockerfile step deletes a file, the file is gone from the image. Fact: the OCI image format records the deletion as a marker in the newer layer, while the earlier layer still contains the file. Anyone who pulls the image layers can extract it.
What the spec says
The OCI image specification describes a layer as a serialized set of changes, not a full copy of the filesystem. Layers are applied on top of each other to build the complete filesystem.
A deletion is recorded as a whiteout: an empty file with a special name that tells the runtime to treat a path as deleted. The name is a dot, the letters wh, a dot, and then the name of the removed entry. To hide a whole directory's contents there is an opaque marker with a longer special name.
The specification also says whiteouts apply only to resources in lower or parent layers. So the earlier layer is untouched. The merged view you see in a running container shows the file as missing, but the data is still stored in the layer that added it. The specification does not discuss storage impact directly. This follows from how the layered model works.
Why it bites teams
Here is a typical mistake.
COPY id_rsa /root/.ssh/id_rsa
RUN git clone git@example.com:org/private.git
RUN rm /root/.ssh/id_rsa
The final container has no key file. The layer created by the first COPY still holds it. The image is a stack of tar archives, and extracting one layer gives you the key.
The same applies to:
- Package caches you remove in a separate RUN
- Source trees deleted after a build step
- Config files with credentials, edited later
- Large temporary downloads (a size problem, not only a secret problem)
What actually works
Never write the secret into a layer. Use a build secret mount so the value exists only for one instruction. See the build secrets post.
Delete in the same instruction that creates. If a temporary file must exist, create it and remove it within one RUN so no layer records it. This is fine for caches and downloads, but not a safe pattern for real credentials.
Use a multi-stage build. Do the work in a build stage and copy only the output. The earlier stage's layers are not part of the final image. See multi-stage builds.
Squashing is a patch, not a plan. Flattening layers can hide old content, but a secret that already reached a registry should be treated as exposed.
How to check an image you already have
- Export the image to a tar archive with your build tool.
- List the layer archives inside.
- Search each layer for the file name or string you worry about.
- If you find a secret, rotate it before cleaning anything.
Scanners that read every layer can find secrets in files that the final filesystem no longer shows. Safeguard's SBOM and dependency scanning covers what the image contains, and you can try it on a free scan.
Quick check
Does docker history show the deleted file?
It shows the instructions that created each layer, which often reveals the command that added the file. The file's contents are in the layer archive, not the history output.
Is the file visible when I run the container?
No. The runtime applies the whiteout, so the path looks deleted inside the running container.
Sources
Last reviewed: 19 October 2026.