How to Set Disk Quotas per Directory in Linux: A Practical Guide for Developers


1 views

Many developers face situations where they need to limit disk usage for specific directories rather than entire partitions. A common scenario involves PostgreSQL installations where tablespaces (/pg/tbs1, /pg/tbs2, etc.) need individual size restrictions while sharing the same underlying filesystem.

The conventional method involves creating loopback devices:


# Create a 10GB file for the quota
dd if=/dev/zero of=/pg/tbs1.img bs=1G count=10
mkfs.ext4 /pg/tbs1.img
mount -o loop,rw,usrquota,grpquota /pg/tbs1.img /pg/tbs1

While this works, it creates management overhead and makes expansion difficult. You'd need to:

  1. Unmount the directory
  2. Resize the image file
  3. Check and resize the filesystem
  4. Remount the device

For modern Linux systems, consider these alternatives:

1. Using project quotas (XFS)

XFS supports project-level quotas that can be applied to directories:


# Format the partition as XFS
mkfs.xfs /dev/sdX

# Enable project quotas
mount -o prjquota /dev/sdX /mnt

# Set up project IDs
echo "42:/pg/tbs1" >> /etc/projects
echo "tbs1:42" >> /etc/projid

# Initialize quota
xfs_quota -x -c 'project -s tbs1'

# Set 10GB limit
xfs_quota -x -c 'limit -p bhard=10g tbs1'

2. Using btrfs quotas

If using btrfs, subvolume quotas offer similar functionality:


# Create subvolume
btrfs subvolume create /pg/tbs1

# Enable quota
btrfs quota enable /pg

# Set 10GB limit
btrfs qgroup limit 10G /pg/tbs1

3. Using LVM thin provisioning

For more advanced control, combine LVM thin provisioning with directory binding:


# Create thin pool
lvcreate -L 100G -T vg00/thin_pool

# Create thin volume
lvcreate -V 10G -T vg00/thin_pool -n tbs1

# Format and mount
mkfs.ext4 /dev/vg00/tbs1
mount /dev/vg00/tbs1 /pg/tbs1

Consider these factors when selecting a solution:

Method Filesystem Flexibility Complexity
Project Quotas XFS High Medium
btrfs Quotas btrfs High Low
LVM Thin Any Medium High
Loopback Any Low Low

Here's a complete example using XFS project quotas for PostgreSQL tablespaces:


#!/bin/bash
# Initialize XFS project quotas for PG tablespaces

TBS_DIRS=("/pg/tbs1" "/pg/tbs2" "/pg/tbs3")
SIZES=("10G" "20G" "15G")

# Initialize project files
echo "PG Tablespace Quotas" > /etc/projects
echo "PG Tablespace Quotas" > /etc/projid

for i in "${!TBS_DIRS[@]}"; do
    # Assign project IDs
    PROJECT_ID=$((1000+i))
    echo "${PROJECT_ID}:${TBS_DIRS[i]}" >> /etc/projects
    echo "tbs$((i+1)):${PROJECT_ID}" >> /etc/projid
    
    # Initialize quota
    xfs_quota -x -c "project -s tbs$((i+1))"
    xfs_quota -x -c "limit -p bhard=${SIZES[i]} tbs$((i+1))"
    
    # Set directory attributes
    chattr +P ${TBS_DIRS[i]}
done

Regularly check quota usage with:


# For XFS
xfs_quota -x -c 'report -h'

# For btrfs
btrfs qgroup show -re /pg

# For LVM
lvs -o+metadata_percent

Remember to update your fstab for persistent mounts and consider automating quota reports through cron jobs or monitoring systems.


Linux's native quota system operates at the filesystem or user/group level, but many real-world scenarios require directory-level restrictions. This becomes particularly important in database management (like your PostgreSQL tablespace directories), shared hosting environments, or multi-tenant systems where different projects need isolated storage limits.

While creating loopback filesystem images works, it introduces several drawbacks:

# Traditional loopback approach example
dd if=/dev/zero of=/pg/tbs1.img bs=1G count=100
mkfs.ext4 /pg/tbs1.img
mount -o loop,rw,usrquota,grpquota /pg/tbs1.img /pg/tbs1

The main issues with this method include inflexible resizing, performance overhead, and complex management during storage expansion.

1. Project Quotas (XFS/ext4)

Modern Linux filesystems support project quotas which can map to directories:

# Enable project quotas on filesystem
tune2fs -O project /dev/sdX
mount -o prjquota /dev/sdX /pg

# Configure for a directory
echo "42:/pg/tbs1" >> /etc/projects
echo "dbspace:/pg/tbs1" >> /etc/projid
xfs_quota -x -c 'project -s dbspace'
xfs_quota -x -c 'limit -p bhard=100G dbspace'

2. Linux Container (LXC/LXD) Approach

For isolated directory trees, consider containerization:

lxc launch ubuntu:20.04 db-container
lxc config device add db-container pg-disk disk source=/pg/tbs1 path=/mnt/pg
lxc config set db-container limits.disk.size 100GB

3. Btrfs Subvolume Quotas

If using Btrfs, subvolumes provide native quota support:

btrfs subvolume create /pg/tbs1
btrfs qgroup limit 100G /pg/tbs1

For a PostgreSQL tablespace scenario using XFS project quotas:

#!/bin/bash
# Initialize project IDs
for i in {1..3}; do
  echo "$i:/pg/tbs$i" >> /etc/projects
  echo "pgtbs$i:$i" >> /etc/projid
done

# Set 100GB limit per tablespace
for i in {1..3}; do
  xfs_quota -x -c "project -s pgtbs$i"
  xfs_quota -x -c "limit -p bhard=100G pgtbs$i"
done

To check current usage:

xfs_quota -x -c 'report -h'

Configure alerts when nearing limits by parsing this output and integrating with monitoring tools like Nagios or Prometheus.

For environments where modifying filesystems isn't possible, consider:

  • Regular cron jobs checking directory sizes with du
  • Inotify-based monitoring with custom enforcement scripts
  • Implementing soft limits through application logic

Remember that some approaches require specific filesystem types (XFS, Btrfs) or kernel versions. Always test quota enforcement mechanisms before deploying to production.