1. Introduction
In a previous article, we explained the difference between Docker images and Docker containers. In short: An image is like a Java class, and containers are like Java objects.
In this tutorial, we'll look at the various ways of removing Docker images.
2. Why Remove Docker Images?
The Docker Engine stores images and runs containers. For that purpose, the Docker Engine reserves a certain amount of disk space as a “storage pool” for images, containers, and everything else (such as global Docker volumes or networks).
Once that storage pool is full, the Docker Engine stops working: We can't create or download new images anymore, and our containers fail to run.
Docker images take up the majority of the Docker Engine storage pool. So we remove Docker images to keep Docker running.
We also remove images to keep our Docker Engine organized and clean. For instance, we can easily create dozens of images during development that we soon don't need anymore. Or, we download some software images for testing that we can dispose of later.
We can easily remove a Docker image that we pulled from a Docker repository: If we ever need it again, we'll just pull it from the repository once more.
But we have to be careful with Docker Images we created ourselves: Once removed, our own images are gone unless we saved them! We can save Docker images by pushing them to a repository or exporting them to a TAR file.
3. Downloading PostgreSQL 13 Beta Images
PostgreSQL is an open-source relational database. We'll use the first two PostgreSQL 13 beta Docker images as examples. These two images are relatively small, so we can download them quickly. And because they are beta software, we don't have them in our Docker Engine already.
We'll use the beta 2 image to create a container. We won't use the beta 1 image directly.
But before we download these two images, let's check first how much space Docker images take up in the storage pool:
docker system df --format 'table {{.Type}}\t{{.TotalCount}}\t{{.Size}}'
Here's the output from a test machine. The first line shows that our 71 Docker images use 7.8 GB:
TYPE TOTAL SIZE
Images 71 7.813GB
Containers 1 359.1MB
Local Volumes 203 14.54GB
Build Cache 770 31.54GB
Now we download the two PostgreSQL images and recheck the Docker storage pool:
docker pull postgres:13-beta1-alpine
docker pull postgres:13-beta2-alpine
docker system df --format 'table {{.Type}}\t{{.TotalCount}}\t{{.Size}}'
As expected, the number of images increased from 71 to 73. And the overall image size went from 7.8 GB to 8.1 GB.
We'll just show the first line for brevity:
TYPE TOTAL SIZE
Images 73 8.119GB
4. Removing a Single Image
Let's start a container with the PostgreSQL 13 beta 2 image. We set secr3t as the password for the database root user because the PostgreSQL container won't start without one:
docker run -d -e POSTGRES_PASSWORD=secr3t postgres:13-beta2-alpine
docker ps --format 'table {{.ID}}\t{{.Image}}\t{{.Status}}'
Here is the running container on the test machine:
CONTAINER ID IMAGE STATUS
527bfd4cfb89 postgres:13-beta2-alpine Up Less than a second
Now let's remove the PostgreSQL 13 beta 2 image. We use docker image rm to remove a Docker image. That command removes one or more images:
docker image rm postgres:13-beta2-alpine
This command fails because a running container still uses that image:
Error response from daemon: conflict: unable to remove repository reference "postgres:13-beta2-alpine" (must force) - container 527bfd4cfb89 is using its referenced image cac2ee40fa5a
So let's stop that running container by using its ID, which we obtained from docker ps:
docker container stop 527bfd4cfb89
We now try to remove the image again – and get the same error message: We can't remove an image used by a container, running or not.
So let's remove the container. Then we can finally remove the image:
docker container rm 527bfd4cfb89
docker image rm postgres:13-beta2-alpine
The Docker Engine prints details of the image removal:
Untagged: postgres:13-beta2-alpine
Untagged: postgres@sha256:b3a4ebdb37b892696a7bd7e05763b938345f29a7327fc17049c7148c03ff6a92
removed: sha256:cac2ee40fa5a40f0abe53e0138033fe7a9bcee28e7fb6c9eaac4d3a2076b1a86
removed: sha256:6a14bab707274a8007da33fe08ea56a921f356263d8fd5e599273c7ee4880170
removed: sha256:5e6ef40b9f6f8802452dbca622e498caa460736d890ca20011e7c79de02adf28
removed: sha256:dbd38ed4b347c7f3c81328742a1ddeb1872ad52ac3b1db034e41aa71c0d55a75
removed: sha256:23639f6bd6ab4b786e23d9d7c02a66db6d55035ab3ad8f7ecdb9b1ad6efeec74
removed: sha256:8294c0a7818c9a435b8908a3bcccbc2171c5cefa7f4f378ad23f40e28ad2f843
The docker system df confirms the removal: The number of images decreased from 73 to 72. And the overall image size went from 8.1 GB to 8.0 GB:
TYPE TOTAL SIZE
Images 72 7.966GB
5. Removing Multiple Images by Name
Let's download the PostgreSQL 13 beta 2 image again that we just removed in the previous section:
docker pull postgres:13-beta2-alpine
Now we want to remove both the beta 1 image and the beta 2 image by name. We've only used the beta 2 image so far. As mentioned earlier, we're not using the beta 1 image directly, so we can just remove it now.
Unfortunately, docker image rm doesn't offer a filter option for removing by name. Instead, we'll chain Linux commands to remove multiple images by name.
We'll reference images by repository and tag, like in a docker pull command: The repository is postgres, the labels are 13-beta1-alpine and 13-beta2-alpine.
So, to remove multiple images by name, we need to:
- List all images by repository and tag, such as postgres:13-beta2-alpine
- Then, filter those output lines through a regular expression with the grep command: ^postgres:13-beta
- And finally, feed those lines to the docker image rm command
Let's start putting these together. To test for correctness, let's run just the first two of these pieces:
docker image ls --format '{{.Repository}}:{{.Tag}}' | grep '^postgres:13-beta'
And on our test machine we get:
postgres:13-beta2-alpine
postgres:13-beta1-alpine
Now given that, we can add it to our docker image rm command:
docker image rm $(docker image ls --format '{{.Repository}}:{{.Tag}}' | grep '^postgres:13-beta')
As before, we can only remove images if no container, running or stopped, uses them. We then see the same image removal details as in the previous section. And docker system df shows that we're back to 71 images at 7.8 GB on the test machine:
TYPE TOTAL SIZE
Images 71 7.813GB
This image removal command works in a terminal on Linux and Mac. On Windows, it requires the “Docker Quickstart Terminal” of the Docker Toolbox. In the future, the more recent Docker Desktop for Windows may work with this Linux command on Windows 10, too.
6. Removing Images by Size
An excellent way to save disk space is to remove the largest Docker images first.
Now docker image ls can't sort by size, either. So, we list all images and sort that output with the sort command to view images by size:
docker image ls | sort -k7 -h -r
which on our test machine outputs:
collabora/code 4.2.5.3 8ae6850294e5 3 weeks ago 1.28GB
nextcloud 19.0.1-apache 25b6e2f7e916 6 days ago 752MB
nextcloud latest 6375cff75f7b 5 weeks ago 750MB
nextcloud 19.0.0-apache 5c44e8445287 7 days ago 750MB
Next, we manually review to find what we want to remove. The ID, column three, is easier to copy and paste than the repository and tag, columns one and two. Docker allows removing multiple images in one go.
Let's say we want to remove nextcloud:latest and nextcloud:19.0.0-apache. Simply put, we can look at their corresponding IDs in our table and list them in our docker image rm command:
docker image rm 6375cff75f7b 5c44e8445287
As before, we can only remove images not used by any container and see the usual image removal details. Now we're down to 69 images at 7.1 GB on our test machine:
TYPE TOTAL SIZE
Images 69 7.128GB
7. Removing Images by Creation Date
Docker can remove images by their creation date. We'll use the new docker image prune command for that. Unlike docker image rm, it is designed to remove multiple images or even all images.
Now, let's remove all images created before July 7, 2020:
docker image prune -a --force --filter "until=2020-07-07T00:00:00"
We still can only remove images not used by any container, and we still see the usual image removal details. This command removed two images on the test machine, so we're at 67 images and 5.7 GB on the test machine:
TYPE TOTAL SIZE
Images 67 5.686GB
Another way to remove images by their creation date is to specify a time span instead of a cut-off date. Let's say we wanted to remove all images older than a week:
docker image prune -a --force --filter "until=168h"
Note that the Docker filter option requires us to convert that time span into hours.
8. Pruning Containers and Images
docker image prune bulk-removes unused images. It goes hand-in-hand with docker container prune, which bulk-removes stopped containers. Let's start with that last command:
docker container prune
This prints a warning message. We have to enter y and press Enter to proceed:
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
removed Containers:
1c3be3eba8837323820ecac5b82e84ab65ad6d24a259374d354fd561254fd12f
Total reclaimed space: 359.1MB
So on the test machine, this removed one stopped container.
Now we need need to discuss image relationships briefly. Our Docker images extend other images to gain their functionality, just as Java classes extend other Java classes.
Let's look at the top of the Dockerfile for the PostgreSQL beta 2 image to see what image it's extending:
FROM alpine:3.12
So the beta 2 image uses alpine:3.12. That's why Docker implicitly downloaded alpine:3.12 when we pulled the beta 2 image at first. We don't see these implicitly downloaded images with docker image ls.
Now let's say we removed the PostgreSQL 13 beta 2 image. If no other Docker image extended alpine:3.12, then Docker would consider alpine:3.12 a so-called “dangling image”: A once implicitly downloaded image that's now not needed anymore. docker image prune removes these dangling images:
docker image prune
This command also requires us to enter y and press Enter to proceed:
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Total reclaimed space: 0B
On the test machine, this didn't remove any images.
docker image prune -a removes all images not used by containers. So if we don't have any containers (running or not), then this will remove all Docker images! That is a dangerous command indeed:
docker image prune -a
On the test machine, this removed all images. docker system df confirms that neither containers nor images are left:
TYPE TOTAL SIZE
Images 0 0B
Containers 0 0B
9. Conclusion
In this article, we first saw how we could remove a single Docker image. Next, we learned how to remove images by name, size, or creation date. Finally, we learned how to remove all unused containers and images.