Committing Docker Container Changes to New Images

Overview

Docker containers are ephemeral, and that means changes to a running container are not preserved. In general, it is advisable to continue using Dockerfiles to generate images, as we want to ensure a certain state exists for all new containers based on an image. However, what happens when you need to modify a running Docker container and preserve those changes?

There are a few scenarios where one would want to commit changes from a container to a new image. One example would be to debug an existing container, where you need to hotfix a bug or bad configurations.

Another scenario could be for investigating an issue, where you need to exact state of a running container. For example, an intrusion was detected and you need to preserve the running container to thoroughly examine it.

The container is now comprised and you need to analyze it, however, the ephemeral nature of containers means you should also replace the running container immediately. By committing the compromised container to a new image, it can be investigated in a safe sandbox environment, while a replacement container can be deployed immediately to remove any damage or payloads.

Docker Commit

The Docker commit command was introduced to provide a mechanism to commit container changes.

As mentioned, Docker commit can create a mirror image of a container. It can modify instructions, such as CMD or ENV when hotfixes are needed in an image.

Docker Layers and How Commit Works

Docker images and, inherently, containers consist of multiple layers, with each layer being a separate filesystem stacked on top of each other.

Each Dockerfile instruction creates a new layer, which are given a unique hash value, and then stacked on top of the next instruction set. Layers actually exist as directories on the host server, with the their hash value used for their name.

When Docker commit is executed, the newly created image references all of the unaltered layers used by the original image, and then copies the ephemeral layers into permanent storage.

Committing an Existing Container

To commit changes from a container to a new image, you use the docker commit command and specify the source container ID.

The container you are committing from can be running or stopped, the command will work either way.

docker commit <container id> <image-name>:<version>

For example, we have a Python Flask API running on a Docker host. It has been running for 7 days, and a back is needed for audit reasons.

$ docker ps

CONTAINER ID        IMAGE                 COMMAND             CREATED             STATUS              PORTS                    NAMES
 13c6e55583a5        serverlab/flask-api   "python main.py"    7 days ago          Up 7 days           0.0.0.0:5000->5000/tcp   dreamy_grothendieck

To commit the container to an image, we would use the following command.

docker commit 13c6e55583a5 serverlab/flask-api-audit:20190612

A new hash will be outputted, which is assigned to the newly created image.

sha256:dcbb3c4f74fa5857bea801b27477fa54a92df4db2c59d95cc8e6eabf64a241f4

When we list Docker images with the docker image command, we can see our newly created image.

REPOSITORY TAG IMAGE ID CREATED SIZE
serverlab/flask-api-audit 20190612 dcbb3c4f74fa About a minute ago 464MB

Modifying ENV Instructions

To change Docker image instructions, such as ENV when adjustments to Environment Variables are needed, you use the –change flag with docker commit.

For example, if you wanted to change the ENV instruction for an environment variable for setting the containers environment, you would use the following command.

docker commit --change='ENV environment production' dcbb3c4 serverlab/flask-api:1.0.1

Modifying the CMD Instruction

Like the ENV instruction above, we use the –change flag to adjust the CMD instruction.

For example, if we mistakenly run a Python Flask API using Python directly rather than uwsgi server, we would run the following command.

docker commit --change='CMD ["uwsgi", "--ini", "myproject.ini"] dcbb3c4 serverlab/flask-api:1.0.2

Modify Multiple Instructions

The commit command allows you to modify multiple instructions, if needed. Additional -c flags with instruction changes can be added to the command.

For example, to adjust the CMD instruction and EXPOSE instruction, you would use the following command.

docker commit --change='CMD ["uwsgi", "--ini", "myproject.ini"]' -c "EXPOSE 80" dcbb3c4 serverlab/flask-api:1.0.2