Docker Image Config Viewer

Read what is in an image without running it. Everything here is visible to anybody who can pull the image, which is the point of looking.

Paste below, or drop a file anywhere on this panel

Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.

The answer appears here

Paste on the left and press Read. Nothing leaves this tab.

Examples

Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.

A token in the environment

Reported by name only, and readable by anybody who can pull the image

{
  "config": {
    "User": "app",
    "Env": ["PATH=/usr/bin", "NPM_TOKEN=REPLACE_ME"]
  },
  "rootfs": { "diff_ids": ["sha256:aaa"] }
}

An empty User

Which means root, not a default user

{
  "config": { "User": "", "Env": [] },
  "rootfs": { "diff_ids": ["sha256:aaa"] }
}

Common mistakes

These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.

  1. Putting a credential in an ENV because the image is private

    Private means fewer people can pull it, not none. Everybody who can pull can read the config, and the value is also in every container's environment.

    Instead:Pass it at run time, or use a build secret mount if it is only needed during the build.

  2. Comparing diff_ids to the manifest's layer digests

    One is uncompressed and the other compressed, so they never match. It looks exactly like a tampered image.

    Instead:Compare diff_ids with diff_ids and manifest digests with manifest digests.

  3. Leaving User unset because the app does not need root

    An unset User is root. The container runs as root whether or not the application wants to.

    Instead:Add a USER instruction after the steps that need write access to system paths.

The image config is not private

docker inspect shows it without starting a container, and a plain registry GET of the config blob shows it to anybody with pull access. There is no such thing as a secret in here.

Env is readable by everyone who can pull

Every variable and every value. There is no instruction that removes one, because the config records the final state rather than the sequence. A credential in an ENV is a published credential, and rebuilding does not un-publish an image already pushed.

diff_ids never match the manifest's layer digests

A diff_id is the digest of the uncompressed layer tar; a manifest layer digest is the digest of the compressed blob. They describe the same layer through different bytes. Comparing them to verify an image always fails and always looks like tampering, which is a very convincing false alarm.

An empty User means root

Not a default user, not nobody: root. Anything the process can reach on a mounted volume it reaches as root on the host's filesystem, and a container escape starts from a much better position.

history records every build step, including the arguments

docker history reads exactly this array. A build argument's value is recorded here whether or not the file it wrote survived, which is why deleting the file in a later layer does not help.

empty_layer marks metadata-only steps

ENV, LABEL, EXPOSE and similar change the config without producing a layer. They still appear in the history, so the number of history entries is not the number of layers.

What this cannot see

The layer contents. It reads the config, in your browser, and prints environment NAMES only, never values.

More docker tools

Docker Container Image Reference Parser Is that a registry or a Docker Hub user? Dockerfile Linter What this image will do to you later docker run to Kubernetes YAML And the flags that have no equivalent Docker Compose to Kubernetes And why depends_on has no equivalent Docker Logging Driver Generator json-file never rotates Dockerfile Non-Root USER Generator Numeric, or Kubernetes rejects it Docker OCI Image Labels Generator One of them actually does something Docker Hub Pull Rate Limit Calculator Counted per IP, not per person Docker Registry Storage Calculator Deleting a tag frees nothing Docker Build Context Analyzer All of it uploads before anything runs Docker Compose to docker run Converter Names stop resolving Docker Compose Version Migrator version: is obsolete now Docker Compose Network and Port Viewer Everything reaches everything Docker Command Explainer Which flags hand over the host Docker BuildKit Cache Mount Generator apt deletes the cache you just mounted Docker BuildKit Secret Mount Generator ARG is in docker history forever Docker .dockerignore Pattern Tester *.log does not match logs/app.log Docker Image Manifest Viewer unknown/unknown is not broken Docker Registry Token Decoder Check the scope before the credentials Docker HEALTHCHECK Generator Docker does nothing when it fails Docker Network Subnet Calculator 31 networks, then it stops Docker History Analyzer The rm did not save anything Docker Run to Compose Converter Some flags have no equivalent Docker CMD and ENTRYPOINT Tester Why docker stop takes ten seconds Docker Port Mapping Tester -p reaches past your firewall Docker -v to --mount Converter -v invents missing directories Docker Restart Policy Simulator The backoff resets after ten seconds Docker Exit Code Explainer 137 is two different problems Docker CPU Limit Converter --cpu-shares limits nothing Docker Memory Limit Calculator -m 512m allows a gigabyte Docker Compose Interpolation Tester An unset variable is an empty string Docker Environment Variable Precedence Tester Your shell does not reach the container Docker Compose Override Merge Previewer An override can add a port, never remove one Dockerfile Multi-Stage Build Analyzer Which stages reach the image Dockerignore Validator It is not .gitignore

Elsewhere on the site