Storage Drivers and overlay2

A storage driver turns an image's layers into the root filesystem a container sees. For a decade Docker 514 used its own graph drivers, and overlay2, built on OverlayFS and storing layers under /var/lib/docker/overlay2, became the default on every mainstream distribution. Engine 29 moved new installations to the containerd 234,762 image store, where containerd's snapshotters do the same job and keep data under /var/lib/containerd:

The storage driver and where the layers liveShell
docker info --format '{{.Driver}} {{json .DriverStatus}}'
sudo ls /var/lib/containerd | grep -E 'content|snapshotter'
sudo ls /var/lib/containerd/io.containerd.content.v1.content/blobs/sha256 | wc -l
sudo ctr -n moby images ls -q | grep l3-
Output
overlayfs [["driver-type","io.containerd.snapshotter.v1"]]
io.containerd.content.v1.content
io.containerd.snapshotter.v1.blockfile
io.containerd.snapshotter.v1.btrfs
io.containerd.snapshotter.v1.erofs
io.containerd.snapshotter.v1.native
io.containerd.snapshotter.v1.overlayfs
529
l3-layers:1.0

The work is split in two. The content store keeps every compressed blob exactly as downloaded, named by digest (529 on this host), which is what lets Engine push, save and verify images byte for byte and hold multi-platform images. The overlayfs snapshotter unpacks each layer once into a directory and mounts them as OverlayFS for each container, exactly as the classic overlay2 driver did. The price is disk space: each layer exists both compressed and unpacked, which is why docker image ls now reports disk usage alongside content size.

Other backends (btrfs, zfs) suit hosts whose data root lives on those filesystems, and native/vfs make full copies, useful only for debugging. A host upgraded from Engine 28 or earlier keeps overlay2 until you set "features": {"containerd-snapshotter": true} in daemon.json; switching hides images stored the old way, so docker save them first.