Restore/Rollback Layers from Docker Image

Frank Chung
DeepQ Research Engineering Blog
3 min readJul 13, 2023
Docker Layer Extraction

Background

If you see this article, I believe you must meet the following condition:

  1. You have a pre-built docker image locally
  2. You cannot build it from scratch (e.g., missing dockerfile, base image not found, …)
  3. You want to extract a specific layer to a new image

If the above conditions are matched, please take a look.

Method1: Restore from Docker History

If you have a image named myimage:latest , just see its history:

$ docker history myimage

IMAGE CREATED CREATED BY SIZE COMMENT
cf7eff3a099a 46 minutes ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0
<missing> 46 minutes ago ENV hello=hello 0B buildkit.dockerfile.v0
<missing> 8 days ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
<missing> 8 days ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B
<missing> 8 days ago /bin/sh -c #(nop) EXPOSE 80 0B

If the layer image existed in your local machine, congratulation! That means you can just use it with its image id.

But in most cases, your history always show <missing> that means you need to take more look in this article below.

Method2: Restore from tar

To see and modify layers in a docker image, we first need to save it into a tar file.

$ docker save -o myimage.tar myimage
$ tar -xf myimage.tar

and you will see the structure like:

.
├── d0258d951c321280514e3ad0805e901622341efa419063abd5f56f994c9936cc
│ ├── VERSION
│ ├── json
│ └── layer.tar
├── e398982e9943cd762c1ecd08916b0a84e2c19e2ab67f0ed3b222a5932aa401d6
│ ├── VERSION
│ ├── json
│ └── layer.tar
├── 381fdd67c27a597fb878f0855835cd117edfae360dce72726bc8e1771be6a80a.json
├── manifest.json
└── repositories

Manifest.json records the layers id that are one-one mapping to the layer folder. Config json records the history for each layer. And please take a look at the history to find the layer diff-ids (empty layer is not linked with a layer).

"history": [
{
"created": "2023-07-13T02:04:25.6259336Z",
"created_by": "ENV hello=hello",
"comment": "buildkit.dockerfile.v0",
"empty_layer": true
},
{
"created": "2023-07-13T02:04:25.6259336Z",
"created_by": "ADD hello.txt /usr/share/nginx/html/hello.txt # buildkit",
"comment": "buildkit.dockerfile.v0"
},
{
"created": "2023-07-13T02:04:25.6259336Z",
"created_by": "CMD [\"nginx\" \"-g\" \"daemon off;\"]",
"comment": "buildkit.dockerfile.v0",
"empty_layer": true
}
],
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:1ef811e4cafadd84db9b7a6b351842b8c7b005f051a9e6b516ddde5981482056"
]
}

Take above config json as an example, if we want to remove the layer:

ADD hello.txt /usr/share/nginx/html/hello.txt

We can find the corresponding layer in diff_ids by default sort order. And also we can locate the physical layer id in manifest.json in the same sort order.

Now it is time to do the modification, let’s delete the layer in history , diff_ids and Layers in config and manifest.json, and also delete the corresponding layer folder.

After deletion, we can restore to a new docker image.

$ tar -cf myimage_new.tar *
$ docker load -i myimage_new.tar

Now the docker image myimage:latest is loaded and the layer is removed.

Summary

Sometimes we just need to do a small change without re-build the whole docker image, and this method can help you.

Another case is that if the base image is missing from dockerhub, we can remove extra layers from its derived image to “rollback” it to base image.

--

--