Docker, as well as container engines in general, makes use of a union mount file system concept.
Union mounting is a way of combining multiple directories into one that appears to contain their combined contents.
Thus, an image is actually composed of atomic immutable folders aka "layers".
Primary goal of this mechanism is data deduplication.
One layer may be shared by several images which saves a lot storage capacity.
E. g. you download CenOS base image (700 mb) and build several custom images on top of it.
The big CentOS part is still stored once - your images are just referencing it.
Anoher good answer - old, but still relevant.
When you build an image with Dockerfile, each separate comand in Dockerfile produces a new layer, putting it on top of the previous one (or "merging" them - in terms of union file systems).
Resulting image will contain all intermediate layers created in course of your build.
FROM busybox
# layer 1: added test1 50 mb
RUN dd if=/dev/zero of=/tmp/test1 bs=1M count=50
# layer 2: added test2 50 mb
RUN dd if=/dev/zero of=/tmp/test2 bs=1M count=50
# layer 3: "removed" test1, but actually test1 still lives in layer 1
# "removal" just made it invisible for upper layers
# that is how removal works in union mount file systems
# technically, in terms of UFS, it's not a "removal", but a "merge" of layer 3 with layer 1
RUN rm -rf /tmp/test1
# layer 4: same as layer 3
RUN rm -rf /tmp/test2
The common way to save space is to chain shell commands under a single RUN instruction.
# in this case only one layer will be created
# it will contain eventual state of the filesystem after full command chain completion
RUN dd if=/dev/zero of=/tmp/test1 bs=1M count=50 && \
dd if=/dev/zero of=/tmp/test2 bs=1M count=50 && \
rm -rf /tmp/test1 && \
rm -rf /tmp/test2