Open-source project
wagoodman/dive avatar
wagoodman/dive

dive reads each layer of a Docker image and reports, it never rewrites one

GitHub describes it as A tool for exploring each layer in a docker image. The repository metadata lists Go as its primary language. The metadata lists the MIT license. This article stays within the project description and details documented in the GitHub repository README.

54,621 stars2,002 forksGoMIT

At a glance

What is it?
dive is a Go terminal UI that breaks a Docker image into layers, marks what changed in each one and estimates how much space was wasted. The estimate is a guess the README calls experimental, the report is the entire output, and the podman source is Linux only.
Who is it for?
dive fits whoever owns a Dockerfile and keeps wondering where the bytes went, because the layer tree, the change markers and the build command answer that inside one terminal, and CI=true runs the same analysis with no interface. Skip it if you expect the tool to hand back a smaller image, since no documented command rewrites one, and decide what to do about the efficiency score before you gate on it, because the README calls that number experimental.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Activity is slowing. The repository last received commits 9 months ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The left pane lists layers, the right pane replays them

Selecting a layer in the left pane fills the right pane with that layer's contents combined with everything below it, so the tree you read is the filesystem as it stood at that point rather than a raw diff. Arrow keys walk the file tree, and files that were changed, modified, added or removed are marked in place. The marking is switchable: show the changes belonging to one layer, or aggregate every change up to that layer. Layer sizes and the efficiency estimate share the lower left pane.

Underneath sits gocui. go.mod pins github.com/awesome-gocui/gocui v1.1.0 alongside a separate keybinding module, with lipgloss v1.1.0 for styling and termenv v0.16.0 for colour detection, which is why dive wants a real terminal and why the pipeline path has to switch the interface off instead of rendering it into a log. The source is split three ways, cmd/ for the command entry point, dive/ for the analysis package itself and internal/ for the rest.

The efficiency score is a guess the README calls experimental

The lower left pane reports what the project calls image efficiency: a percentage score plus total wasted file space. The mechanism behind the number is a heuristic, and the README says so, describing an experimental metric that guesses how much wasted space an image holds. It attributes waste to three causes: files duplicated across layers, files moved between layers, and files that were added and then not fully removed.

A pass in a pipeline is a comparison of thresholds applied to that guess, so a build can fail on a reading rather than on bytes you can point at, and nothing in the report lets you calibrate the score or exclude a file from it. The headless mode is reached by setting CI=true in the environment, and the Makefile shows the same idea written as flags on the Windows test run:

bash
dive.exe --source docker-archive .data/test-docker-image.tar --ci --ci-config .data/.dive-ci

Those two flags do not appear in the usage examples, so the config file they point at is one you supply.

dive reads an image and never writes one back

No documented command produces a smaller image. `dive <your-image-tag>` inspects an image that already exists, and `dive build -t <some-tag> .` runs a build and then analyses the result, which the README frames as replacing your docker build command with the same dive build command. The output is a report rather than an artifact: there is no export step, no squash step and no rewrite step anywhere in the usage.

So the wasted-space figure tells you which layer to open, not how to restructure the Dockerfile. Moving an apt cache into a build stage or dropping a file that was added and deleted again stays manual work, and a team that treats the percentage as a target will be editing Dockerfiles by hand to move a number the tool only estimates.

The alternative approach on the same host is the Docker CLI's own image history plus docker save to a tar you open yourself. That gives raw per-layer diffs with no score attached and nothing to gate a pipeline on. The project's own container ships a Docker CLI, so both commands sit alongside dive in the same image.

The podman source is Linux only, so macOS runs the container

Three sources are documented and they are not equal. `docker` is the default and talks to the engine. `docker-archive` reads a Docker tar archive from disk, which is the one path that needs no running daemon at all. `podman` talks to the Podman engine and is marked Linux only. On macOS the project supports the Docker container engine only, so a Mac without a working Docker engine has a gap between what exists and what is written down, and the tar archive becomes the fallback.

The macOS invocation for the build form shows what the container route costs in setup:

bash
docker run --rm -it \
      -v /var/run/docker.sock:/var/run/docker.sock \
      -v  "$(pwd)":"$(pwd)" \
      -w "$(pwd)" \
      -v "$HOME/.dive.yaml":"$HOME/.dive.yaml" \
      docker.io/wagoodman/dive:latest build -t <some-tag> .

It mounts the working directory, sets it as the container's working directory, and mounts your config from `$HOME/.dive.yaml` to the same path, so the file travels into the run instead of being read off the host's home directory.

Installing dive: a release script, a versioned package, or go install

On Debian and Ubuntu the documented route resolves the newest tag from the GitHub API, downloads the matching deb and installs it:

bash
DIVE_VERSION=$(curl -sL "https://api.github.com/repos/wagoodman/dive/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
curl -fOL "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

Nothing in that script pins a version, so an unpinned host tracks whatever release came out last, which right now is v0.13.1 from 2025-03-29, eight days after v0.13.0 and a year and a half after v0.12.0 on 2024-02-02. The other package managers are one line each:

bash
brew install dive
bash
pacman -S dive

Arch also carries it in the extra repository, RHEL and CentOS take the matching rpm, and Windows has choco, scoop and winget. The Nix attributes are nixos.dive and nixpkgs.dive.

The Go route is the one with a stated price. It needs Go 1.10 or higher, and the caveat under it is that installing this way you will not see a proper version when running `dive -v`. That removes your ability to tell which build a job or a colleague is running.

The snap route is the one the project warns against

The snap install is four commands, and the caution attached to it is the reason to pick the deb or the container instead. The README states that the snap method is not recommended if you installed Docker via apt-get, since it might break your existing Docker daemon, and it points at issue 546 for the details.

bash
sudo snap install docker
sudo snap install dive
sudo snap connect dive:docker-executables docker:docker-executables
sudo snap connect dive:docker-daemon docker:docker-daemon

The two connect calls are the part that matters. A snap-packaged Docker and a snap-packaged dive negotiate access to the daemon through explicit interfaces, so if your engine came from apt-get or from the Docker apt repository you now have two confinement systems describing the same socket, and the outcome of that negotiation is the break the warning describes. A machine that already has a working engine is the exact case the warning names, so the ordering matters: read the caution before you run the four lines, not after your daemon stops.

Reaching the daemon: the socket, the API version, and Colima

The container route mounts the Docker socket, and that mount is the whole reason a containerised dive can see an image:

bash
docker run --rm -it \
    -v /var/run/docker.sock:/var/run/docker.sock \
    docker.io/wagoodman/dive:latest <dive arguments...>

The socket ties the tool to a running daemon, and two adjustments are documented for the cases where the socket is not where dive expects it. On some Docker versions the API version has to be pinned, and the example sets DOCKER_API_VERSION=1.37, with the container form passing the same value through -e. On an alternative runtime such as Colima the local images sit behind a different host, so the documented fix is to export the host from the current context:

bash
export DOCKER_HOST=$(docker context inspect -f '{{ .Endpoints.docker.Host }}')

Skip either step when you need it and a run against a local image finds nothing where you expected it, while the failure points at the engine rather than at the image name you typed. A user with no Docker daemon at all has no documented route through the socket, and the archive path becomes the only one left.

The last push is 2025-12-15 and the newest tag is v0.13.1

The repository is not archived, and it is not the kind of project whose cadence you can read off its tags. The last push was on 2025-12-15 while the newest published release, v0.13.1, came out on 2025-03-29, so the default branch sits a long way past the last tag. A package manager gives you the release; a go install, a source build or a container tag can be ahead of it, and no channel between the two is named in the usage.

The project describes itself in one line: This is beta quality, with an invitation to open an issue for a feature or a bug. Licence is MIT, so the code carries no copyleft obligation for teams embedding it in their own tooling.

The image build shows the same pragmatism. The Dockerfile starts from alpine:3.21, downloads a Docker CLI at a version passed in as an argument, copies the dive binary into /usr/local/bin, and keeps alpine rather than scratch, with a comment saying users are expecting to be able to exec into it. That choice is what makes the socket-mounted container above usable as a shell when a build fails.

Editorial conclusion

dive fits whoever owns a Dockerfile and keeps wondering where the bytes went, because the layer tree, the change markers and the build command answer that inside one terminal, and CI=true runs the same analysis with no interface. Skip it if you expect the tool to hand back a smaller image, since no documented command rewrites one, and decide what to do about the efficiency score before you gate on it, because the README calls that number experimental. Two things to settle first. Check whether your podman hosts are Linux, since the podman source is documented as Linux only and the macOS path covers the Docker container engine. And decide which version you are pinning, because the last push on 2025-12-15 sits well past v0.13.1 from 2025-03-29, and a go install reports no version at all when you run dive -v.

Frequently asked questions

Does dive shrink the Docker image it inspects?

No. The documented commands analyse an image and print a report, and the build form, dive build -t <tag> ., builds and then analyses. No export or rewrite command appears in the usage.

Which image sources can dive read?

Three: docker for the engine, which is the default, docker-archive for a Docker tar archive read from disk, and podman for the Podman engine, documented as Linux only.

Why does dive -v report no version after a go install?

The README says that installing with go install means you will not see a proper version when running dive -v. Package manager installs are versioned releases, so they do not carry that gap.

Can dive use podman on macOS?

The podman source is marked Linux only, and the macOS instructions cover the Docker container engine. On a Mac without a working Docker engine, the archive source is the only documented route left.

Official sources

  1. Official README
  2. Project repository
  3. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/wagoodman-dive.svg)](https://hysenlabs.com/projects/wagoodman-dive)