182

I'm trying to remove an image and I get:

# docker rmi f50f9524513f  
Failed to remove image (f50f9524513f): Error response from daemon: conflict: unable to delete f50f9524513f (cannot be forced) - image has dependent child images

This is the docker version:

# docker version
Client:
 Version:      1.10.3
 API version:  1.22
 Go version:   go1.5.3
 Git commit:   20f81dd
 Built:        Thu Mar 10 21:49:11 2016
 OS/Arch:      linux/amd64

Server:
 Version:      1.10.3
 API version:  1.22
 Go version:   go1.5.3
 Git commit:   20f81dd
 Built:        Thu Mar 10 21:49:11 2016
 OS/Arch:      linux/amd64

but there is no extra information:

# docker images --format="raw" | grep f50f9524513f -C3

repository: debian
tag: 8
image_id: f50f9524513f
created_at: 2016-03-01 18:51:14 +0000 UTC
virtual_size: 125.1 MB

repository: debian
tag: jessie
image_id: f50f9524513f
created_at: 2016-03-01 18:51:14 +0000 UTC
virtual_size: 125.1 MB

How can I get the dependent child images it claims to have?

there are no running nor stopped containers with that image id.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
nicocesar
  • 2,674
  • 2
  • 17
  • 25
  • 21
    I have to wonder why docker team can't provide a native way to do this? – Alkanshel Dec 14 '18 at 01:19
  • I fully agree with @Alkanshel, but to me this is not just an inconvenience, but also a security concern. Neither docker-cli now docker-hub provide more straight forward ways to find out what was baked into an image. Why is retrieving this such a pain??? – Axel Heider Jun 16 '23 at 19:24

13 Answers13

138

If you don't have a huge number of images, there's always the brute-force approach:

for i in $(docker images -q)
do
    docker history $i | grep -q f50f9524513f && echo $i
done | sort -u
Simon Brady
  • 1,877
  • 3
  • 13
  • 10
  • 2
    I think this is the "best" solution. You can expand this a little bit and make it a bit more clear what's what by changing the `echo $1` to the uglier (but still brute-forcy) `docker images | grep $i` (more portable in docker versions than using `--filter` flags for the Image Id) – Jon V Jan 11 '18 at 20:02
  • You can add the `-q` flag to the `docker history` command to speed up execution a bit – Lord Elrond Nov 19 '20 at 22:12
73

Short answer: Here is a python3 script that lists dependent docker images.

Long answer: You can see the image id and parent id for all image created after the image in question with the following:

docker inspect --format='{{.Id}} {{.Parent}}' \
    $(docker images --filter since=f50f9524513f --quiet)

You should be able to look for images with parent id starting with f50f9524513f, then look for child images of those, etc.. But .Parent isn’t what you think., so in most cases you would need to specify docker images --all above to make that work, then you will get image ids for all intermediate layers as well.

Here's a more limited python3 script to parse the docker output and do the searching to generate the list of images:

#!/usr/bin/python3
import sys

def desc(image_ids, links):
    if links:
        link, *tail = links
        if len(link) > 1:
            image_id, parent_id = link
            checkid = lambda i: parent_id.startswith(i)
            if any(map(checkid, image_ids)):
                return desc(image_ids | {image_id}, tail)
        return desc(image_ids, tail)
    return image_ids


def gen_links(lines):
    parseid = lambda s: s.replace('sha256:', '')
    for line in reversed(list(lines)):
        yield list(map(parseid, line.split()))


if __name__ == '__main__':
    image_ids = {sys.argv[1]}
    links = gen_links(sys.stdin.readlines())
    trunc = lambda s: s[:12]
    print('\n'.join(map(trunc, desc(image_ids, links))))

If you save this as desc.py you could invoke it as follows:

docker images \
    | fgrep -f <(docker inspect --format='{{.Id}} {{.Parent}}' \
        $(docker images --all --quiet) \
        | python3 desc.py f50f9524513f )

Or just use the gist above, which does the same thing.

Aryeh Leib Taurog
  • 5,370
  • 1
  • 42
  • 49
19

Install dockviz and follow the branches from the image id in the tree view:

dockviz images --tree -l
mmoya
  • 1,901
  • 1
  • 21
  • 30
9

I've created a gist with shell script to print out descendant tree of a docker image, should anyone be interested in bash solution:

#!/bin/bash
parent_short_id=$1
parent_id=`docker inspect --format '{{.Id}}' $1`

get_kids() {
    local parent_id=$1
    docker inspect --format='ID {{.Id}} PAR {{.Parent}}' $(docker images -a -q) | grep "PAR ${parent_id}" | sed -E "s/ID ([^ ]*) PAR ([^ ]*)/\1/g"
}

print_kids() {
    local parent_id=$1
    local prefix=$2
    local tags=`docker inspect --format='{{.RepoTags}}' ${parent_id}`
    echo "${prefix}${parent_id} ${tags}"

    local children=`get_kids "${parent_id}"`

    for c in $children;
    do
        print_kids "$c" "$prefix  "
    done
}

print_kids "$parent_id" ""
domino
  • 2,137
  • 1
  • 22
  • 30
  • I just had trouble formatting the code, so I gave up, thanks for doing this Jan – Michal Linhard Dec 01 '17 at 12:17
  • Hey @MichalLinhard! I found your snippet extremely useful. I have included it in https://github.com/PHPExpertsInc/DockerUpgrade/blob/master/docker-images-update If you could grant me (or everyone!) a MIT license to this code, it'd be highly appreciative! – Theodore R. Smith Sep 19 '20 at 17:26
5

Based on slushy and Michael Hoffman answers, if you don't have a ton of images you can use this shell function:

docker_image_desc() {
  for image in $(docker images --quiet --filter "since=${1}"); do
    if [ $(docker history --quiet ${image} | grep ${1}) ]; then
      docker_image_desc "${image}"
    fi
  done
  echo "${1}"
}

and then call it using

docker_image_desc <image-id> | awk '!x[$0]++'
Maxim Suslov
  • 4,335
  • 1
  • 35
  • 29
5

Here's a simple way to get a list of child images that are dependent on a parent image:

image_id=123456789012

docker images -a -q --filter since=$image_id |
xargs docker inspect --format='{{.Id}} {{.Parent}}' |
grep $image_id

That will generate a list of child/parent images IDs, for example (truncated for brevity):

sha256:abcdefghijkl sha256:123456789012

The left side is the child image ID, the right side is parent image ID that we are trying to delete, but it is saying "image has dependent child images". This tells us that abcdefghijkl is the child that is dependent on 123456789012. So we need to first docker rmi abcdefghijkl, then you can docker rmi 123456789012.

Now, there may be a chain of dependent child images, so you may have to keep repeating to find the last child.

wisbucky
  • 33,218
  • 10
  • 150
  • 101
4

Here's a solution based on the Python API (pip install docker) that recursively lists descendants together with their tags (if any), increasing the indentation according to the depth of the relationship (children, grandchildren, etc.):

import argparse
import docker

def find_img(img_idx, id):
    try:
        return img_idx[id]
    except KeyError:
        for k, v in img_idx.items():
            if k.rsplit(":", 1)[-1].startswith(id):
                return v
    raise RuntimeError("No image with ID: %s" % id)

def get_children(img_idx):
    rval = {}
    for img in img_idx.values():
        p_id = img.attrs["Parent"]
        rval.setdefault(p_id, set()).add(img.id)
    return rval

def print_descendants(img_idx, children_map, img_id, indent=0):
    children_ids = children_map.get(img_id, [])
    for id in children_ids:
        child = img_idx[id]
        print(" " * indent, id, child.tags)
        print_descendants(img_idx, children_map, id, indent=indent+2)

def main(args):
    client = docker.from_env()
    img_idx = {_.id: _ for _ in client.images.list(all=True)}
    img = find_img(img_idx, args.id)
    children_map = get_children(img_idx)
    print_descendants(img_idx, children_map, img.id)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("id", metavar="IMAGE_ID")
    main(parser.parse_args())

Example:

$ python find_dep_img.py 549afbf12931
 sha256:913d0981fdc7d2d673f2c8135b7afd32ba5037755e89b00432d3460422ba99b9 []
   sha256:0748dbc043b96ef9f88265c422e0267807f542e364b7a7fadf371ba5ee082b5d []
     sha256:6669414d2a0cc31b241a1fbb00c0ca00fa4dc4fa65dffb532bac90d3943d6a0a []
       sha256:a6441e7d9e92511608aad631f9abe8627033de41305c2edf7e03ee36f94f0817 ['foo/bar:latest']

I've made it available as a gist at https://gist.github.com/simleo/10ad923f9d8a2fa410f7ec2d7e96ad57

simleo
  • 2,775
  • 22
  • 23
3

This is what I did in order to preserve my final "image" (layer, really - which is what threw me off, as I am just getting into docker builds).

I was getting the whole "... cannot be forced..." message. I realized I couldn't delete the images I didn't need because they are not really independent images created by 'docker commit'. My issue was, I had several images (or layers) between the base image and my final, and just trying to clean up is where I met the error/warning about the child and parent.

  1. I exported the final image (or layer, if you will) out to a tarball.
  2. I then deleted all the images I wanted to, including my final - I have it saved to a tarball so, while I wasn't sure if I would be able to use it, I was just experimenting.
  3. I then ran docker image load -i FinalImage.tar.gz. The output was something like:

7d9b54235881: Loading layer [==================================================>]  167.1MB/167.1MB
c044b7095786: Loading layer [==================================================>]  20.89MB/20.89MB
fe94dbd0255e: Loading layer [==================================================>]  42.05MB/42.05MB
19abaa1dc0d4: Loading layer [==================================================>]  37.96MB/37.96MB
4865d7b6fdb2: Loading layer [==================================================>]  169.6MB/169.6MB
a0c115c7b87c: Loading layer [==================================================>]    132MB/132MB

Loaded image ID: sha256:82d4f8ef9ea1eab72d989455728762ed3c0fe35fd85acf9edc47b41dacfd6382

Now, when I list with 'docker image ls', I only have the original base image, and the final image I previously saved to a tarball.

[root@docker1 ~]# docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
httpd               import              82d4f8ef9ea1        3 days ago          747MB
centos              httpd               36540f359ca3        5 weeks ago         193MB

My system is 'clean' now. I only have the images I want. I even deleted the base image without a problem.

[root@docker1 ~]# docker rmi 36540f359ca3
Untagged: centos:httpd
Untagged:     centos@sha256:c1010e2fe2b635822d99a096b1f4184becf5d1c98707cbccae00be663a9b9131
Deleted: sha256:36540f359ca3b021d4b6a37815e9177b6c2bb3817598979ea55aee7ecc5c2c1f
Paulie-C
  • 1,674
  • 1
  • 13
  • 29
  • 1
    Good answer, but load is only supposed to be used on an image created from "docker save". The proper restore for a "docker export" is "docker import" – Alkanshel Dec 03 '18 at 19:03
0

You can delete Docker images irrespective of parent and child relation through the below directory of Docker

/var/lib/docker/image/devicemapper/imagedb/content/sha256

In this directory you can find Docker images, so you can delete what you want.

nwinkler
  • 52,665
  • 21
  • 154
  • 168
Jvn
  • 17
  • 3
  • I'm trying to make portable commands using the docker API, assuming device mapper and docker engine (as opposed to docker swarm for example) makes this a non portable solution. Also is risky to be deleting files in the filesystem while other process (including the docker daemon) could be using it. – nicocesar Dec 19 '16 at 14:24
  • where is it on macos? – Anthony Kong Jun 02 '17 at 01:37
  • 2
    *WARNING" this can cause multiple errors with deleting and pulling containers in future. Never delete dirs in `/var/lib/docker` directly by hand – vladkras Oct 15 '18 at 07:09
0

How about:

ID=$(docker inspect --format="{{.Id}}" "$1")
IMAGES=$(docker inspect --format="{{if eq \"$ID\" .Config.Image}}{{.Id}}{{end}}" $(docker images --filter since="$ID" -q))
echo $(printf "%s\n" "${IMAGES[@]}" | sort -u)

It'll print the child image id's, with the sha256: prefix.
I also had the following, which appends the names:

IMAGES=$(docker inspect --format="{{if eq \"$ID\" .Config.Image}}{{.Id}}{{.RepoTags}}{{end}}" $(docker images --filter since="$ID" -q))

  • ID= Gets the full id of the image
  • IMAGES= Gets all child images that have this image listed as an Image
  • echo... Removes duplicates and echos the results
Justin
  • 4,434
  • 4
  • 28
  • 37
0

I cooked up this to recursively find children, their repo tags, and print what they're doing:

docker_img_tree() {
    for i in $(docker image ls -qa) ; do
        [ -f "/tmp/dii-$i"  ] || ( docker image inspect $i > /tmp/dii-$i)
        if grep -qiE 'Parent.*'$1 /tmp/dii-$i ; then
            echo "$2============= $i (par=$1)"
            awk '/(Cmd|Repo).*\[/,/\]/' /tmp/dii-$i | sed "s/^/$2/"
            docker_img_tree $i $2===
        fi
    done
}

Don't forget to delete /tmp/dii-* if your host isn't secure.

android.weasel
  • 3,343
  • 1
  • 30
  • 41
0

Here's a script that directly gives the dependent child images ids, using awk:

#!/bin/sh
set -e
parent=$1
echo "Dependent Child Images IDs of $parent "
docker inspect --format='{{.Id}} {{.Parent}}' \
$(docker images --all --quiet --filter since=$parent) \
| awk -v parent=$parent '{ if($2 == parent) print $1 }'

It's used by passing the parent image id as first parameter:

$ ./get_children_of.sh sha256:15f16e6da280b21e8d3b17ac49022732a553550a15ad2d0d12d5cf22b36254cb
Dependent Child IDs of sha256:15f16e6da280b21e8d3b17ac49022732a553550a15ad2d0d12d5cf22b36254cb 
sha256:c18db0311efb263c0f52e520d107b02a373aac7a4821164e7a1b46b14a3a1419
exaucae
  • 2,071
  • 1
  • 14
  • 24
-7

I was also facing the same issue. Fallowed steps below to resolve the issue.

Stop all running containers

docker stop $(docker ps -aq) Remove all containers

docker rm $(docker ps -aq) Remove all images

docker rmi $(docker images -q)

Ram Pasupula
  • 317
  • 3
  • 5