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:
- Unmount the directory
- Resize the image file
- Check and resize the filesystem
- 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.