This article explores Docker’s data storage management, including default folder structures, storage drivers, and image layer organization.
Welcome to this article where we explore how Docker manages data storage within the filesystem. You will learn where Docker stores its data by default, and how it structures files related to containers, images, and layers.When Docker is installed, it creates a default folder structure at /var/lib/docker. For example, listing the contents of this directory might display the following:
Copy
Ask AI
root@Docker_Host_2:/root # ls -l /var/lib/dockertotal 40drwx------ 5 root root 4096 Aug 19 21:40 aufsdrwx------ 2 root root 4096 Aug 19 21:40 builderdrwx------ 2 root root 4096 Aug 20 20:52 containersdrwx------ 3 root root 4096 Aug 19 21:40 imagedrwx------ 4 root root 4096 Aug 19 21:40 networkdrwx------ 2 root root 4096 Aug 19 21:40 pluginsdrwx------ 2 root root 4096 Aug 19 21:40 swarmdrwx------ 2 root root 4096 Aug 20 20:48 tmpdrwx------ 2 root root 4096 Aug 19 21:40 trustdrwx------ 2 root root 4096 Aug 19 21:40 volumesroot@Docker_Host_2:/root #
In this structure, directories such as aufs, builder, containers, image, network, plugins, and swarm are clearly visible. Docker stores container-related files in the containers folder and image-related files in the image folder.
The storage driver plays a crucial role in managing image layers and associated files. You can check which storage driver is active by running docker info.
When you execute the docker info command, you will see several details including the Docker version (e.g., 17.09) and the storage driver. In this example, the storage driver is aufs, which is the default on Debian and Ubuntu systems. The root directory for the storage driver is /var/lib/docker/aufs.Listing the contents of the /var/lib/docker/aufs directory provides further insight:
Copy
Ask AI
root@Docker_Host_2:/root # ls -l /var/lib/docker/aufstotal 12drwx------ 2 root root 4096 Aug 20 20:52 diffdrwx------ 2 root root 4096 Aug 20 20:52 layersdrwx------ 2 root root 4096 Aug 20 20:52 mnt
The diff folder contains the actual content of each image layer. Every instruction in a Dockerfile (for instance, copying source code into the image) creates a new layer stored within this directory.
The layers folder holds metadata indicating how these image layers are stacked.
The mnt folder stores information about the associated mount points.
At this stage, if no images or containers have been created, these directories may appear empty:
Copy
Ask AI
root@Docker_Host_2:/root # ls -l /var/lib/docker/aufs/difftotal 0root@Docker_Host_2:/root # ls -l /var/lib/docker/aufs/layerstotal 0root@Docker_Host_2:/root # ls -l /var/lib/docker/aufs/mnttotal 0
After pulling the image, inspecting /var/lib/docker/aufs again shows the directories (while they might still appear empty at the top level, their subdirectories are now populated):
Copy
Ask AI
root@Docker_Host_2:/root # ls -l /var/lib/docker/aufstotal 12drwx------ 2 root root 4096 Aug 20 20:52 diffdrwx------ 2 root root 4096 Aug 20 20:52 layersdrwx------ 2 root root 4096 Aug 20 20:52 mnt
The output from pulling the hello-world image is similar to:
Copy
Ask AI
root@Docker_Host_2:/root # docker pull hello-worldUsing default tag: latestlatest: Pulling from library/hello-world5b0f327be733: Pull completeDigest: sha256:07d5f7800dfe378c2196c7b1c524c33808ce2e0f74e7aa00e603295ca9a0972Status: Downloaded newer image for hello-world:latestroot@Docker_Host_2:/root #
To inspect how the hello-world image is built, use the docker history command along with its image ID. First, list the available images:
Copy
Ask AI
root@Docker_Host_2:/root # docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEhello-world latest 05a3bd381fc2 Less than a second ago 1.84kBroot@Docker_Host_2:/root #
Then, view the image history:
Copy
Ask AI
root@Docker_Host_2:/root # docker history 05a3bd381fc2IMAGE CREATED CREATED BY SIZE COMMENT05a3bd381fc2 Less than a second ago /bin/sh -c #(nop) CMD ["/hello"] 0B <missing> Less than a second ago /bin/sh -c #(nop) COPY file:b65349dad8105c... 1.84kB root@Docker_Host_2:/root #
This output reveals that the hello-world image comprises two steps:
A script is copied into the image.
The copied script is then set as the container’s command.
Although you could theoretically run this script directly from the Docker host, this demonstration is purely for illustrating Docker’s file storage locations.
Next, let’s build a custom Docker image containing a simple Python Flask web application. In the sample application folder named simple-webapp-docker, you will find two files:
During the build process, layers are created and cached. For instance, because Ubuntu is not available locally, it is pulled automatically. The build output will indicate the progress of each step, similar to:
Copy
Ask AI
Sending build context to Docker daemon 50.69kBStep 1/5 : FROM ubuntu:17.0417.04: Pulling from library/ubuntu...Successfully built <image-id>root@Docker_Host_2:/root/sample-application/simple-webapp-docker #
After the build completes, list the available images:
Copy
Ask AI
root@Docker_Host_2:/root/sample-application/simple-webapp-docker # docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEubuntu 17.04 6ca5545c1cef Less than a second ago 94.7MBhello-world latest 05a3bd381fc2 Less than a second ago 1.84kB<none> <none> 5745fc6e89fe 21 seconds ago 467MBroot@Docker_Host_2:/root/sample-application/simple-webapp-docker #
The unnamed image is the one you just built because no repository name or tag was specified. To make management easier, tag your image using the -t parameter.
To view detailed build history and layer information, run:
Copy
Ask AI
docker history <image-id>
For example:
Copy
Ask AI
root@Docker_Host_2:/root/sample-application/simple-webapp-docker # docker history 5745fc6e89feIMAGE CREATED CREATED BY SIZE COMMENT6ca5545c1cef4 Less than a second ago /bin/sh -c #(nop) ENTRYPOINT ["/bin/sh" ... 0B 05a3bd381fc2 Less than a second ago /bin/sh -c COPY file:29b928534d73898... 229B61d0e1d1b685 35 seconds ago /bin/sh -c pip install flask 5.71MB5745fc6e89fe 8 minutes ago /bin/sh -c apt-get update && apt-get install ... 366MB<missing> Less than a second ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B...
This history illustrates that the bulk of the image size arises from the Ubuntu base and the Python dependency installations.
You might recognize the folder that contains your application code by its smaller size. For instance, if you suspect a directory holds your application files, navigate into it and inspect its contents:
Copy
Ask AI
root@Docker_Host_2:/var/lib/docker/aufs/diff/<folder-id> # lsoptroot@Docker_Host_2:/var/lib/docker/aufs/diff/<folder-id>/opt # lsapp.pyroot@Docker_Host_2:/var/lib/docker/aufs/diff/<folder-id>/opt # cat app.pyimport osfrom flask import Flaskapp = Flask(__name__)@app.route("/")def main(): return "Welcome!"@app.route("/how are you?")def hello(): return "I am good, how about you?"if __name__ == "__main__": app.run()
Demonstrating Layered Architecture with Multiple Dockerfiles
Docker’s layered architecture allows reuse of base layers (like operating system and dependencies) even if only the application code changes. Consider the following example with two Dockerfiles:
Since the base operating system and dependencies remain unchanged, Docker reuses the cached layers for both builds. Only the layer that involves copying the updated application code (app2.py) is rebuilt. Note that when any step changes, Docker clears the cache for that step and all subsequent layers.
Edit app2.py (for example, change “Welcome” to “Welcome 2”) and update Dockerfile2 to reference app2.py:
Copy
Ask AI
FROM ubuntu:17.04RUN apt-get update && apt-get install -y python python-pipRUN pip install flaskCOPY app2.py /opt/ENTRYPOINT FLASK_APP=/opt/app2.py flask run --host=0.0.0.0
Build the new image with the updated Dockerfile:
Copy
Ask AI
root@Docker_Host_2:/root/sample-application/simple-webapp-docker # docker build -f Dockerfile2 -t sample-webapp2:latest .Sending build context to Docker daemon 52.74kBStep 1/5 : FROM ubuntu:17.04 ---> 6ca5545c1e...Step 2/5 : RUN apt-get update && apt-get install -y python python-pip ---> Using cache ---> 2b00cb5a0256Step 3/5 : RUN pip install flask ---> Using cache ---> 947b3a20cb4dStep 4/5 : COPY app2.py /opt/ ---> f7d212ed84faStep 5/5 : ENTRYPOINT FLASK_APP=/opt/app2.py flask run --host=0.0.0.0 ---> Running in c135ff9454ba ---> 61d0e1d1b685Removing intermediate container c135ff9454baSuccessfully built 61d0e1d1b685Successfully tagged sample-webapp2:latest
List the images to confirm the updated tags:
Copy
Ask AI
docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEubuntu 17.04 6ca5545c1cef Less than a second ago 94.7MBhello-world latest 05a3bd381fc2 Less than a second ago 1.84kBsample-webapp2 latest 61d0e1d1b685 About a minute ago 467MBsimple-webapp latest 2b00cb5a0256 8 minutes ago 467MB
Although both web application images report a size of 467MB, most layers (such as the Ubuntu base image and dependencies) are shared. The reported size includes duplicated layers; to view the unique disk usage, use the following command:
Copy
Ask AI
root@Docker_Host_2:/root/sample-application/simple-webapp-docker # docker system dfTYPE TOTAL ACTIVEImages 4 0Containers 0 0Local Volumes 3 0Images space usage:REPOSITORY TAG IMAGE ID SIZE RECLAIMABLEsample-webapp2 latest 5179af23f3c1 431.7MB 431.7MB (100%)hello-world latest 05a3bd381fc2 1.84kB 0Bsimple-webapp latest 0b2e4c549545 431.7MB 431.7MB (100%)ubuntu latest ccc7a1d651b1 120.1MB 0B
The docker system df command presents the actual disk consumption without counting shared layers multiple times. For an even more detailed view, include the verbose flag (-v) to examine each image’s layer breakdown.