Docker Container To Image

We know how Docker can turn an Image into a Running Container. When we run that container again, we get the same thing we got the first time. The entire point of images is that they are fixed points where you know everything’s good, and you can always start from there. When you’ve got a Running Container, you make changes to that container and put files there. It’s beneficial when you want to be able to save those. The next step in the Docker flow is a Stopped Container. So the container that’s running is alive and has a process in it. The container is still there when that process exits, so any files you added to the container are still there. We can go back and see them, as Docker didn’t delete them. It’s just that it’s currently in a Stopped Container.

Docker Bash

Let’s get a container up and running using this command.

docker run -it ubuntu bash

The command above should now have a running container for us. Now we want to be able to use docker ps format vertically. To do this, we can use the string below at the terminal to store the formatting in the $FORMAT variable.

export FORMAT="ID\t{{.ID}}\nNAME\t{{.Names}}\nIMAGE\t{{.Image}}\nPORTS\t{{.Ports}}\nCOMMAND\t{{.Command}}\nCREATED\t{{.CreatedAt}}\nSTATUS\t{{.Status}}\n"

The use of vertical output is seen here.

docker ps --format="$FORMAT"
ID	f3e8d5e3b007
NAME	recursing_khayyam
IMAGE	ubuntu
PORTS	
COMMAND	"bash"
CREATED	2022-11-11 12:30:22 -0500 EST
STATUS	Up 9 minutes

Add Files To Container

Let’s add a file in our container to demonstrate a few things.

root@f3e8d5e3b007:~# touch NEWFILE.txt
root@f3e8d5e3b007:~# ls
NEWFILE.txt

Now let’s stop the container which has a new file in it.

docker stop recursing_khayyam

Last Exited Container

So we can look at the most recently exited container with the docker ps command, just like we do when we want to look at the Running Containers, except Stopped Containers don’t show up by default. To see Stopped Containers, we can specify the -a argument to see all containers, a for all. And if we want to see the last container to exit, we can do docker ps -l. Now since we just exited a container right there, we’re going to say docker ps -l, because we want to look at the last one. Let’s put the format argument to fit it nicely on the screen.

docker ps -l --format="$FORMAT"
ID	f3e8d5e3b007
NAME	recursing_khayyam
IMAGE	ubuntu
PORTS	
COMMAND	"bash"
CREATED	2022-11-11 12:30:22 -0500 EST
STATUS	Exited (137) 3 minutes ago

We see we have a container. That’s the container ID from this one that we just exited. You see the image that the container started from it. The command we had run bash, we made it three minutes ago, and it exited with an exit code of 137. That’s just the return value of whatever process you ran. Zero generally means success. These exit codes can be a good clue as to why a container died. If you expected a container to be running and find it to be stopped for some reason, this could often give you a clue. This container doesn’t have any networking going on, and Docker can be newly made up a name for us. So now we have a Stopped Container. Alright, now say I’ve got a Stopped Container with a file we want to use for the future. We started from a base image. We installed our software, and I’ve got a container with our software installed.

Docker Commit

The next step is the Docker Commit command. That takes containers and makes images out of them. It doesn’t delete the container. The container is still there. Now we have an image with the same content in that container. So Docker Run and Docker Commit are complementary to each other. Docker Run takes images to containers, and Docker Commit takes containers back to new images. It doesn’t overwrite the image that the container was made from. So now we can make a new image.

The stopped container has a new file on it. This is for simplicity. It could be that we made extensive changes or installed different software. We can take that new container and turn it back into an image. We do this with the docker commit command passing it the ID of the container we want to turn into an image.

% docker commit f3e8d5e3b007 
sha256:978fbe5583de6cbd66bd42afbdb491abda1ba27d3a159430812742944686de21

We just got an image ID out of it. It looks very different from a container ID. Now we have made a new image. The original Ubuntu image is unchanged. We have a new image with a big number, which is not very convenient.

Docker Tag

So the last step in the Docker flow is the tag command to give image names. We do this by using the docker tag command passing in the image id we got when running the docker commit command.

docker tag 978fbe5583de6cbd66bd42afbdb491abda1ba27d3a159430812742944686de21 my-new-image

docker images
REPOSITORY     TAG       IMAGE ID       CREATED         SIZE
my-new-image   latest    978fbe5583de   5 minutes ago   69.2MB
ubuntu         latest    3c2df5585507   8 days ago      69.2MB
hello-world    latest    46331d942d63   7 months ago    9.14kB

When we run docker images, we see a new image called my-new-image. Now let’s launch that new image.

docker run -it my-new-image bash
root@fe12fb60b6b9:/# cd ~
root@fe12fb60b6b9:~# ls
NEWFILE.txt

Running New Images

We can say docker run to my-new-image, which has my important new file inside. Committing images and then tagging them is such a typical pattern that it’s built into the Docker Commit command. So you can skip the steps about copying the image name over to a Docker tag command and just run Docker Commit, the name of the container with its nice human-readable name, and then you can, as the following argument, say the name you’d like it to be tagged as.

docker commit recursing_khayyam my-2nd-image
sha256:7147f810f60b90af135c75344d096b33342fdda1583153d3935fba3fedcba40f

docker images
REPOSITORY     TAG       IMAGE ID       CREATED          SIZE
my-2nd-image   latest    7147f810f60b   5 seconds ago    69.2MB
my-new-image   latest    978fbe5583de   15 minutes ago   69.2MB
ubuntu         latest    3c2df5585507   8 days ago       69.2MB
hello-world    latest    46331d942d63   7 months ago     9.14kB

We can see from the steps above how we made a second image out of the container using my-2nd-image as the name. This is the approach you should probably use in your daily life. There’s no reason to go through the extra step of doing a commit, then running Docker tag, but it is essential to understand that that’s what’s happening under the hood.