How to Set Docker Container Storage Limits Using storage-opt with XFS and Overlay2
Introduction: Why You Need Storage Limits in Docker
By default, Docker does not enforce a strict storage limit on a container's writable layer. Each container has its own writable layer, which resides on the host's disk — usually under /var/lib/docker.
That means containers can continue writing data until they consume the entire host filesystem. If left unchecked, this can create serious problems:
Risks of Not Setting Storage Limits
-
Disk Space Exhaustion
- Containers can fill up the host’s disk.
- New containers may fail to start.
- Even system processes — including root login — may fail.
-
Performance Degradation
- Disk I/O latency increases as storage fills.
- Docker’s overlay storage driver slows down with a bloated writable layer.
-
Data Loss & Instability
- Applications may crash or behave unpredictably.
- File writes can fail, causing corruption or inconsistent states.
To prevent this, Docker provides the storage-opt option, which allows you to enforce per-container storage limits. This guide walks you through configuring it from scratch with overlay2 and XFS.
How Docker Enforces Storage Limits with storage-opt
When you run a container with --storage-opt size=1G, Docker doesn’t apply the limit directly. Instead, it depends on:
- Storage Driver: For most Linux systems, this is
overlay2. - Filesystem Support: XFS with project quotas (
pquota) enabled.
Only when both conditions are met can Docker successfully apply storage-opt.
To know more about other supported storage drivers and backing filesystems, see the official Docker storage drivers documentation.
Step-by-Step Guide: Enabling storage-opt for Docker
Let’s go through the process of configuring storage limits for Docker containers.
Step 1: Configure the Backing Filesystem
In this example, we’ll create a dedicated disk for Docker data, using LVM with an XFS filesystem.
1# I intend to use 'sdb' for docker.
2$ lsblk | grep sdb
3sdb 8:16 0 50G 0 disk
4
5# Create a physical volume
6$ sudo pvcreate /dev/sdb
7
8# Create a volume group
9$ sudo vgcreate docker_vg /dev/sdb
10
11# Create a logical volume
12$ sudo lvcreate -l 100%FREE -n docker_lv01 docker_vg
13
14# Format the LV with XFS filesystem
15$ sudo mkfs.xfs /dev/docker_vg/docker_lv01
Step 2: Mount Docker Data Directory
Now we’ll mount the newly created filesystem at /var/lib/docker.
- First, fetch the UUID of the logical volume:
1$ blkid /dev/docker_vg/docker_lv01
2/dev/docker_vg/docker_lv01: UUID="38266c4b-da6b-4df8-a5c4-b0f1e0342194" BLOCK_SIZE="512" TYPE="xfs"
- Add the following entry to
/etc/fstabfor persistence:
1UUID=38266c4b-da6b-4df8-a5c4-b0f1e0342194 /var/lib/docker xfs defaults 0 0
- Then, mount and verify:
1$ sudo mkdir /var/lib/docker
2$ sudo mount -a
3$ df -hT
You should see /var/lib/docker mounted with XFS.
Step 3: Install Docker
Docker can be installed via:
- APT repository
- Manual package
- Convenience script
For detailed instructions, refer to my Docker installation guide.
Step 4: Verify the Storage Driver
Check which storage driver Docker is using:
1$ docker info | grep "Storage Driver"
2 Storage Driver: overlay2
If you see overlay2, you’re good to proceed.
Step 5: Run a Test Container with Storage Limit
Try running an Nginx container with a 1GB storage limit:
1$ docker run -d --storage-opt size=1G nginx
You may encounter this error:
1docker: Error response from daemon: --storage-opt is supported only for overlay over xfs with 'pquota' mount option
This means you need to enable project quotas.
Step 6: Enable Project Quotas (pquota)
Check current mount options:
1$ findmnt /var/lib/docker
2TARGET SOURCE FSTYPE OPTIONS
3/var/lib/docker /dev/mapper/docker_vg-docker_lv01 xfs rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota
If you don’t see prjquota (only noquota), update /etc/fstab with pquota :
1UUID=38266c4b-da6b-4df8-a5c4-b0f1e0342194 /var/lib/docker xfs rw,pquota 0 1
Apply the changes:
1sudo systemctl stop docker
2sudo umount /var/lib/docker
3sudo mount -a
4sudo systemctl start docker
Confirm with:
1$ findmnt /var/lib/docker
2/var/lib/docker ... xfs rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,prjquota
Now pquota is enabled.
Step 7: Test Storage Limit Enforcement
Run the container again:
1$ docker run --name nginx -d --storage-opt size=1G nginx
Verify with:
1$ docker inspect nginx | jq -r '.[0].HostConfig.StorageOpt'
2{
3 "size": "1G"
4}
Step 8: Validate by Writing Data Inside Container
Exec into the container and try writing files:
1$ docker exec -it nginx bash
2root@10beafafdbce:/# df -kh
3Filesystem Size Used Avail Use% Mounted on
4overlay 1.0G 20K 1.0G 1% /
5tmpfs 64M 0 64M 0% /dev
6shm 64M 0 64M 0% /dev/shm
7/dev/mapper/docker_vg-docker_lv01 50G 588M 50G 2% /etc/hosts
8tmpfs 5.9G 0 5.9G 0% /proc/acpi
9tmpfs 5.9G 0 5.9G 0% /proc/scsi
10tmpfs 5.9G 0 5.9G 0% /sys/firmware
11tmpfs 5.9G 0 5.9G 0% /sys/devices/virtual/powercap
12
13# Try writing a 1.5GB file
14fallocate -l 1.5G myfile
15# Output: No space left on device
16
17# Write a smaller file within limits
18fallocate -l 0.5G myfile
19
20root@10beafafdbce:/# df -kh
21Filesystem Size Used Avail Use% Mounted on
22overlay 1.0G 513M 512M 51% /
23tmpfs 64M 0 64M 0% /dev
24shm 64M 0 64M 0% /dev/shm
25/dev/mapper/docker_vg-docker_lv01 50G 1.1G 49G 3% /etc/hosts
26tmpfs 5.9G 0 5.9G 0% /proc/acpi
27tmpfs 5.9G 0 5.9G 0% /proc/scsi
28tmpfs 5.9G 0 5.9G 0% /sys/firmware
29tmpfs 5.9G 0 5.9G 0% /sys/devices/virtual/powercap
This proves the limit works.
Bonus: Check Storage Quotas from Linux
Docker relies on the Linux kernel to enforce quotas. You can check the project quota assigned to the container:
1# Get container ID
2$ docker ps
3e3c3628826d2 nginx "/docker-entrypoint.…" 24 minutes ago Up 24 minutes 80/tcp nginx
4
5# Find overlay UpperDir
6$ docker inspect -f '{{ .GraphDriver.Data.UpperDir }}' e3c3628826d2
7/var/lib/docker/overlay2/ff7f2c5a832681c29ed86cf41ad84f14aac2bffe288d4553712b8d8b172847fd/diff
8
9# Check project ID
10$ sudo xfs_io -c "lsproj" /var/lib/docker/overlay2/ff7f2c5a832681c29ed86cf41ad84f14aac2bffe288d4553712b8d8b172847fd/diff
11projid = 3
12
13# Report quota usage
14$ sudo xfs_quota -x -c "report -h" /var/lib/docker | grep 3
15#3 512.0M 1G 1G 00 [------]
This shows how much storage the container has actually consumed at the filesystem level.
Conclusion
Setting storage limits in Docker with storage-opt is essential for:
- Preventing containers from consuming all disk space.
- Maintaining host performance and stability.
- Ensuring data integrity and workload reliability.
By using overlay2 + XFS with project quotas, you can confidently enforce per-container storage quotas in production.
Now you have a fully working setup to protect your Docker host from runaway containers.