Docker image layers are a way to group related Docker images together. They can be used to simplify the process of building and testing Docker applications. A Docker image layer is created by combining two or more Dockerfiles into a single file. The first line of the file defines the name of the layer, followed by a list of files that will be included in the layer. The second line of the file defines how to build and run the layer. The lines following this define where to find and build the images for this layer. The final line of the file defines how to use this layer in a docker-compose or docker-compose up command.


Docker images consist of multiple layers that collectively provide the content you see in your containers. But what actually is a layer, and how does it differ from a complete image?

In this article you’ll learn how to distinguish these two concepts and why the difference matters. While you can use Docker without a thorough understanding of layers, having an awareness of their purpose will help you identify optimization opportunities.

What’s an Image?

A Docker “image” behaves like a template from which consistent containers can be created. If Docker was a traditional virtual machine, the image could be likened to the ISO used to install your VM. This isn’t a robust comparison, as Docker differs from VMs in terms of both concept and implementation, but it’s a useful starting point nonetheless.

Images define the initial filesystem state of new containers. They bundle your application’s source code and its dependencies into a self-contained package that’s ready to use with a container runtime. Within the image, filesystem content is represented as multiple independent layers.

What are Layers?

Layers are a result of the way Docker images are built. Each step in a Dockerfile creates a new “layer” that’s essentially a diff of the filesystem changes since the last step. Metadata instructions such as LABEL and MAINTAINER do not create layers because they don’t affect the filesystem.

This image has two instructions (COPY and RUN) so it’ll create two layers:

The first step copies foo. txt into a new layer that’s based on the ubuntu:latest image. The second step runs the date command and pipes its output into a file. This creates a second layer that’s based on the previous one.

Create foo.txt in your working directory:

Now build the sample image:

Each build step emits the ID of the created layer. The last step’s layer becomes the final image so it gets tagged with foo:latest.

The sequence reveals that layers are valid Docker images. Although the term “layer” isn’t normally used to refer to a tagged image, all tagged images are technically just layers with an identifier assigned.

You can start a container from an intermediate layer’s image:

This example starts a container from the layer created by the second build step. foo.txt is available in the container but built-on.txt doesn’t exist because it’s not added until the third step. That file’s only available in the filesystems of subsequent layers.

The Role of Layers

Layers contain the changes created by a build step, relative to the previous layer in the Dockerfile. FROM instructions are a special case that reference the final layer of an existing image.

Layers allow build steps to be cached to avoid redundant work. Docker can skip unchanged instructions in your Dockerfile by reusing the previously created layer. It bases the next step on that existing layer, instead of building a new one.

You can see this by modifying your Dockerfile as follows:

The third build step has changed. Now rebuild your image:

The second build step shows as Using cache and produces the same layer ID. Docker could skip building this layer as it was already created earlier and foo.txt hasn’t changed since the first build.

This caching only works up to the point a layer is modified. All the steps after that layer will need to be rebuilt too so they’re based on the new filesystem revision.

Layers and Pull Operations

Another benefit of layers is how they enable partial image pulls. Once you’ve downloaded a few images to your machine, you’ll often find new pulls can skip some layers that you already have. This image contains 13 layers but only six had to be downloaded by the pull operation:

The other layers were already present on the Docker host so they could be reused. This improves performance and avoids wasting network bandwidth.

Inspecting Image Layers

You can list the layers within an image by running the docker image history command. Each layer displays the ID of the created image and the Dockerfile instruction that caused the change. You can see the total size of the content within the layer too.

The last layer displays as because it refers to a layer within the ubuntu:latest base image. This is not available locally, as only the final layer of the base image (df5de72bdb3b) gets pulled down during builds. There’s no need to independently pull all the intermediate layers when you want to use a specific image.

Summary

Docker images and layers are generally interchangeable terms. A layer is an image and an image is formed from one or more layers. The major difference lies in tags: an image will be tagged and designed for end users, while the term “layer” normally refers to the untagged intermediate images created as part of a build operation. These aren’t visible unless you go looking for them.

There’s one more topic that relates to layers: running containers add an extra writable layer on top of their image. Layers sourced from the container’s image are read-only so filesystem modifications made by the container target its ephemeral writable layer. The writable layer gets discarded when the container’s stopped or deleted.