Does Docker EXPOSE make a new layer?

Yes, every instruction in a Dockerfile generates a new layer for the resulting image.

However, layers created via EXPOSE are empty layers. That is, their size is 0 bytes.

While they don't impact you storage-wise, they do count for leveraging layer cache while building or pulling/pushing images from a registry.

A good way to understand an image's layers is to use the docker history command. For instance, given the following Dockerfile:

FROM scratch

EXPOSE 4000
EXPOSE 3000

do

docker build -t test/image .

If you then docker history test/image you'll see:

IMAGE               CREATED             CREATED BY                           SIZE                COMMENT
ab9f435de7bc        4 seconds ago       /bin/sh -c #(nop)  EXPOSE 4000/tcp   0 B                 
15e09691c313        5 seconds ago       /bin/sh -c #(nop)  EXPOSE 3000/tcp   0 B     

If you switch the order of the EXPOSE statements and build again, you'll see the layer cache being ignored.


All instructions create new layers, but instructions that do not change the filesystem will create a layer that is empty.

It's worth looking into how Docker's filesystem layering works which you can read about here or here for AUFS.

Essentially new layers on the file system are made of those files that have changed from the layer below them, it's like a stack of diffs. As such, if there is no change, there is no layer to make. Mostly...

Every instruction in a Dockerfile will create an image layer, but for AUFS (in the case of EXPOSE) that layer will be empty (no difference between it and the one below it).


I realised i could test this myself. and I've found that adding EXPOSE does not add a new file system layer, but it does add a layer none the less, also it does matter which order you make your docker files for your cache layers.

basically: every command creates a new layer, every command that changes the file system creates a filesystem layer.

FROM ...
EXPOSE 80
COPY smthing .

is different from:

FROM ...
COPY smthing .
EXPOSE 80

When executed multiple times (say in a development environment).

in the first example the EXPOSE command is cached and is not executed even if the smthing file changes. If the something file changes, docker build will only re-executed this command (rest is taken from cache).

In the second example. if the smthing file changes, the EXPOSE command will also be rebuild. (since everything after the copy command is invalidated and re executed on docker build).

Would i change the EXPOSE port the first case would have to re-execute the copy command, where the second example wouldn't.

But both would lead to the exact same end result file-system layer wise.

docker inspect imageName #shows the file system layer
docker history imageName #shows all the layers

No, directive EXPOSE does not add a new layer. This information will be stored permanently in the image and container config and could be retrieved via:

docker inspect --format '{{.Config.ExposedPorts}}' <image_id>

But you may still be wondering why there is a line in the output that says that the new image was created for this command. Consider this Dockerfile:

FROM alpine
EXPOSE 8000

In the end, Docker produces such output:


Step 1/2 : FROM alpine
 ---> 965ea09ff2eb
Step 2/2 : EXPOSE 8000
 ---> Running in 6c8fae4f3499
Removing intermediate container 6c8fae4f3499
 ---> 067aa2abe94f
Successfully built 067aa2abe94f
Successfully tagged envtest:latest

In the same time docker history outlines:


IMAGE               CREATED              CREATED BY                                      SIZE                COMMENT
067aa2abe94f        About a minute ago   /bin/sh -c #(nop)  EXPOSE 8000                  0B                  
965ea09ff2eb        7 weeks ago          /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B

Every line in the Dockerfile causes image creation on the top of the current image, thus image has a link to the parent image. Afterward, the immediate container will be initialized based on this new image then your command will be executed within and result committed to the image.

As a general rule, if the command does not lead to changes in the filesystem the new layer won't be created. I would recommend using dive for exploring each layer in a docker image.