Visual Docker: Images are Immutable
Tags: docker, containers, cloudnative
Published: 2023-04-07 11:07:15

## Changing an image alters the rules mid-game
This is the second article in a series dedicated to visually explaining Docker and container concepts using a game of Tic-Tac-Toe and little or no code. It's focused on image immutability and tags, and demonstrates why images can't change.

The [last post](https://dev.to/oraclesean/visual-docker-images-and-containers-48d9) in this series used a game of Tic-Tac-Toe to show how containers are thin layers projected onto images. The image is the Tic-Tac-Toe board and includes the game's rules. It's shared by every Tic-Tac-Toe game container. Individual containers are layers that start out empty. As players add moves, they're recorded in the container layer.

The game only makes sense when you see both pieces together. On its own, the container layer is just random X's and O's. The game board in the image is perpetually empty because it can't change. In the container world, we say that images are _immutable_ and, therefore, also _stateless_ since altering the state requires saving those changes somewhere.

## Change ≠ Replace
Is that really true, though? After all, I can change a Dockerfile and rebuild the image using the same name, which would seem to change the image. More accurately, I _replaced_ the old image and assigned the same name.

It's an important distinction. To illustrate why, let's list the images on the system:
```
> docker images
REPOSITORY   TAG           IMAGE ID       CREATED      SIZE
games        tic-tac-toe   ec616ed52b3b   1 hour ago   100KB
```
Then, start a new game container using that image. The "friendly" image name is its `REPOSITORY` and `TAG`, joined by a colon:
```
docker run games:tic-tac-toe
```
When I list the containers running on my system, I see my game:
```
> docker ps -a
CONTAINER ID  IMAGE        COMMAND           CREATED       STATUS                 PORTS   NAMES
f8763546a856  ec616ed52b3b "/bin/tictactoe"  1 minute ago  Up 1 minute (healthy)          abundant_fun
```
Inside the container, players make a series of moves ending in a stalemate:

{% embed https://youtu.be/bKu5H0DiSPM %}

Recall that the moves are saved in the container layer, but the game board and all of the rules are part of the image. What if I could change the _existing_ image used by a running container? Perhaps rewriting the Dockerfile and shifting the board to the right:

{% embed https://youtu.be/-Bxe_-UyOVQ %}

If the container used the changed image, moves in the leftmost column of the container layer would fall off the board, while three new spaces open up on the right! The game is no longer a stalemate! Now, Player O has two paths to victory courtesy of the repositioned game board in the new image, and Player X can block only one!

{% embed https://youtu.be/7LFvAltPOF8 %}

That demonstrates what _would_ happen if you _could_ change an existing image. But it does raise a question: what happens when you replace an image?

## The <none> Repo
Recall the original image was named `games:tic-tac-toe`. If there were no containers using the image, Docker simply removes and replaces the image. But in the example above, a container _was_ using the "old" image. So, does Docker respond by removing the container? Does it crash or stop responding when its image is replaced? Fortunately not!

In this case, Docker preserves the original image by un-tagging it—removing the friendly name—making the `games:tic-tac-toe` tag available to the newly built image. Once untagged, the image no longer has a friendly name, which I'll see when I once again list the images on the system:
```
> docker images
REPOSITORY   TAG           IMAGE ID       CREATED       SIZE
games        tic-tac-toe   2fafb511dc00   1 hour ago    100KB
<none>       <none>        ec616ed52b3b   2 hours ago   100KB
```
One shows `<none>` for the `REPOSITORY` and `TAG`. Look closely, and you'll see the `IMAGE ID` matches the original image. By rebuilding the image, Docker untagged the old one but didn't delete it! Any containers that used the "old" image continue to work as before, without any change!

If you see `<none>` listed for an image repository and tag, it just means it was untagged or replaced while still in use by a running container.