Most Docker images ship with megabytes of junk nobody notices. Build caches, apt lists, temp files, duplicated layers. The dive docker tool shows you exactly where that junk lives. It opens any image in a terminal UI, breaks it down layer by layer, and highlights every file each layer added, changed, or deleted. I run it on every image before pushing to a registry, and it has caught bloat I would never have spotted from a Dockerfile alone.
Dive was written by Alex Goodman and sits at over 54,000 stars on GitHub. It works with Docker, Podman, and plain image tarballs, so you can use it even on machines without a Docker daemon.
Installing the Dive Docker Tool
Dive is a single Go binary. On Debian and Ubuntu, grab the .deb from the dive GitHub repository:
DIVE_VERSION=$(curl -sL "https://api.github.com/repos/wagoodman/dive/releases/latest" | grep '"tag_name"' | sed -E 's/.*"v([^"]+)".*/\1/')
curl -OL "https://github.com/wagoodman/dive/releases/download/v${DIVE_VERSION}/dive_${DIVE_VERSION}_linux_amd64.deb"
sudo apt install ./dive_${DIVE_VERSION}_linux_amd64.deb
Arch users get it from the official repos with pacman -S dive. On Fedora, download the .rpm from the same releases page. There is also a container image, which means you can run dive without installing anything:
docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock \
wagoodman/dive:latest nginx:latest
Check the install with dive --version. Then point the dive docker tool at any image you have locally:
dive nginx:latest
1. Reading Layers with the Dive Docker Tool
The screen splits in two. The left pane lists every layer with its size and the command that created it. The right pane shows the filesystem tree as it exists at the selected layer. Move down the layer list with arrow keys and the tree updates to show what that specific layer changed.
Files are color coded. Yellow means modified, green means added, red means removed. The dive docker tool gives you the fastest answer I know to the question “why did my image grow by 80 MB after that one RUN line”.
The keybindings that matter:
- Tab: switch between the layer pane and the file tree
- Ctrl+U: show only the files the current layer changed
- Ctrl+F: filter the file tree by name
- Space: collapse or expand a directory
- Ctrl+A: toggle added files on and off
- Ctrl+R: toggle removed files on and off
Ctrl+U is the one to learn first. A base image tree has thousands of files, and filtering to just the current layer’s changes cuts the noise instantly.
2. Finding Wasted Space
The bottom left panel shows an efficiency score and a wasted space total. Dive computes this by tracking files that appear in one layer and get overwritten or deleted in a later one. Those bytes still ship in the image. Deleting a file in a later layer never shrinks anything, because the file remains in the earlier layer’s archive.
Classic offenders show up in the dive docker tool immediately. An apt-get update in one RUN line and an rm -rf /var/lib/apt/lists/* in a separate RUN line wastes the full size of the package lists. The fix is chaining both into a single RUN so the cleanup happens inside the same layer. The same logic applies to pip caches, npm caches, and downloaded tarballs you extract and delete.
Dive lists the worst files in a “potential wasted space” table, sorted by size. I have seen 200 MB images where a third of the weight was a build toolchain that a later layer deleted. A multi-stage build fixed it in ten minutes.
3. CI Mode: Failing Builds on Bloat
The interactive UI is for humans. For pipelines, the dive docker tool has a CI mode that runs without the interface and exits nonzero when an image misses your thresholds:
CI=true dive myapp:latest \
--lowestEfficiency=0.95 \
--highestUserWastedPercent=0.10
That command fails the build if the efficiency score drops below 95 percent or wasted space exceeds 10 percent of the image. Wire it into GitHub Actions or GitLab CI right after the build step and image bloat becomes a failed pipeline instead of a slow surprise in production. I treat it the same way as a linter: annoying for a week, then you stop writing the mistakes it catches.
Thresholds also live in a config file at ~/.config/dive/dive.yaml, which keeps the pipeline definition clean:
rules:
lowestEfficiency: 0.95
highestUserWastedPercent: 0.10
4. Analyzing Tarballs and Podman Images
The dive docker tool does not need a running Docker daemon. If you export an image to a tar archive, it reads the archive directly:
docker save myapp:latest -o myapp.tar
dive --source docker-archive myapp.tar
Podman users pass a different source flag:
dive podman://myapp:latest
This matters on hardened servers where the Docker socket is locked down, or when someone hands you an image tarball from an air-gapped environment and asks what is inside it. You get the full layer breakdown without loading the image anywhere.
Where the Dive Docker Tool Fits in a Workflow
The dive docker tool answers one question: what is inside this image and why is it that big. It does not scan for vulnerabilities, so pair it with a scanner. I covered Trivy earlier, and the two together cover both size and security before an image leaves your machine.
It also fits the same niche as other terminal UIs that replace flag-heavy commands. If you like working this way, lazygit does it for git, and yazi does it for file management. Same idea: stop memorizing inspection commands, look at the actual state on screen.
Smaller images pull faster, cold-start faster, and expose fewer packages to patch. A slim image is not a vanity metric. Registry storage and transfer costs scale with every gigabyte you push, and so does the time your nodes spend pulling.
When Not to Use the Dive Docker Tool
Skip it for images you do not control, like third-party base images you cannot rebuild. Knowing that postgres:16 wastes 12 MB helps nobody, since you are not going to patch upstream Dockerfiles. Spend the effort on the layers your own Dockerfile adds.
It is also not a runtime inspector. For questions about a live container, docker exec and docker stats remain the right tools. Dive reads image archives, not running processes.
Everything else in the container build loop is fair game. Run dive myapp:latest after your next build. The wasted space table usually pays for the five minutes it takes.