Where are Docker Images, Containers and Volumes Stored on the Linux Host System?

Want to know where Docker images, containers and volumes are located?

In a typical Linux environment, you can find the Docker image and container data in:

/var/lib/docker/

If your server is running out of space, you should definitely take a look into this directory.

Primarily, all Docker related entities are located at /var/lib/docker. But let us look into it more specifically, with the Alpine image and container as a hands-on example.

Note: Please note that this information is for educational purpose only. Manipulating with host system Docker directories/files are never actually recommended. The docker and docker-compose commands should be the preferred method always. Physically located Docker directories/files should be accessed or manipulated only as a last resort during emergency situations.

Docker images location

Whenever you use the docker pull command or run docker-compose up -d to prepare the launch of applications, this is where images are stored on an Ubuntu server:

/var/lib/docker/overlay2

Here, Overlay2 is the default Docker storage driver on Ubuntu. You can confirm this by running the docker info command and looking for the Storage Driver:

...
Storage Driver: overlay2
...

If this is different than yours, then you're using a different storage driver for Docker. Likewise, the directory location would be named as per the same storage driver. Availability of the storage driver depends upon kernel support.

Specific image locations

If you are looking for the locations of specific images, you can use the inspect command on Docker for the pulled image.

Say, for example, I've pulled the alpine image with docker pull alpine. Run the following command to inspect it:

avimanyu@iborg-desktop:~$ docker inspect alpine

Once you run the command, you'll notice three fields inside the Data subsection under GraphDriver:

...
        "GraphDriver": {
            "Data": {
                "MergedDir": "/var/lib/docker/overlay2/64c9c0cf8c9cfb0e2168071df0652a317d49f58a68fe86e4a9a9a525ab9e365e/merged",
                "UpperDir": "/var/lib/docker/overlay2/64c9c0cf8c9cfb0e2168071df0652a317d49f58a68fe86e4a9a9a525ab9e365e/diff",
                "WorkDir": "/var/lib/docker/overlay2/64c9c0cf8c9cfb0e2168071df0652a317d49f58a68fe86e4a9a9a525ab9e365e/work"
            },

...

All the above directory paths are the physical locations of the alpine image on the host system. Do take a note of the large hash named directory in the three fields above: 64c9c0cf8c9cfb0e2168071df0652a317d49f58a68fe86e4a9a9a525ab9e365e.

In total, there are actually four types of fields:

LowerDir: These are the read-only layers of an overlay filesystem. For docker, these are the image layers assembled in order.

UpperDir: This is the read-write layer of an overlay filesystem. For docker, that is the equivalent of the container specific layer that contains changes made by that container.

WorkDir: This is a required directory for overlay, it needs an empty directory for internal use.

MergedDir: This is the result of the overlay filesystem. Docker effectively chroot's into this directory when running the container.

Quoted from this reference (good for further reading).

Docker containers' location

Like images, containers are also stored inside the same storage driver based directory.

/var/lib/docker/overlay2

Specific container locations

If you are looking for the locations of specific containers, you can again use the inspect command on the running container.

Say, for example, I've run the alpine container with docker run -ti -d alpine . When you run docker ps, you'll see that it's running:

avimanyu@iborg-desktop:~$ docker ps
CONTAINER ID   IMAGE     COMMAND     CREATED         STATUS         PORTS     NAMES
cb341d6a28fa   alpine    "/bin/sh"   6 seconds ago   Up 5 seconds             confident_banzai

Here, the container has been randomly named confident_banzai. So let's inspect it:

avimanyu@iborg-desktop:~$ docker inspect confident_banzai

Once you run the above command, you'll notice all the four fields mentioned earlier inside the Data subsection under GraphDriver. These directories are where the container data is physically located on your host system:

...
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/d734685e284c92bdcb6063ac292a48813f30f4b0b2dd6fa2882279c569e506a3-init/diff:/var/lib/docker/overlay2/64c9c0cf8c9cfb0e2168071df0652a317d49f58a68fe86e4a9a9a525ab9e365e/diff",
                "MergedDir": "/var/lib/docker/overlay2/d734685e284c92bdcb6063ac292a48813f30f4b0b2dd6fa2882279c569e506a3/merged",
                "UpperDir": "/var/lib/docker/overlay2/d734685e284c92bdcb6063ac292a48813f30f4b0b2dd6fa2882279c569e506a3/diff",
                "WorkDir": "/var/lib/docker/overlay2/d734685e284c92bdcb6063ac292a48813f30f4b0b2dd6fa2882279c569e506a3/work"
            },
            "Name": "overlay2"
        },
...

Reference for further reading.

The above directories are the physical locations of your container data inside the host system. Remember the large hash-named directory: 64c9c0cf8c9cfb0e2168071df0652a317d49f58a68fe86e4a9a9a525ab9e365e from the Docker Images section? The directory under it is called diff, as you can see in the LowerDir section after :, which is now a mount point for the container - fetched from the base image UpperDir!

These directories will continue to be available even after you stop the container. So, the complete path which is common between the image (MergedDir, UpperDir and WorkDir) and the container (LowerDir mount point) is:

/var/lib/docker/overlay2/64c9c0cf8c9cfb0e2168071df0652a317d49f58a68fe86e4a9a9a525ab9e365e/

The purpose of the image is contiguous after assigning itself to the container upto the LowerDir level, which is why the four fields are allocated and based on a different directory (with a new hash) because of the dynamic nature of the latter, which becomes:

/var/lib/docker/overlay2/d734685e284c92bdcb6063ac292a48813f30f4b0b2dd6fa2882279c569e506a3/
Note: The directory mounting process from the base-image to the container is very similar to how you mount volumes on Docker!

Docker volumes location

Unlike Docker images and containers, the physical locations for volumes is pretty straightforward. Volumes are located at:

/var/lib/docker/volumes/

Specific Volume Locations

In this case, there are two types primarily. One is regular Docker volumes and the other is bind mounts.

Docker Volumes

If you are looking for the locations of specific volumes, you can use the docker volume ls command first and check the volume name or ID.

For example, I've run the alpine container with the following command with a volume:

avimanyu@iborg-desktop:~$ docker run -ti -d --name alpine-container -v test-data:/var/lib/app/content alpine

Now, a volume named test-data will automatically get created. Let's now create a file named test.md inside this location:

avimanyu@iborg-desktop:~$ docker exec alpine-container sh -c "touch /var/lib/app/content/test.md"

Verify the file has indeed been created:

avimanyu@iborg-desktop:~$ docker exec -ti alpine-container sh
/ # ls /var/lib/app/content/
test.md
/ # exit

When you run docker volume ls, the volume named test-data would be listed:

avimanyu@iborg-desktop:~$ docker volume ls
DRIVER    VOLUME NAME
local     d502589845f7ae7775474bc01d8295d9492a6c26db2ee2c941c27f3cac4449d1
local     e71ee3960cfef0a133d323d146a1382f3e25856480a727c037b5c81b5022cb1b
local     test-data

Finally, you can confirm the actual location of the file on your host system:

avimanyu@iborg-desktop:~$ sudo ls -l /var/lib/docker/volumes/test-data/_data
total 0
-rw-r--r-- 1 root root 0 Oct  6 23:20 test.md

Therefore, the path for the mounted volume is always located inside a directory named _data inside the respective volume directory.

You can also check such paths the Docker way by using the docker volume inspect command followed by the volume name or ID and looking into the Mountpoint parameter within the output:

avimanyu@iborg-desktop:~$ docker volume inspect test-data
[
    {
        "CreatedAt": "2021-10-06T23:20:20+05:30",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/test-data/_data",
        "Name": "test-data",
        "Options": null,
        "Scope": "local"
    }
]

Bind Mounts

Locations of bind mounts is pretty obvious and even more straightforward because you mount the volume directly from a host side location:

avimanyu@iborg-desktop:~$ mkdir /home/avimanyu/test-data
avimanyu@iborg-desktop:~$ docker run -ti -d --name alpine-container -v /home/avimanyu/test-data:/var/lib/app/content alpine

In this case, a bind mounted volume named test-data will become available on the container side as /var/lib/app/content.

Summary

In this quick tutorial, I've taken a generic Linux based approach to show the physical locations of Docker images, containers and volumes residing on your Linux server (Ubuntu 20.04 in this case) at the host level.

If you want to share any feedback, comment or suggestion towards this approach, please leave your thoughts in the comment section below.