Docker: How to Check and List Your Images


Mastering Your Docker Environment: A Deep Dive into Checking and Listing Images

Docker has revolutionized the way we build, ship, and run applications. Its containerization technology provides consistency across environments, simplifies deployment, and optimizes resource utilization. At the heart of Docker are Docker images – the blueprints from which containers are created. As your development workflow or production environment scales, managing these images becomes crucial. You might accumulate dozens or even hundreds of images over time, consuming valuable disk space and potentially introducing complexity or security risks if not properly managed.

Therefore, knowing how to effectively check, list, inspect, and filter your Docker images is a fundamental skill for any Docker user, from beginners to seasoned DevOps engineers. It allows you to maintain a clean system, understand your image inventory, troubleshoot issues, and ensure you’re using the correct and most secure image versions.

This comprehensive guide will delve deep into the various commands and techniques available in Docker for listing and examining your images. We will explore the primary command, docker image ls, in extensive detail, covering its basic usage, options, powerful filtering capabilities, and custom formatting features. We’ll also look at related commands like docker image inspect for detailed metadata, docker image history for layer analysis, and docker image prune for cleanup. By the end of this article, you’ll have a thorough understanding of how to navigate and manage your Docker image landscape effectively.

Table of Contents

  1. Understanding Docker Images: The Foundation
    • What is a Docker Image?
    • The Layered Filesystem
    • Images vs. Containers
    • Why Image Management Matters
  2. The Core Command: docker image ls (and docker images)
    • Basic Usage: Getting Started
    • Understanding the Default Output Columns
      • REPOSITORY
      • TAG
      • IMAGE ID
      • CREATED
      • SIZE (and what it really means)
    • Common Options and Flags
      • Listing All Images (including intermediate layers): -a or --all
      • Quiet Mode (Image IDs only): -q or --quiet
      • Showing Full Image IDs: --no-trunc
      • Displaying Image Digests: --digests
  3. Advanced Filtering with docker image ls --filter
    • Filtering Basics: The -f or --filter flag
    • Filtering by Dangling Status (dangling=true/false)
    • Filtering by Label (label=<key> or label=<key>=<value>)
    • Filtering by Creation Time (before=<image> or since=<image>)
    • Filtering by Reference (Name/Tag Patterns) (reference=<pattern>)
    • Combining Multiple Filters
    • Practical Filtering Examples
  4. Customizing Output with docker image ls --format
    • Introduction to Go Templates in Docker Formatting
    • Basic Formatting Placeholders (e.g., {{.ID}}, {{.Repository}}, {{.Tag}})
    • Creating Custom Table Outputs
    • Formatting as JSON
    • Using Functions within Templates (e.g., json, println)
    • Useful Formatting Recipes
  5. Beyond Listing: Inspecting Image Details
    • docker image inspect <image>: The Deep Dive
    • Understanding the JSON Output Structure
    • Extracting Specific Information (Layers, Environment Variables, Entrypoint, Cmd, Labels, Architecture, OS)
    • Using --format with docker image inspect
  6. Understanding Image Construction: docker image history
    • Viewing Image Layers and Build Steps
    • Interpreting the Output
    • Identifying Large Layers
    • Security Considerations
  7. Cleaning Up: Managing Your Image Inventory
    • Identifying Unused Images
    • The Concept of Dangling Images
    • Using docker image prune
      • Pruning Dangling Images
      • Pruning All Unused Images (-a)
      • Filtering Prune Operations (--filter)
      • Forcing Removal (-f)
  8. Practical Scenarios and Workflows
    • Finding Large Images Consuming Disk Space
    • Identifying Images Older Than a Certain Date
    • Listing Images for a Specific Application Using Labels
    • Scripting Image Cleanup Tasks
    • Verifying Image Configuration Before Deployment
  9. Best Practices for Image Management
    • Use Meaningful Tags
    • Regularly Prune Unused Images
    • Leverage Labels for Organization
    • Be Mindful of Image Size
    • Scan Images for Vulnerabilities (Brief Mention)
  10. Conclusion

1. Understanding Docker Images: The Foundation

Before diving into the commands, let’s briefly recap what Docker images are and why managing them is essential.

What is a Docker Image?

Think of a Docker image as a blueprint or a template for creating Docker containers. It’s a lightweight, standalone, executable package that includes everything needed to run a piece of software: the code, runtime, system tools, system libraries, and settings. Images are immutable; once built, they don’t change. If you need to make changes, you typically build a new image based on the old one with the modifications added as a new layer.

The Layered Filesystem

Docker images are built using a layered filesystem (often Union File System variants like OverlayFS). Each instruction in a Dockerfile (e.g., RUN, COPY, ADD) typically creates a new layer in the image. These layers are stacked on top of each other. This layered approach has several benefits:

  • Efficiency: Layers are cached. If you change a later step in your Dockerfile, Docker only rebuilds the changed layer and subsequent layers, reusing unchanged layers from the cache.
  • Sharing: Different images can share common base layers. For example, multiple Python application images might share the same base Python runtime layer, saving disk space and download time.

Images vs. Containers

It’s crucial to understand the distinction:

  • Image: The immutable blueprint/template (e.g., python:3.9-slim).
  • Container: A runnable instance of an image. It adds a thin writable layer on top of the image layers. You can start, stop, move, and delete containers. Multiple containers can be run from the same image.

Why Image Management Matters

As you pull images from registries like Docker Hub or build your own custom images, your local storage fills up. Effective image management is vital for:

  • Disk Space: Images, especially those with large base layers or many dependencies, can consume significant disk space. Regularly listing and cleaning up unused images is essential.
  • Clarity & Organization: Knowing exactly which images (and which versions/tags) you have helps avoid confusion and ensures you’re using the intended blueprints for your containers.
  • Security: Old images might contain unpatched vulnerabilities. Listing images allows you to identify potentially outdated versions that need updating or removal.
  • Troubleshooting: When diagnosing container issues, examining the image’s history or metadata can provide valuable clues.
  • Workflow Efficiency: Quickly finding the right image or identifying intermediate build layers speeds up development and deployment processes.

2. The Core Command: docker image ls (and docker images)

The primary command for listing images stored locally on your Docker host is docker image ls.

You might also encounter the older, shorter alias docker images. Functionally, docker images is identical to docker image ls. Docker introduced the newer syntax (docker <object> <verb>, e.g., docker image ls, docker container run) for better organization and consistency across its command-line interface (CLI). While docker images still works perfectly fine and is widely used, it’s generally recommended to adopt the newer docker image ls syntax for consistency and future compatibility. Throughout this guide, we will primarily use docker image ls, but remember that docker images can be used interchangeably in most contexts.

Basic Usage: Getting Started

Simply running the command without any options will list the most relevant images on your system:

bash
docker image ls

or

bash
docker images

Understanding the Default Output Columns

The default output provides a concise overview of your top-level images (images with a repository name and tag, or recently built images without a tag):

REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest a9dee87a005e 3 weeks ago 77.8MB
python 3.9-slim f1a35a6d4d5e 4 weeks ago 114MB
nginx stable-alpine c316d156e457 2 months ago 23.5MB
my-app v1.2 b8fca9a0e9c7 2 days ago 350MB
<none> <none> d1ecca7a9a0a 5 hours ago 345MB

Let’s break down each column:

  • REPOSITORY: This is the name of the image repository. It often indicates the base application or service (e.g., ubuntu, nginx, python). For images pulled from registries like Docker Hub, it might include the username or organization (e.g., bitnami/redis). If an image was built locally without being tagged with a repository name, or if its tag was overwritten by a newer build of the same name:tag, it might show <none>.
  • TAG: Tags are labels used to differentiate versions or variants of an image within a repository (e.g., latest, 3.9-slim, stable-alpine, v1.2). The latest tag is a convention, often pointing to the most recent stable build, but its meaning is defined by the repository maintainer – it doesn’t automatically mean the newest version exists. An image can have multiple tags pointing to the same underlying Image ID. If an image lacks a tag (often seen in intermediate build layers or images where the tag was removed), it will display <none>. Images listed as <none>:<none> are often referred to as “dangling” images (more on this later).
  • IMAGE ID: This is a unique identifier for the image, represented as a 64-character hexadecimal SHA256 hash of the image’s configuration and layer content. By default, Docker displays a truncated, 12-character short ID for brevity. This ID is the definitive way to refer to a specific image, regardless of its tags.
  • CREATED: This timestamp indicates when the image was built or, more accurately, when the image’s configuration manifest was created. It’s usually displayed in a human-readable relative format (e.g., “3 weeks ago”, “5 hours ago”).
  • SIZE: This shows the virtual size of the image. This is a crucial point: The size reported is the sum of the sizes of the image’s unique layers plus the sizes of any shared base layers. This means the total disk space consumed by all your images might be less than the sum of the sizes reported by docker image ls, thanks to layer sharing. For example, if you have ten different application images all based on ubuntu:latest, the base Ubuntu layers are only stored once on disk, but their size contributes to the reported size of each of the ten images.

Common Options and Flags

The basic docker image ls command can be modified with several useful flags:

Listing All Images (including intermediate layers): -a or --all

By default, docker image ls hides intermediate image layers that were created during builds but don’t have a user-assigned repository and tag. These layers are often dependencies for other tagged images. To see all images, including these intermediate ones, use the -a or --all flag:

bash
docker image ls -a

This output will often include many entries with <none> in both the REPOSITORY and TAG columns. These aren’t necessarily all “dangling” (unused) if they are parent layers for other tagged images.

Quiet Mode (Image IDs only): -q or --quiet

Sometimes, you only need the Image IDs, perhaps for scripting purposes (e.g., passing IDs to another command like docker image rm or docker image inspect). The -q or --quiet flag suppresses the header and all columns except the Image ID:

bash
docker image ls -q

Output:

a9dee87a005e
f1a35a6d4d5e
c316d156e457
b8fca9a0e9c7
d1ecca7a9a0a

Combined with -a, docker image ls -aq will list the IDs of all images, including intermediate ones.

Showing Full Image IDs: --no-trunc

The default 12-character short Image ID is usually sufficient for identification, but occasionally you might need the full 64-character SHA256 hash. The --no-trunc flag prevents truncation of the output, primarily affecting the IMAGE ID column:

bash
docker image ls --no-trunc

Output (IMAGE ID column shown):

REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest sha256:a9dee87a005e8940f0a113a601b1a973a61f8786159f8065776a480689c17a3c 3 weeks ago 77.8MB
python 3.9-slim sha256:f1a35a6d4d5e0b0a0b6f6d3e0d8e0c7f8e1b8a1b7f8e1b8a1b7f8e1b8a1b7f8e 4 weeks ago 114MB
...

Note: The sha256: prefix clearly indicates the hashing algorithm used for the ID.

Displaying Image Digests: --digests

While tags are mutable (e.g., ubuntu:latest can point to different Image IDs over time), an image digest is an immutable, content-addressable identifier (a SHA256 hash of the image manifest). It guarantees you are referring to the exact same image content. The --digests flag adds a DIGEST column to the output:

bash
docker image ls --digests

Output:

REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
ubuntu latest sha256:a1689773dac93539d4e1647a5e79c1acaa060581ff11e55c1511a3b3a4a5f6d9 a9dee87a005e 3 weeks ago 77.8MB
python 3.9-slim sha256:b123... f1a35a6d4d5e 4 weeks ago 114MB
nginx stable-alpine <none> c316d156e457 2 months ago 23.5MB
...

Note that locally built images might show <none> in the DIGEST column until they are pushed to a registry, as the digest is typically calculated and associated during the push process based on the image manifest. Images pulled from a registry usually have a digest. Using digests (repository@digest) is the most reliable way to ensure you are using a specific, unchanging version of an image.

3. Advanced Filtering with docker image ls --filter

Listing all images is often overwhelming. Docker provides a powerful filtering mechanism using the -f or --filter flag. You can apply one or more filters to narrow down the list based on specific criteria.

Filtering Basics: The -f or --filter flag

The syntax is docker image ls --filter <key>=<value>. You can repeat the --filter flag multiple times to apply several conditions (these conditions are usually combined with an AND logic).

“`bash

General syntax

docker image ls –filter key=value

Example: Filter by label

docker image ls –filter label=maintainer=devteam

Example: Multiple filters (images created since ‘myimage:latest’ AND dangling)

docker image ls –filter since=myimage:latest –filter dangling=true
“`

Filtering by Dangling Status (dangling=true/false)

Dangling images are layers that are no longer referenced by any tagged image. They often result from rebuilding an image with the same name and tag – the old image layers become dangling. These are usually safe to remove to reclaim disk space.

  • List only dangling images:
    bash
    docker image ls --filter dangling=true
    # Alias: docker image ls -f dangling=true

    This will typically show images with <none> in both REPOSITORY and TAG.

  • List only non-dangling images:
    bash
    docker image ls --filter dangling=false

    This usually approximates the default docker image ls output but explicitly excludes any images Docker considers dangling.

Filtering by Label (label=<key> or label=<key>=<value>)

Images can be built with metadata labels (using the LABEL instruction in the Dockerfile). These are key-value pairs useful for organization, automation, or providing information.

  • List images with a specific label key (regardless of value):
    bash
    # Find images that have a 'com.example.project' label
    docker image ls --filter label=com.example.project

  • List images with a specific label key and value:
    “`bash
    # Find images with the label ‘stage=production’
    docker image ls –filter label=stage=production

    Find images maintained by ‘[email protected]

    docker image ls –filter [email protected]
    “`

Filtering by Creation Time (before=<image> or since=<image>)

You can filter images based on their creation time relative to another image. The reference image can be specified by its ID, name, or name:tag.

  • List images created before a specific image:
    “`bash
    # List images created before ‘my-app:v1.2’
    docker image ls –filter before=my-app:v1.2

    List images created before image ID ‘b8fca9a0e9c7’

    docker image ls –filter before=b8fca9a0e9c7
    “`

  • List images created since a specific image:
    “`bash
    # List images created since ‘my-app:v1.0’
    docker image ls –filter since=my-app:v1.0

    List images created since image ID ‘abcdef123456’

    docker image ls –filter since=abcdef123456
    “`
    This is useful for finding newer versions or images built after a certain baseline.

Filtering by Reference (Name/Tag Patterns) (reference=<pattern>)

This filter allows you to list images whose repository name and/or tag match a given pattern. It supports standard shell globbing characters like * (matches any sequence of characters) and ? (matches any single character).

  • List all images for a specific repository:
    bash
    # List all 'nginx' images (nginx:latest, nginx:stable-alpine, etc.)
    docker image ls --filter reference=nginx

  • List images matching a pattern:
    “`bash
    # List all images ending with ‘-alpine’ tag in any repository
    docker image ls –filter reference=’/-alpine’ # Needs quotes for the shell
    docker image ls –filter reference=’:-alpine’ # Or use : separator

    List all images in the ‘my-app’ repository

    docker image ls –filter reference=’my-app’

    List all ‘my-app’ images with tags starting with ‘v1.’

    docker image ls –filter reference=’my-app:v1.?*’

    List all official images (those without a ‘/’ in the repository name)

    This is a bit tricky, might need external tools or formatting,

    but you can approximate by filtering out common user/org patterns if known.

    A simple pattern for images with potentially ‘latest’ tag or version tags:

    docker image ls –filter reference=’:.‘ –filter reference=’:latest’ # Imperfect

    List images from a specific user/organization on Docker Hub

    docker image ls –filter reference=’bitnami/
    ``
    **Important:** Remember to quote patterns containing wildcards (
    ,?`) to prevent your shell from expanding them before Docker sees them.

Combining Multiple Filters

As mentioned, you can use multiple --filter flags. Docker applies them conjunctively (AND logic).

  • List dangling images created before my-app:v1.0:
    bash
    docker image ls --filter dangling=true --filter before=my-app:v1.0

  • List images with label stage=testing belonging to the my-api repository:
    bash
    docker image ls --filter label=stage=testing --filter reference='my-api:*'

Practical Filtering Examples

  • Find all Python 3 images: docker image ls --filter reference='python:3.*'
  • Find potentially old Nginx images (created before nginx:stable): docker image ls --filter reference='nginx' --filter before=nginx:stable
  • Find all images tagged latest: docker image ls --filter reference='*:latest'
  • Find images labeled for a specific project: docker image ls --filter label=project=phoenix

Filtering is incredibly powerful for managing large numbers of images and automating tasks.

4. Customizing Output with docker image ls --format

While the default table output of docker image ls is useful for humans, sometimes you need more control over what information is displayed and how it’s formatted, especially for scripting or reporting. The --format flag allows you to specify a custom output format using Go templates.

Introduction to Go Templates in Docker Formatting

Go templates provide a simple way to structure text output. They work with placeholders enclosed in double curly braces {{ }}. Inside the braces, you refer to data fields of the object being processed (in this case, a Docker image object). A dot (.) represents the current object.

Basic Formatting Placeholders

Here are some common placeholders available for docker image ls --format:

  • {{.ID}}: The full Image ID (SHA256 hash).
  • {{.Repository}}: The image repository name.
  • {{.Tag}}: The image tag.
  • {{.Digest}}: The image digest (if available).
  • {{.CreatedSince}}: Human-readable time since creation (e.g., “3 weeks ago”).
  • {{.CreatedAt}}: Timestamp of creation in a standard format.
  • {{.Size}}: Human-readable image size (e.g., “77.8MB”).
  • {{.VirtualSize}}: Image size in bytes (less commonly used directly than .Size).
  • {{.Containers}}: Number of containers using this image (often 0 unless specifically tracking).

Creating Custom Table Outputs

You can mimic the default table or create entirely new ones. Use tabs (\t) to separate columns and newlines (\n) to separate rows.

  • Display only Repository and Tag:
    bash
    docker image ls --format "{{.Repository}}:{{.Tag}}"

    Output:
    ubuntu:latest
    python:3.9-slim
    nginx:stable-alpine
    my-app:v1.2
    <none>:<none>

  • Display Image ID, Repository, and Size:
    bash
    # Add a header manually using echo/printf or within the template
    docker image ls --format "table {{.ID}}\t{{.Repository}}:{{.Tag}}\t{{.Size}}"

    Output (the table keyword adds headers automatically):
    IMAGE ID REPOSITORY:TAG SIZE
    a9dee87a005e ubuntu:latest 77.8MB
    f1a35a6d4d5e python:3.9-slim 114MB
    c316d156e457 nginx:stable-alpine 23.5MB
    b8fca9a0e9c7 my-app:v1.2 350MB
    d1ecca7a9a0a <none>:<none> 345MB

    Note: The table directive automatically takes the first row of placeholders as headers.

  • Custom Table with Specific Columns and Formatting:
    bash
    docker image ls --format "table {{.Repository}}\t{{.Tag}}\t{{.CreatedSince}}"

    Output:
    REPOSITORY TAG CREATED
    ubuntu latest 3 weeks ago
    python 3.9-slim 4 weeks ago
    nginx stable-alpine 2 months ago
    my-app v1.2 2 days ago
    <none> <none> 5 hours ago

Formatting as JSON

You can output the image data as JSON, which is extremely useful for processing with tools like jq or in automated scripts.

bash
docker image ls --format "{{json .}}"

Output (one JSON object per line):

json
{"Containers":0,"CreatedAt":"2023-10-15 10:00:00 +0000 UTC","CreatedSince":"3 weeks ago","Digest":"sha256:a168...","ID":"a9dee87a005e","Repository":"ubuntu","SharedSize":0,"Size":"77.8MB","Tag":"latest","UniqueSize":0,"VirtualSize":77800000}
{"Containers":0,"CreatedAt":"2023-10-01 12:00:00 +0000 UTC","CreatedSince":"4 weeks ago","Digest":"sha256:b123...","ID":"f1a35a6d4d5e","Repository":"python","SharedSize":0,"Size":"114MB","Tag":"3.9-slim","UniqueSize":0,"VirtualSize":114000000}
...

Using Functions within Templates

Go templates support simple functions. The json function used above is one example. Another is println.

“`bash

Print each repository:tag on a new line (similar to the earlier example)

docker image ls –format “{{println .Repository}}:{{.Tag}}”
“`

Useful Formatting Recipes

  • Get Image IDs and their sizes:
    bash
    docker image ls --format "{{.ID}}: {{.Size}}"

  • List Repository:Tag@Digest for precise identification:
    bash
    # Filter out those without digests if needed
    docker image ls --digests --format "{{.Repository}}:{{.Tag}}@{{.Digest}}" | grep -v '@<none>'

  • CSV Output (ID, Repo, Tag, Size):
    bash
    docker image ls --format '"{{.ID}}","{{.Repository}}","{{.Tag}}","{{.Size}}"'

The --format option transforms docker image ls from a simple listing command into a flexible data extraction tool.

5. Beyond Listing: Inspecting Image Details

While docker image ls provides a summary, sometimes you need detailed information about a specific image. This is where docker image inspect comes in.

docker image inspect <image>: The Deep Dive

This command takes one or more image IDs, names, or name:tag references and outputs detailed information about them in JSON format by default.

bash
docker image inspect python:3.9-slim

or

bash
docker image inspect f1a35a6d4d5e # Using Image ID

Understanding the JSON Output Structure

The output is an array of JSON objects, one for each image inspected. The object contains a wealth of information, including:

  • Id: The full SHA256 Image ID.
  • RepoTags: List of repository:tag combinations associated with this image.
  • RepoDigests: List of repository@digest combinations.
  • Parent: ID of the parent image (if applicable).
  • Comment: Comment associated with the image (often empty).
  • Created: Timestamp of image creation.
  • Container: ID of the container used to build this image (if built from a container).
  • ContainerConfig: Configuration of the container used for the build.
  • DockerVersion: Version of Docker used to build the image.
  • Author: Author information (from MAINTAINER in Dockerfile, deprecated in favor of LABEL).
  • Config: The configuration that will be applied when running a container from this image. This is a crucial section containing:
    • Hostname, Domainname, User, AttachStdin, AttachStdout, AttachStderr, Tty, OpenStdin, StdinOnce
    • Env: List of environment variables (e.g., ["PATH=/usr/local/bin:..."]).
    • Cmd: Default command to execute when a container starts.
    • Entrypoint: Default entrypoint for the container.
    • Labels: Map of labels applied to the image.
    • WorkingDir: Default working directory inside the container.
    • ExposedPorts: Ports exposed by the image (e.g., {"80/tcp": {}}).
  • Architecture: CPU architecture the image is built for (e.g., amd64, arm64).
  • Os: Operating system the image is built for (e.g., linux).
  • Size, VirtualSize: Image size information.
  • GraphDriver: Information about the storage driver and layers.
    • Data: Contains layer metadata.
    • Name: Storage driver name (e.g., overlay2).
  • RootFS: Information about the image’s root filesystem, including:
    • Type: Typically layers.
    • Layers: An ordered list of SHA256 diff identifiers for each layer.

Extracting Specific Information

Manually parsing the large JSON output can be tedious. You have two primary ways to extract specific data points:

  1. Using jq (External Tool): jq is a powerful command-line JSON processor.
    “`bash
    # Get the default command (Cmd)
    docker image inspect python:3.9-slim | jq ‘.[0].Config.Cmd’

    Get all environment variables

    docker image inspect my-app:latest | jq ‘.[0].Config.Env’

    Get the architecture

    docker image inspect nginx | jq ‘.[0].Architecture’

    Get all labels

    docker image inspect my-custom-image | jq ‘.[0].Config.Labels’
    “`

  2. Using --format with docker image inspect: Similar to docker image ls, inspect also supports the --format flag with Go templates. This is often simpler for extracting single values or simple structures directly within Docker.
    “`bash
    # Get the OS
    docker image inspect python:3.9-slim –format ‘{{.Os}}’
    # Output: linux

    Get the Entrypoint (might be an array, Go template handles it)

    docker image inspect my-app –format ‘{{.Config.Entrypoint}}’

    Output: [java -jar /app/app.jar]

    Get a specific label value

    docker image inspect my-app –format ‘{{index .Config.Labels “com.example.version”}}’

    Output: v1.2.3

    Get all environment variables, one per line

    docker image inspect my-app –format ‘{{range .Config.Env}}{{println .}}{{end}}’

    Output:

    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

    JAVA_HOME=/opt/java/openjdk

    Get the list of layer diff IDs

    docker image inspect ubuntu:latest –format ‘{{json .RootFS.Layers}}’

    Output: [“sha256:…”, “sha256:…”, …]

    “`

docker image inspect is indispensable for understanding the exact configuration, contents, and provenance of an image.

6. Understanding Image Construction: docker image history

To understand how an image was built and see the individual layers contributing to its size, use the docker image history command.

Viewing Image Layers and Build Steps

bash
docker image history <image>

Example:

bash
docker image history python:3.9-slim

Output (simplified and potentially reordered for clarity):

IMAGE CREATED CREATED BY SIZE COMMENT
f1a35a6d4d5e 4 weeks ago /bin/sh -c #(nop) CMD ["python3"] 0B buildkit.dockerfile.v0
<missing> 4 weeks ago /bin/sh -c set -eux; wget -O get-pip.py ... 7.8MB buildkit.dockerfile.v0
<missing> 4 weeks ago /bin/sh -c #(nop) ENV PYTHON_GET_PIP_UR... 0B buildkit.dockerfile.v0
<missing> 4 weeks ago /bin/sh -c #(nop) ENV PYTHON_SETUPTOO... 0B buildkit.dockerfile.v0
<missing> 4 weeks ago /bin/sh -c #(nop) ENV PYTHON_PIP_VERS... 0B buildkit.dockerfile.v0
<missing> 4 weeks ago /bin/sh -c cd /usr/local/bin && ln -s ... 32B buildkit.dockerfile.v0
<missing> 4 weeks ago /bin/sh -c set -eux; apt-get update; ... 45MB buildkit.dockerfile.v0
<missing> 4 weeks ago /bin/sh -c #(nop) ENV PYTHON_VERSION=3.9.7 0B buildkit.dockerfile.v0
<missing> 4 weeks ago /bin/sh -c #(nop) ENV GPG_KEY=E3FF2839... 0B buildkit.dockerfile.v0
<missing> 4 weeks ago /bin/sh -c #(nop) ENV LANG=C.UTF-8 0B buildkit.dockerfile.v0
<missing> 4 weeks ago /bin/sh -c #(nop) ENV PATH=/usr/local/bi... 0B buildkit.dockerfile.v0
<missing> 5 weeks ago /bin/sh -c set -eux; apt-get update; ... 52MB buildkit.dockerfile.v0
<missing> 5 weeks ago /bin/sh -c #(nop) WORKDIR / 0B buildkit.dockerfile.v0
<missing> 5 weeks ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 5 weeks ago /bin/sh -c #(nop) ADD file:abc... in / 77.8MB (This is the base image layer)

Interpreting the Output

  • IMAGE: The ID of the layer. The top one corresponds to the final image ID. <missing> indicates that the layer exists locally but doesn’t have its own tag (it’s an intermediate layer). Docker might not have the history for base image layers if they weren’t built locally or pulled with full provenance.
  • CREATED: When the layer was created.
  • CREATED BY: The Dockerfile instruction (or command) that created this layer. #(nop) usually indicates metadata instructions like ENV, LABEL, CMD, WORKDIR, EXPOSE, MAINTAINER which don’t add files but modify the image configuration. Commands starting with /bin/sh -c typically correspond to RUN instructions. ADD or COPY instructions will also be shown.
  • SIZE: The size of this specific layer (the files added or changed by this step). This is very useful for identifying which build steps contribute most to the final image size. Note that a 0B size often corresponds to metadata changes.
  • COMMENT: Optional comment, often empty or used by build systems (like buildkit.dockerfile.v0).

Identifying Large Layers

By examining the SIZE column in the history output, you can pinpoint which RUN, COPY, or ADD commands in your Dockerfile are adding the most data. This is crucial for optimizing image size, perhaps by combining RUN commands, removing unnecessary files within the same RUN command, or using multi-stage builds.

Security Considerations

The CREATED BY column can sometimes reveal sensitive information if commands included secrets directly (which is bad practice). It also shows the exact commands run, which can be useful for understanding potential vulnerabilities introduced during the build process.

You can use --no-trunc with docker image history to see the full CREATED BY commands if they are truncated.

7. Cleaning Up: Managing Your Image Inventory

As established, images consume disk space. Regularly cleaning up unnecessary images is good practice.

Identifying Unused Images

There are two main categories of “unused” images:

  1. Dangling Images: These are image layers that are not associated with any tagged image. They often result from rebuilding an image without removing the old one first. They are generally safe to remove.
  2. Unused Non-Dangling Images: These are tagged images (e.g., my-app:v1.1, old-ubuntu:18.04) that are no longer referenced by any container (stopped or running). You might keep them intentionally, but often they represent old versions you no longer need.

The Concept of Dangling Images

You can specifically list dangling images using the filter we saw earlier:

bash
docker image ls --filter dangling=true

Using docker image prune

Docker provides a convenient command, docker image prune, to remove unused images.

Pruning Dangling Images

By default, without any flags, docker image prune only removes dangling images:

bash
docker image prune

Docker will ask for confirmation before proceeding:

WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N]

Enter y to confirm. It will then list the deleted image IDs and the total space reclaimed.

Pruning All Unused Images (-a)

To remove all images that are not associated with at least one container (this includes dangling images and tagged images not used by any container), use the -a flag:

bash
docker image prune -a

Caution: This is more aggressive. It will remove any tagged image (like ubuntu:latest, my-app:v1.0) if no container (running or stopped) is currently based on it. Be sure you don’t need these images before running this command. It will also ask for confirmation.

Filtering Prune Operations (--filter)

You can apply filters to the prune command, similar to ls, to be more selective. A common use case is to remove images older than a certain time. The filter key here is until.

“`bash

Remove images (dangling or unused -a) created more than 24 hours ago

docker image prune -a –filter “until=24h”

Remove images created more than 30 days (approx) ago

You might need to calculate the duration string, e.g., 30*24 = 720h

docker image prune -a –filter “until=720h”

Remove images with a specific label

docker image prune –filter “label=stage=temporary”
``
The
until=filter removes images created *before* the specified duration. You can use hours (h), minutes (m`).

Forcing Removal (-f)

To bypass the confirmation prompt (useful in scripts), use the -f or --force flag:

“`bash

Force remove all dangling images without prompting

docker image prune -f

Force remove all unused images without prompting

docker image prune -a -f
``
Use
-fwith caution, especially with-a`.

8. Practical Scenarios and Workflows

Let’s combine these commands to solve common problems:

Finding Large Images Consuming Disk Space

“`bash

List images sorted by size (requires external sort command)

docker image ls –format “table {{.Repository}}:{{.Tag}}\t{{.Size}}” | sort -k2 -h -r

Or using Go template sorting (less common, might need complex templates)

Inspect the history of a large image to find large layers

docker image inspect my-large-image:latest –format ‘{{.Size}}’ # Get size first
docker image history my-large-image:latest
“`

Identifying Images Older Than a Certain Date

“`bash

Prune images older than 30 days (720 hours)

docker image prune -a -f –filter “until=720h”

List images created since a specific baseline image (potentially for cleanup review)

docker image ls –filter since=my-baseline-image:v1.0
“`

Listing Images for a Specific Application Using Labels

“`bash

List all production images for ‘project-alpha’

docker image ls –filter label=project=project-alpha –filter label=stage=production

List all images maintained by the core team

docker image ls –filter label=team=core
“`

Scripting Image Cleanup Tasks

“`bash

!/bin/bash

echo “Removing dangling images…”
docker image prune -f

echo “Removing images older than 168 hours (7 days)…”
docker image prune -a -f –filter “until=168h”

echo “Listing remaining images…”
docker image ls
“`

Verifying Image Configuration Before Deployment

“`bash

Check exposed ports for the webapp image

docker image inspect my-webapp:prod –format ‘{{json .Config.ExposedPorts}}’

Check environment variables

docker image inspect my-webapp:prod –format ‘{{range .Config.Env}}{{println .}}{{end}}’

Check the entrypoint and command

docker image inspect my-webapp:prod –format ‘Entrypoint: {{.Config.Entrypoint}} | Cmd: {{.Config.Cmd}}’

Check the target architecture

docker image inspect my-webapp:prod –format ‘{{.Architecture}}/{{.Os}}’
“`

9. Best Practices for Image Management

  • Use Meaningful Tags: Avoid relying solely on latest. Use semantic versioning (e.g., v1.2.3) or Git commit hashes for tags to ensure reproducibility and clarity.
  • Regularly Prune Unused Images: Set up scheduled tasks or manually run docker image prune periodically to reclaim disk space and remove clutter. Be cautious with prune -a.
  • Leverage Labels for Organization: Use labels (LABEL in Dockerfile) to categorize images by project, stage (dev/test/prod), maintainer, version, etc. This greatly aids filtering.
  • Be Mindful of Image Size: Optimize Dockerfiles to create smaller images (use smaller base images like Alpine, combine RUN commands, use multi-stage builds, clean up temporary files within the same RUN layer). Use docker image history to identify large layers.
  • Scan Images for Vulnerabilities: While not covered in detail here, use tools like Docker Scout, Trivy, or Snyk to scan your images for known security vulnerabilities before deploying them. Listing images helps identify which ones need scanning or updating.

10. Conclusion

Docker images are the cornerstone of containerization, but they require diligent management. Knowing how to effectively list, filter, inspect, and clean up your images is crucial for maintaining a healthy, secure, and efficient Docker environment.

We’ve explored the fundamental docker image ls command in depth, from its basic usage and output columns to its powerful --filter and --format options that enable precise querying and custom output. We also covered docker image inspect for deep dives into image metadata, docker image history for understanding layer composition and size, and docker image prune for essential cleanup tasks.

By mastering these commands and adopting best practices like meaningful tagging, labeling, regular pruning, and size optimization, you gain full control over your image inventory. This allows you to save disk space, enhance security, streamline workflows, and ultimately leverage the full power of Docker with confidence. Keep exploring, keep building, and keep your image cache clean!


Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top