
Interactive image creation
The first way we can create a custom image is by interactively building a container. That is, we start with a base image that we want to use as a template and run a container of it interactively. Let's say that this is the Alpine image.
To interactively create an image follow along:
- The command to run the container would be as follows:
$ docker container run -it \
--name sample \
alpine:3.10 /bin/sh
The preceding command runs a container based on the alpine:3.10 image.
We run the container interactively with an attached teletypewriter (TTY) using the -it parameter, name it sample with the --name parameter, and—finally—run a shell inside the container using /bin/sh.
In the Terminal window where you run the preceding command, you should see something similar to this:
Unable to find image 'alpine:3.10' locally
3.10: Pulling from library/alpine
921b31ab772b: Pull complete
Digest: sha256:ca1c944a4f8486a153024d9965aafbe24f5723c1d5c02f4964c045a16d19dc54
Status: Downloaded newer image for alpine:3.10
/ #
By default, the alpine container does not have the ping tool installed. Let's assume we want to create a new custom image that has ping installed.
- Inside the container, we can then run the following command:
/ # apk update && apk add iputils
This uses the apk Alpine package manager to install the iputils library, of which ping is a part. The output of the preceding command should look approximately like this:

- Now, we can indeed use ping, as the following code snippet shows:

- Once we have finished our customization, we can quit the container by typing exit at the prompt.
If we now list all containers with the ls -a Docker container, we can see that our sample container has a status of Exited, but still exists on the system, as shown in the following code block:
$ docker container ls -a | grep sample
040fdfe889a6 alpine:3.10 "/bin/sh" 8 minutes ago Exited (0) 4 seconds ago
- If we want to see what has changed in our container in relation to the base image, we can use the docker container diff command, as follows:
$ docker container diff sample
The output should present a list of all modifications done on the filesystem of the container, as follows:
C /usr
C /usr/sbin
A /usr/sbin/getcap
A /usr/sbin/ipg
A /usr/sbin/tftpd
A /usr/sbin/ninfod
A /usr/sbin/rdisc
A /usr/sbin/rarpd
A /usr/sbin/tracepath
...
A /var/cache/apk/APKINDEX.d8b2a6f4.tar.gz
A /var/cache/apk/APKINDEX.00740ba1.tar.gz
C /bin
C /bin/ping
C /bin/ping6
A /bin/traceroute6
C /lib
C /lib/apk
C /lib/apk/db
C /lib/apk/db/scripts.tar
C /lib/apk/db/triggers
C /lib/apk/db/installed
We have shortened the preceding output for better readability. In the list, A stands for added, and C stands for changed. If we had any deleted files, then those would be prefixed with a D.
- We can now use the docker container commit command to persist our modifications and create a new image from them, like this:
$ docker container commit sample my-alpine
sha256:44bca4141130ee8702e8e8efd1beb3cf4fe5aadb62a0c69a6995afd49c2e7419
With the preceding command, we have specified that the new image will be called my-alpine. The output generated by the preceding command corresponds to the ID of the newly generated image.
- We can verify this by listing all images on our system, as follows:
$ docker image ls
We can see this image ID (shortened) as follows:
REPOSITORY TAG IMAGE ID CREATED SIZE
my-alpine latest 44bca4141130 About a minute ago 7.34MB
...
We can see that the image named my-alpine has the expected ID of 44bca4141130 and automatically got a latest tag assigned. This happens since we did not explicitly define a tag ourselves. In this case, Docker always defaults to the latest tag.
- If we want to see how our custom image has been built, we can use the history command as follows:
$ docker image history my-alpine
This will print a list of the layers our image consists of, as follows:

The first layer in the preceding output is the one that we just created by adding the iputils package.