Troubleshooting Docker and Containerd Overlay2 Storage Driver Exhaustion, Unlinked Upperdir Inode Leaks, and Filesystem Read-Only Corruptions Runbook

An exhaustive Linux sysadmin runbook for diagnosing and recovering from Docker and Containerd overlay2 storage exhaustion, unlinked upperdir inode leaks, XFS ftype=1 incompatibilities, and emergency read-only filesystem remounts.

Troubleshooting Docker and Containerd Overlay2 Storage Driver Exhaustion, Unlinked Upperdir Inode Leaks, and Filesystem Read-Only Corruptions Runbook

Modern containerized platforms, high-density microservices fleets, and continuous delivery workers running on Docker Engine or containerd rely on the overlay2 storage driver to deliver lightweight, copy-on-write container filesystems.[1][2] Under normal operating conditions, OverlayFS allows containers to share immutable lower image layers while maintaining independent, writable top layers with minimal memory and disk overhead.[1]

However, production Linux nodes frequently suffer catastrophic outages where the underlying storage layer becomes abruptly unresponsive. Systems administrators commonly encounter perplexing failure modes: commands inside running containers crash with no space left on device even when free disk space shows gigabytes remaining, the Docker daemon refuses to spawn new containers with failed to create containerd task: failed to mount overlay: structure needs cleaning, or the host Linux kernel suddenly drops the root block device into an emergency read-only state (Remounting filesystem read-only).[1][3]

These disruptions are rarely caused by simple disk capacity saturation. Instead, they stem from architectural characteristics of the Linux VFS (Virtual File System) OverlayFS implementation: inode exhaustion, unlinked open file descriptor leaks in the upperdir, backing filesystem d_type incompatibilities, or corrupted metadata whiteout devices.[1][2][4] This diagnostic runbook provides an exhaustive, terminal-level reference for systems engineers, site reliability engineers (SREs), and DevOps administrators to triage, recover, and permanently mitigate container storage driver failures.[2]


Understanding the Linux OverlayFS Architecture in overlay2

To isolate failure modes, systems engineers must understand how the Linux kernel constructs an OverlayFS mount.[1] OverlayFS unifies multiple directory trees into a single virtual presentation layer consisting of four fundamental components:[1][2]

OverlayFS Directory Component Operational Function Permissions / Mutability Inode & Storage Impact
lowerdir Immutable base image layers stacked sequentially. Base operating systems, runtime runtimes, and application binaries reside here. Read-Only (ro) Inodes allocated during image pull; shared across multiple running containers.[2]
upperdir Per-container ephemeral read-write layer where all new file creations, modifications, and runtime writes land. Read-Write (rw) Consumes inodes and blocks directly from host backing filesystem during container runtime.[1]
workdir Kernel scratchpad directory on the backing filesystem used to prepare files atomically before moving them into upperdir. Internal Kernel Scratch Used during copy-up and whiteout creation; requires same filesystem as upperdir.[1]
merged Unified Linux mount point exposed inside the container as the root filesystem (/). Virtual VFS View Does not consume independent inodes; reflects composition of lower and upper layers.[1]

When a process inside a container modifies an existing file that originated in an underlying image layer, the Linux kernel triggers a copy-up operation: the complete file is copied from lowerdir into upperdir before writing the first modified byte.[1]

When a process inside a container deletes a file that exists in lowerdir, the file cannot be removed from the read-only layer. Instead, OverlayFS writes a whiteout character device (c 0 0) inside upperdir matching the target filename.[1] If an entire directory is deleted or replaced, OverlayFS sets an extended file attribute: trusted.overlay.opaque="y".[1] If thousands of temporary files are generated and deleted per minute inside unmounted container paths (such as un-volumed cache directories, batch jobs, or dynamic web uploads), upperdir becomes cluttered with millions of whiteout inodes and uncommitted directory entries, driving the host toward metadata exhaustion.[1][2]


Triage Phase 1: Inode Exhaustion vs. Block Space Saturation

When an application reports no space left on device (Linux error code ENOSPC), standard tooling such as df -h often displays ample available capacity.[3] On Linux filesystems (Ext4, XFS), storage capacity is governed by two independent metrics: data blocks (storing byte contents) and inodes (index nodes storing metadata, permissions, timestamps, and block pointers).[3][6]

Execute a dual-vector storage check on the host terminal:

# Verify byte capacity
df -h /var/lib/docker

# Verify inode allocation percentage
df -i /var/lib/docker

A compromised node experiencing inode exhaustion exhibits output similar to this diagnostic trace:

Filesystem                  Inodes   IUsed   IFree IUse% Mounted on
/dev/mapper/ubuntu--vg-root 6553600 6553592       8  100% /

When IUse% reaches 100%, the Linux Virtual File System rejects every subsequent open(), creat(), mkdir(), or socket() system call with ENOSPC, freezing Docker daemon event loops, logging subsystems, and DNS resolution caches.[3]

To locate the specific overlay2 layer consuming millions of inodes, run this high-speed traversal pipeline:

find /var/lib/docker/overlay2 -maxdepth 2 -type d -name "diff" -exec sh -c '
    echo "$(find "$1" | wc -l) $1"
' sh {} + | sort -rn | head -n 10

Once you identify the offending directory hash (for example, /var/lib/docker/overlay2/c4f98a72b.../diff), map the storage hash to its active container ID:

docker inspect $(docker ps -q) | grep -B 10 -A 20 "c4f98a72b" | grep -E '"Id"|"Name"'

Triage Phase 2: Unlinked Open File Descriptor Leaks (lsof +L1)

A insidious cause of phantom storage exhaustion occurs when an application writes large files or high frequencies of temporary files, deletes them with unlink() or rm, but keeps the underlying file descriptor open.[4]

In POSIX compliance, deleting an open file unlinks its directory entry immediately from the namespace, but the Linux VFS cannot reclaim the underlying inode or disk blocks until all active processes close their open file descriptors.[4] Consequently, du -sh /var/lib/docker will report low disk usage, while df -h and df -i report complete saturation.[4]

Identify all unlinked file descriptors held by container processes using lsof with the link count filter +L1:

lsof +L1 /var/lib/docker

Review the diagnostic output to determine the process ID and deleted file magnitude:

COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF NLINK    NODE NAME
node      4128 root   18u   REG  253,0 48291048     0 3981240 /var/lib/docker/overlay2/.../merged/tmp/session-cache (deleted)
php-fpm   9481 root    4w   REG  253,0 12894012     0 3981248 /var/lib/docker/overlay2/.../merged/var/log/debug.log (deleted)

To reclaim the unlinked inodes and blocks without restarting the host server, gracefully terminate or reload the specific child process holding the descriptor:

# Gracefully signal the specific container process
kill -15 4128

# If the process is unresponsive, terminate via Docker
docker kill --signal=SIGTERM <container_id>

Backing Filesystem Requirements: Verifying XFS ftype=1

The overlay2 storage driver strictly requires that the underlying backing filesystem supports directory type flags (d_type).[2][5] The d_type flag allows file system readdir functions to return file types without issuing individual stat() system calls for every directory entry, which OverlayFS relies upon for whiteout detection.[1][5]

On Red Hat Enterprise Linux, CentOS, AlmaLinux, or Rocky Linux systems formatted with older XFS defaults, ftype may be disabled (ftype=0).[2][5] Running Docker or containerd on an XFS partition with ftype=0 causes unrecoverable copy-up failures, silent inode corruptions, and abrupt container crashes.[2]

Inspect your active XFS configuration using xfs_info:

xfs_info /var/lib/docker

Examine the naming parameter line in the terminal output:

meta-data=/dev/sdb1              isize=512    agcount=4, agsize=6553600 blks
         =                       sectsz=4096  attr=2, projid32bit=1
data     =                       bsize=4096   blocks=26214400, imaxpct=25
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=4096   blocks=12800, version=2

[!IMPORTANT] If ftype=0 is reported, OverlayFS cannot operate reliably.[2] Existing XFS filesystems cannot have ftype toggled online via tune2fs or xfs_admin. The partition must be backed up, reformatted with mkfs.xfs -n ftype=1 /dev/sdb1, and restored.[2][5]


Emergency Procedure: Recovering from Read-Only Filesystem Remounts

When the Linux VFS detects metadata inconsistencies in /var/lib/docker/overlay2, broken block pointers, or unhandled I/O journal failures, the kernel driver invokes an emergency protective remount (errors=remount-ro).[3] When this occurs, any write operation fails with:

touch: cannot touch '/var/lib/docker/test': Read-only file system

Follow this strict sysadmin sequence to recover the filesystem without destroying operational data:[3][6]

# Step 1: Immediately stop all container engine daemons
systemctl stop docker
systemctl stop containerd

# Step 2: Unmount any lingering overlay mounts held by orphaned containers
umount -R /var/lib/docker/overlay2/* 2>/dev/null

# Step 3: Run filesystem consistency check on the underlying block volume
# For Ext4 filesystems:
e2fsck -fy /dev/mapper/ubuntu--vg-docker

# For XFS filesystems (must be unmounted):
umount /var/lib/docker
xfs_repair /dev/sdb1

# Step 4: Remount the volume in read-write mode
mount -o remount,rw /var/lib/docker

# Step 5: Start the container engines
systemctl start containerd
systemctl start docker

Once the daemon restarts, review kernel logs with dmesg -T | grep -E "EXT4|XFS|overlayfs" to ensure that journal replays completed cleanly and hardware I/O errors are absent.[3]


Production Prevention & Daemon Configuration

To ensure continuous container stability and prevent repeat incidents of inode and block saturation, implement these operational hardening configurations:[2]

1. Enforce Global Container Log Rotation

Unconstrained container logs (json-file) write to /var/lib/docker/containers/<id>/<id>-json.log, consuming both data blocks and directory inodes. Enforce strict limits globally in /etc/docker/daemon.json:

{
  "storage-driver": "overlay2",
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "50m",
    "max-file": "3"
  }
}

Reload the daemon configuration without dropping active workloads:

systemctl reload docker

2. Segregate Container Storage on Dedicated NVMe Partitions

In multi-tenant or enterprise environments, running /var/lib/docker directly on the host operating system’s root filesystem (/) represents a critical single point of failure. When container logs or application temporary files exhaust all inodes on /, essential host services (OpenSSH, Cron, SSSD, and auditd) crash, preventing administrator access.

For high-throughput workloads, deploy container infrastructure on enterprise hardware with dedicated secondary NVMe storage arrays. By utilizing high-bandwidth dedicated servers and localized dedicated servers in Pakistan, DevOps teams can isolate container storage onto high-speed XFS partitions formatted specifically with large inode ratios (-i size=512,maxpct=40), insulating the host OS from container volume surges.

3. Enforce Volume Mounts for High-I/O Application Paths

Never allow database engines (MySQL, PostgreSQL, Redis), search clusters (Elasticsearch), or persistent cache directories to write directly to the container’s ephemeral upperdir. Direct writes to upperdir trigger continuous OverlayFS copy-up operations and inode overhead.[1][2] Always map these paths to named Docker volumes or host bind mounts, routing I/O directly to the native Linux filesystem.


Systematic Cleanup & Maintenance Automation

To maintain a healthy container ecosystem, schedule automated maintenance routines to eliminate dangling layers, stopped build containers, and orphaned snapshot layers:

# Safe prune of unreferenced build cache and dangling images
docker system prune -f

# Comprehensive cleanup including unused named volumes (caution in production)
docker system prune -a --volumes --filter "until=168h"

Verify that all active layers in /var/lib/docker/overlay2 correspond to registered container objects. Any directory in overlay2 that does not match an entry in docker image ls or docker ps -a indicates an orphaned snapshot resulting from a previous ungraceful daemon crash, safe to reclaim following container daemon verification.[2]


Sources & Official References
  1. Linux Kernel Organization. (2026). Documentation for OverlayFS. Retrieved from https://docs.kernel.org/filesystems/overlayfs.html
  2. Docker Documentation. (2026). Use the OverlayFS Storage Driver. Retrieved from https://docs.docker.com/engine/storage/drivers/overlayfs-driver/
  3. Linux Kernel Documentation. (2026). Filesystems in the Linux Kernel: Error Handling & Remount Behaviors. Retrieved from https://docs.kernel.org/filesystems/index.html
  4. Linux Programmer's Manual. (2026). lsof(8) - List Open Files and Link Count Inspection (+L). Retrieved from https://man7.org/linux/man-pages/man8/lsof.8.html
  5. Linux Programmer's Manual. (2026). xfs_info(8) - Display XFS Filesystem Geometry & ftype Parameters. Retrieved from https://man7.org/linux/man-pages/man8/xfs_info.8.html
  6. Linux Programmer's Manual. (2026). tune2fs(8) - Adjust Tunable Filesystem Parameters on Ext2/Ext3/Ext4 Filesystems. Retrieved from https://man7.org/linux/man-pages/man8/tune2fs.8.html

Deploy Resilient High-Performance Container Infrastructure

Eliminate disk bottlenecks, inode exhaustion, and multi-tenant performance degradation with Nextgen's high-speed bare-metal hardware. Deploy enterprise NVMe storage arrays with dedicated I/O channels engineered specifically for Docker, containerd, and Kubernetes clusters.

Disclaimer: This post is for informational and educational purposes only, accurate to the best of our knowledge at publication. It does not constitute advice. We do not guarantee accuracy or completeness and accept no liability for any decisions or losses resulting from reliance on this content.

Disclaimer: This post is for informational and educational purposes only, accurate to the best of our knowledge at publication. It does not constitute advice. We do not guarantee accuracy or completeness and accept no liability for any decisions or losses resulting from reliance on this content.