> ## Content Index
> Fetch the complete content index at: https://debugly.dev/llms.txt
> Use this file to discover other available public pages before exploring further.

# Reading a Container Image With docker history
- URL: https://debugly.dev/docker-history-image-forensics/
- Published: 2026-10-09T12:28:00.000Z
- Updated: 2026-10-17T05:56:42.000Z
- Description: Every layer is a transaction and the ledger never lies. docker history shows the secret you deleted, the cache you paid for and the compiler you shipped…
- Author: Rohit Bhadani
- Tags: Tooling, Docker, Security

The security review asked whether the image still contained the credentials that had been "removed" in a later commit, and the answer arrived in one command, because the image's history showed the COPY layer that added the secret and the RUN layer that deleted it, and the two layers coexist, which is the entire lesson: a container image is a ledger, and deletion is a new transaction that hides the file from the final view but not from the record. The secret was shipped to every registry and every node that ever pulled the image, and the rm was a cover up performed in public.

This is how to read an image the way a forensic accountant reads a ledger, because the image is one, and the tools are a couple of commands.

This was Docker 27.5, and the readings below are the ones that answer the three questions every image review asks: what does it weigh, what did it cost, and what is hiding in it.

## The ledger model

An image is an ordered stack of layers, each layer the diff produced by one instruction, and the filesystem you run is the sum. The history lists the layers with their instruction and their size, and the sizes are the ledger's amounts. Reading it is:

```
docker history --no-trunc --human image:tag

```

The flags matter: no truncation shows the full instruction, which is where the copied paths and the build arguments live, and human sizes make the heavy layers jump out. The first pass is a scan for the tall rows, because the cost of the image is concentrated in a few layers, and the few are where the review goes.

## What it weighs: the layers that never shrink

The tall rows are usually the package installs and the dependency copies, and the ordering tells the cache story. A COPY of the whole source before the install throws away the install cache on every source change, which is the layer invalidation defect in [why your docker build reinstalls every dependency every time](https://debugly.dev/docker-layer-cache-invalidation/), and the history shows it as an install layer that rebuilds constantly in CI, which is a time tax, not just a size one.

The final image size is the sum, and the review question is whether the final layer set matches what the runtime needs. A compiler in the final sum is a build stage that leaked, which is the multistage argument in [a one point two gigabyte image that should have been ninety megabytes](https://debugly.dev/dockerfile-multistage-slim-image/), and the history makes the leak visible as a layer the runtime never uses but permanently carries.

## What is hiding: deletion is a transaction, not an erasure

The security reading is the inverse of the size reading: look for RUN rm and the layers before it. A layer that adds a file and a later layer that removes it leaves the file present in the earlier layer, pullable by anyone with the image, because pulling fetches every layer. The history shows both rows, and the pair is the evidence that the secret shipped.

The same applies to build arguments: an ARG value is not a layer but it lands in the image's metadata, and history with the right flags exposes the instruction line carrying it, which is why secrets in ARG are secrets in the registry. The ledger records the instruction, and the instruction is where the value sat.

To confirm a file's presence in an old layer, the accountant exports the layer and inspects it, which is the difference between reading the ledger and opening the box it describes, and it is how the review proves the secret is retrievable rather than merely once present.

## The squash and the slim are different claims

A squashed image collapses the layers, which removes the old layers from the ledger and with them the deleted files, so squashing is a real erasure of the record, at the cost of the cache granularity and the readable history. The review should therefore treat a squashed image as a different artefact: smaller and cleaner, but with the ledger destroyed, which is fine for a runtime image and wrong for the image you want to audit.

The honest pipeline keeps the auditable, layered image for review and ships a slim, multistage runtime image that never contained the secret in any layer, because not adding is the only erasure that needs no tool, which is the prevention half of [deleting the commit did not delete the secret](https://debugly.dev/secrets-in-git-history-are-still-there/) translated to images.

## The routine reading

Made a habit, the history reading is a five line review: scan the tall rows and ask whether each belongs in the runtime, scan the add then delete pairs and ask what they tried to hide, scan the instructions for ARGs and copied paths that name secrets, and compare the final sum to the budget. The habit catches the bloat, the leak and the secret in one pass, before the registry does the distribution that makes them permanent.

## The rule I keep

A container image is a ledger where every layer is a transaction and deletion is a new transaction that hides but never removes, so the history answers what the image weighs, what its cache costs and what it is hiding. Read the tall rows for bloat, the add then delete pairs for secrets, the instructions for leaked values, and ship a runtime image that never contained the secret in any layer.

The rm that follows the COPY is not a fix. It is a transaction in a public ledger, and the ledger is exactly what the attacker pulls, layer by layer, which is why the only clean erasure is never having written the line.

---

Published on [debugly.dev](https://debugly.dev/) by [Rohit Bhadani](https://debugly.dev/about/) | Founder of [Krova Cloud](https://krova.cloud/?ref=debugly.dev)