Docker Storage Driver

Docker containers appear lightweight because they do not require a complete filesystem to be copied for every new instance. Behind this efficiency is Docker’s storage driver, the component responsible for organizing image layers, creating container filesystems, and handling changes made at runtime. Understanding how it works is essential for troubleshooting disk usage, improving I/O performance, and preventing data loss.
A Docker image consists of an ordered stack of read-only layers. These layers are commonly produced by instructions in a Dockerfile, although modern build tools may create additional layers. Because multiple containers can reference the same image layers, identical images consume relatively little additional disk space.

When a container starts, Docker combines its image layers into a unified filesystem. A new writable layer is placed at the top, allowing processes inside the container to create and modify files without altering the original image.

This design relies on copy-on-write (CoW). If a container changes a file that exists in a lower, read-only layer, the storage driver preserves the original and places the modified copy in the writable layer. Reads then resolve to the newer version. As a result, Docker can start containers quickly without duplicating every file in the image.

What the Storage Driver Does Not Manage

Storage drivers primarily control:

  • Image layers stored on the local host
  • The writable layer created for each container
  • Layer mounting, snapshotting, and copy-on-write behavior

Persistent data volumes and bind mounts follow a different path. They use storage on the host filesystem rather than the container’s layered filesystem. This distinction matters when deciding where application data, database files, and user uploads should be stored.

Common Storage Drivers

Docker supports several storage drivers, each designed for different host environments.

  • overlay2: The recommended default on modern Linux systems. It uses the kernel’s OverlayFS implementation, performs efficiently, and supports copy-on-write. Most current Docker installations use this driver.
  • fuse-overlayfs: A user-space OverlayFS implementation frequently used with rootless Docker. It offers similar behavior when native OverlayFS is unavailable or unsuitable.
  • btrfs: Designed for hosts running the Btrfs filesystem. It provides advanced capabilities such as snapshots and copy-on-write at the filesystem level.
  • zfs: Integrates with ZFS on hosts that already use it, offering snapshots, checksumming, compression, and other ZFS features.
  • devicemapper: A legacy block-based driver historically common on CentOS and RHEL. It is deprecated and has been removed from recent Docker Engine releases.
  • vfs: A simple compatibility driver that copies complete directories instead of sharing layers efficiently. It consumes significantly more disk space and should generally be limited to testing or unusual recovery scenarios.

Checking the Active Driver

The docker info command displays the configured storage driver and the backing filesystem:

docker info | grep -i "storage driver"

A typical result looks like this:

 Storage Driver: overlay2
  Backing Filesystem: ext4

The backing filesystem indicates which host filesystem supports Docker’s data directory. It is also useful when investigating problems involving disk pressure, filesystem limitations, or performance.

For a more direct view of the driver name, use:

docker info --format '{{.Driver}}'

Configuring a Storage Driver

On Linux, the storage driver can be specified in /etc/docker/daemon.json:

{
  "storage-driver": "overlay2"
}

After validating the JSON file, restart the Docker service:

sudo systemctl restart docker

Then confirm that the new driver is active:

docker info

Changing the storage driver is not a routine configuration change. Existing images and containers were created using the previous driver’s internal directory structure, so Docker may be unable to access them after the switch. Before changing drivers:

  • Push important images to a registry or export them with docker save.
  • Back up application data stored in volumes or bind mounts.
  • Stop workloads cleanly and document the migration procedure.
  • Verify available disk space in the Docker data directory.
  • Avoid combining a driver migration with an operating-system upgrade unless the vendor’s documentation explicitly supports it.

Choosing the Right Driver

For most Linux hosts, overlay2 is the safest choice. It provides good performance, efficient layer sharing, and broad compatibility with current Docker Engine releases. Its backing filesystem is commonly ext4 or xfs.

When selecting a driver, consider the environment as a whole:

  1. Follow the distribution’s guidance. Some platforms maintain driver packages, kernel requirements, or patches that affect compatibility.
  2. Use native filesystem features where appropriate. Btrfs and ZFS are compelling when the host is already standardized on those filesystems.
  3. Account for rootless operation. Rootless environments often work best with fuse-overlayfs.
  4. Avoid vfs for production. Its lack of efficient layer sharing can multiply disk usage and degrade container performance.
  5. Check kernel and filesystem support. Features such as d_type must be enabled and supported for reliable operation.

Store Persistent Data in Volumes

Applications should not rely on a container’s writable layer for durable data. That layer is tied to the container lifecycle and is removed when the container is deleted. Large writable layers can also slow container shutdown, removal, and image cleanup.

A named volume provides a better location for persistent application data:

docker run -d \
  --name myapp \
  --mount source=appdata,target=/var/lib/myapp \
  nginx:latest

Named volumes are managed separately from the container and are typically stored under the Docker data root, often at:

/var/lib/docker/volumes/

They continue to exist after the container is removed, making them suitable for databases, uploads, caches that must survive restarts, and other important state. Bind mounts are another common option, especially when files must remain directly accessible on the host.

Operational Best Practices

Monitor Docker’s disk consumption regularly:

docker system df

Unused images, stopped containers, build caches, and volumes can all contribute to storage pressure. Use pruning commands carefully, and never remove a volume until its contents have been backed up or are no longer needed.

Log rotation should also be configured. By default, some deployments allow JSON log files to grow without limit, eventually filling the host disk.

Finally, treat storage-driver configuration as part of infrastructure management. Keep daemon.json under version control, validate changes before restarting Docker, and test driver migrations in a nonproduction environment. With overlay2, properly mounted persistent volumes, and routine disk monitoring, Docker’s storage layer can remain both efficient and reliable.