The Complete Linux EXT4 Disk Quota Guide

From Absolute Beginner to Production Expert (RHEL 8/9/10)
Last Updated: November 2025
Target Systems: RHEL 8, 9, 10 with EXT4 filesystem
Knowledge Level: Beginner to Advanced
Table of Contents
What is Disk Quota? (The Simple Story)
Understanding Quota Concepts
How Quotas Work Inside Linux
Setting Up Quotas on EXT4 (Step-by-Step)
Managing Quotas (Daily Operations)
Advanced Scenarios & Troubleshooting
Production Best Practices
Part 1: What is Disk Quota? (The Simple Story)
The Library Card Analogy π
Imagine you run a public library with 1,000 books. If you let people borrow unlimited books:
One person might check out 200 books and keep them for months
Others can't find books they need
Library becomes unusable
Your library rules:
Each person can borrow 5 books normally (comfortable limit)
They can temporarily borrow up to 8 books (absolute maximum)
If they have 6-8 books, they get 2 weeks to return extras
After 2 weeks with too many books, they can't borrow more until they return some
Linux disk quota works exactly the same:
5 books = Soft limit (recommended usage)
8 books = Hard limit (absolute maximum)
2 weeks = Grace period (time to fix the situation)
Can't borrow more = Can't write new files
Real-World Example
Without Quota:
Server has 1 TB total space
User John: Downloads 700 GB of videos
User Mary: Has 50 GB of work files
User Tom: Has 20 GB of project data
Result: Server only has 230 GB left
John's downloads prevent others from working β
With Quota:
Server has 1 TB total space
User John: Limited to 200 GB (soft: 180 GB) β
User Mary: Limited to 200 GB (soft: 180 GB) β
User Tom: Limited to 200 GB (soft: 180 GB) β
Result: Fair distribution
Everyone can work
400 GB free for new users β
Why Do We Need Quotas?
Fairness - Everyone gets their fair share of storage
Prevention - One user can't accidentally fill the entire disk
Planning - You know exactly how many users you can support
Accountability - Users think twice before saving everything
System Stability - Server never runs completely out of space
Part 2: Understanding Quota Concepts
The Two Types of Limits
Think of your phone:
Storage space - How much data you can store (in GB)
Number of apps - How many apps you can install (count)
Linux tracks both:
1. Block Quota = Storage Space Limit
Controls how much disk space you can use
Measured in 1 KB blocks (not filesystem blocks!)
Example: "You can store up to 10 GB of files"
Important: Linux quota counts in 1024-byte units
Block calculation:
Want 5 GB limit?
5 GB = 5 Γ 1024 Γ 1024 KB = 5,242,880 blocks
Want 500 MB limit?
500 MB = 500 Γ 1024 KB = 512,000 blocks
2. Inode Quota = File Count Limit
Controls how many files and folders you can create
Measured in number of items (each file/folder = 1 inode)
Example: "You can create up to 10,000 files"
Important: Every directory counts as 1 inode (this includes the user's home directory)
Why Track Both?
Real scenario:
User Alice: Saves 1 large video file
- 5 GB size β Uses 5,242,880 blocks β
- 1 file + 1 directory β Uses 2 inodes β
- Hits BLOCK quota first
User Bob: Saves 50,000 tiny text files
- 50 MB total size β Uses 51,200 blocks β
- 50,000 files + 1 directory β Uses 50,001 inodes β
- Hits INODE quota first
Different users need different controls!
Soft Limit vs Hard Limit (The Library Card Model)
This is the heart of the quota system. Let's use our library analogy:
Soft Limit = Comfortable Borrowing Limit
"This is how many books you should normally have"
Can be temporarily exceeded if you need a few extra days
Like having 6 books when limit is 5
You get a grace period to return the extras
Hard Limit = Absolute Maximum
"You can NEVER exceed this, no exceptions"
Enforced immediately - no grace period
Like trying to borrow a 9th book when maximum is 8
Librarian says NO right away
Grace Period = The Warning Timer
Time allowed to stay above soft limit
"You have 7 days to return those extra books"
After grace expires, soft limit becomes as strict as hard limit
Grace period is per filesystem, not per user
The Complete Picture (Example)
Block Soft Limit: 180 GB (comfortable usage)
Block Hard Limit: 200 GB (absolute ceiling)
Grace Period: 7 days (time to clean up)
What happens in practice:
| User's Storage | System Response | Can Write Files? |
| 100 GB | β Normal - everything fine | YES |
| 185 GB | β οΈ Over soft limit - grace timer starts (7 days) | YES (temporarily) |
| 185 GB + 8 days later | β Grace expired - soft acts like hard | NO* |
| 201 GB (tries to go over) | π Blocked immediately at hard limit | NO |
| Cleans up to 170 GB | β Grace resets - back to normal | YES |
Note: *Grace enforcement is "lazy" - you'll be blocked on your next write attempt after grace expires, not exactly at the expiration moment.
Visual Timeline: Grace Period in Action
Day 1: User has 150 GB [β
NORMAL]
Writing files freely
Day 2: User uploads large dataset β 185 GB [β οΈ SOFT LIMIT EXCEEDED]
βββββββββββββββββββββββββββββββββββββββ
β Grace period starts: 7 days β
β Warning shown: "Please clean up" β
βββββββββββββββββββββββββββββββββββββββ
Still can write files (temporary allowance)
Day 4: Still at 185 GB [β οΈ 5 days remaining]
Can still write, but should clean up
Day 9: Grace period expired! [β ENFORCEMENT BEGINS]
Next write attempt:
$ touch /data/newfile.txt
touch: cannot touch 'newfile.txt': Disk quota exceeded
User deletes 20 GB β now at 165 GB [β
GRACE RESETS]
βββββββββββββββββββββββββββββββββββββββ
β Back under soft limit β
β Grace timer reset β
βββββββββββββββββββββββββββββββββββββββ
Can write freely again
Key takeaway: Grace period is like a "soft warning phase" that becomes strict enforcement after time expires.
Part 3: How Quotas Work Inside Linux
The Kernel's Job (Simple Explanation)
Every time you create or write to a file, the Linux kernel performs quota checks:
1. User types: echo "hello" > myfile.txt
2. Kernel checks (in this order):
A. INODE CHECK (file count):
ββ Current inodes used: 500
ββ Inode hard limit: 1000
ββ Is 500 < 1000? β
YES β Continue
ββ If NO β STOP, show error
B. INODE SOFT LIMIT CHECK:
ββ Current: 500
ββ Soft limit: 800
ββ Is 500 < 800? β
YES β No grace timer needed
ββ If NO β Start/check grace period
C. BLOCK CHECK (space):
ββ Current blocks: 5,000,000 (about 4.7 GB)
ββ New file needs: 1,024 blocks (1 MB)
ββ Total would be: 5,001,024
ββ Block hard limit: 10,485,760 (10 GB)
ββ Is 5,001,024 < 10,485,760? β
YES β Continue
ββ If NO β STOP, show error
D. BLOCK SOFT LIMIT CHECK:
ββ Current: 5,001,024
ββ Soft limit: 9,437,184 (9 GB)
ββ Is 5,001,024 < 9,437,184? β
YES β No grace needed
ββ If NO β Start/check grace period
3. All checks passed β
β File created successfully
β Quota counters updated
If ANY check fails β Error message: "Disk quota exceeded"
Where Quota Data Lives
Modern EXT4 (RHEL 8/9/10) - Built-in Quota Feature:
When you format with: mkfs.ext4 /dev/sdb1
The filesystem is created with quota support BUILT-IN:
ββ Special hidden inodes created automatically
β ββ Inode #3: User quota data
β ββ Inode #4: Group quota data
β ββ These are invisible to normal users
ββ Protected by filesystem journal
ββ Can't be accidentally deleted
ββ Updated atomically (safely)
To verify quota feature is enabled:
tune2fs -l /dev/sdb1 | grep quota
Output should show:
Filesystem features: ... quota ...
User quota inode: 3
Group quota inode: 4
Legacy Method (RHEL 6/7 old-style):
External files in filesystem root:
ββ aquota.user (user quota data)
ββ aquota.group (group quota data)
Problems:
ββ Can get corrupted
ββ Need manual checking with quotacheck
ββ Not recommended anymore
We'll ONLY use the modern built-in method in this guide.
Evolution of Quota in RHEL
| RHEL Version | Default Method | Notes |
| RHEL 6 | External quota files | Needed quotacheck command |
| RHEL 7 | Both methods available | Transition period |
| RHEL 8+ | Built-in ext4 quota | External files deprecated |
| RHEL 9/10 | Built-in only (recommended) | quotacheck not needed |
This guide focuses on RHEL 8/9/10 modern approach.
Key Commands Overview
| Command | What It Does | When to Use |
quotaon | Enable quota enforcement | After mounting filesystem |
quotaoff | Disable quota enforcement | For maintenance |
setquota | Set limits via command line | Scripting, automation |
edquota | Edit limits interactively | Manual configuration |
quota | Check your own quota | User self-check |
repquota | Generate quota report | Admin monitoring |
Deprecated commands (don't use with built-in quota):
quotacheck- Not needed with built-in ext4 quota
Part 4: Setting Up Quotas on EXT4 (Step-by-Step)
Prerequisites Check
Verify your system:
# 1. Check RHEL version (should be 8, 9, or 10)
cat /etc/redhat-release
Expected output:
Red Hat Enterprise Linux release 9.x (Plow)
# 2. Check available disks
lsblk
Look for an unused disk, example:
NAME SIZE TYPE
sda 20G disk β We'll use this
sdb 100G disk β Our target disk for quota
ββsdb1 50G part
# 3. Verify you're root
whoami
Should show: root
# 4. Check if quota tools are installed
rpm -qa | grep quota
Should show: quota-4.x.x (if not, install it)
Install quota tools if missing:
dnf install quota -y
Understanding Our Setup
What we're building:
Physical disk: /dev/sdb (the whole disk device)
β
Partition: /dev/sdb1 (a section of the disk)
β
Filesystem: EXT4 with quota feature enabled
β
Mount point: /quotadata (where users access it)
β
Quota enforcement: Active for users and groups
Important distinction:
/dev/sdb= The entire physical disk (like the whole hard drive)/dev/sdb1= First partition on that disk (like a section of the drive)We'll create the partition, then format it, then mount it
Step 1: Create the Partition
β οΈ WARNING: This will erase all data on /dev/sdb. Make sure it's the right disk!
# Start partitioning tool
fdisk /dev/sdb
Inside fdisk (interactive commands):
Welcome to fdisk (util-linux 2.37.4).
Command (m for help): n β Press 'n' for new partition
Partition type
p primary (0 primary, 0 extended, 4 free)
e extended (container for logical partitions)
Select (default p): p β Press 'p' for primary
Partition number (1-4, default 1): 1 β Press '1' (first partition)
First sector (2048-209715199, default 2048): [Press Enter]
Last sector, +/-sectors or +/-size{K,M,G,T,P}: +20G β Type '+20G' for 20 GB
Created a new partition 1 of type 'Linux' and of size 20 GiB.
Command (m for help): w β Press 'w' to write and exit
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.
Reload partition table:
partprobe /dev/sdb
Verify partition was created:
lsblk /dev/sdb
Expected output:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 50G 0 disk
ββsdb1 8:17 0 20G 0 part β Our new partition
β
Step 1 complete! You now have /dev/sdb1 ready for formatting.
Step 2: Create EXT4 Filesystem with Quota
Create the filesystem:
# Format partition as EXT4 with a label
mkfs.ext4 -L QUOTADATA /dev/sdb1
Expected output:
mke2fs 1.46.5 (30-Dec-2021)
Creating filesystem with 5242880 4k blocks and 1310720 inodes
Filesystem UUID: a1b2c3d4-e5f6-...
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912
...
Check if quota feature is enabled (should be by default):
tune2fs -l /dev/sdb1 | grep -i quota
Expected output:
Filesystem features: ... quota ...
User quota inode: 3
Group quota inode: 4
β If you see this, quota feature is already enabled! Modern ext4 enables it by default.
If quota feature is NOT shown (rare), enable it:
# Enable quota feature
tune2fs -O quota /dev/sdb1
# Verify filesystem integrity (recommended after adding features)
e2fsck -f /dev/sdb1
Understand what happened:
mkfs.ext4created a new ext4 filesystemQuota feature is built-in by default in RHEL 8+
Special inodes #3 and #4 store quota data
No external files needed
Step 3: Create Mount Point
# Create directory where filesystem will be accessible
mkdir -p /quotadata
# Verify it was created
ls -ld /quotadata
Expected output:
drwxr-xr-x. 2 root root 6 Nov 3 10:00 /quotadata
Step 4: Configure Automatic Mounting
We need to edit /etc/fstab so the filesystem mounts automatically at boot.
# Backup fstab first (safety measure)
cp /etc/fstab /etc/fstab.backup
# Edit fstab
nano /etc/fstab
Add this line at the end:
/dev/sdb1 /quotadata ext4 defaults,usrquota,grpquota 0 2
Understanding each field:
Field 1: /dev/sdb1 β The partition to mount
Field 2: /quotadata β Where to mount it
Field 3: ext4 β Filesystem type
Field 4: defaults,usrquota,grpquota β Mount options
ββ defaults: Standard options (rw, suid, dev, exec, auto, nouser, async)
ββ usrquota: Enable user quota tracking
ββ grpquota: Enable group quota tracking
Field 5: 0 β Dump (0 = don't backup with dump command)
Field 6: 2 β fsck order (2 = check after root filesystem)
Save and exit:
In nano: Press
Ctrl+O, thenEnter, thenCtrl+XIn vim: Press
Esc, type:wq, pressEnter
Verify your fstab syntax (important!):
# Test fstab without rebooting
mount -a
# Check for errors
echo $?
# Should show: 0 (means success)
Step 5: Mount the Filesystem
# Reload systemd to recognize fstab changes
systemctl daemon-reload
# Mount the filesystem
mount /quotadata
# Verify it's mounted with correct options
mount | grep /quotadata
Expected output:
/dev/sdb1 on /quotadata type ext4 (rw,relatime,seclabel,quota,usrquota,grpquota)
β
Critical check: You MUST see usrquota and grpquota in the output!
If you DON'T see quota options:
# Remount with correct options
mount -o remount,usrquota,grpquota /quotadata
# Verify again
mount | grep /quotadata
Check disk space:
df -h /quotadata
Expected output:
Filesystem Size Used Avail Use% Mounted on
/dev/sdb1 20G 45M 19G 1% /quotadata
Step 6: Enable Quota Enforcement
With modern built-in ext4 quota, this is simple:
# Turn on quota enforcement
quotaon -v /quotadata
Expected output:
/dev/sdb1 [/quotadata]: user quotas turned on
/dev/sdb1 [/quotadata]: group quotas turned on
β
That's it! No quotacheck needed with built-in quota.
Verify quota is active:
# Check quota status
quotaon -p /quotadata
Expected output:
user quota on /quotadata (/dev/sdb1) is on
group quota on /quotadata (/dev/sdb1) is on
Generate initial report:
repquota -a
Expected output:
*** Report for user quotas on device /dev/sdb1
Block grace time: 7days; Inode grace time: 7days
Block limits File limits
User used soft hard grace used soft hard grace
----------------------------------------------------------------------
root -- 24 0 0 3 0 0
β Quota system is fully operational!
Step 7: Verify SELinux Context (RHEL-specific)
RHEL systems use SELinux for security. Let's ensure proper context:
# Check SELinux context
ls -Zd /quotadata
Expected output:
unconfined_u:object_r:default_t:s0 /quotadata
If there are issues with quota later, restore SELinux context:
# Restore proper context
restorecon -Rv /quotadata
# Verify again
ls -Zd /quotadata
Complete Setup Verification Checklist
Run these commands to verify everything:
# 1. Partition exists
lsblk | grep sdb1
# β
Should show: sdb1 partition
# 2. Filesystem has quota feature
tune2fs -l /dev/sdb1 | grep -i quota
# β
Should show: quota in features list
# 3. Mounted with quota options
mount | grep /quotadata
# β
Should show: usrquota,grpquota
# 4. Quota is turned on
quotaon -p /quotadata
# β
Should show: user quota on ... is on
# 5. Can generate quota report
repquota /quotadata
# β
Should show: quota report table
# 6. fstab entry exists
grep quotadata /etc/fstab
# β
Should show: your fstab line
If all checks pass β , your quota system is ready for use!
Part 5: Managing Quotas (Daily Operations)
Creating Test Users
Let's create users to practice quota management:
# Create first test user
useradd alice
passwd alice
# (set password when prompted)
# Create second test user
useradd bob
passwd bob
# Verify users were created
id alice
id bob
# Give them access to quota filesystem
mkdir -p /quotadata/alice
mkdir -p /quotadata/bob
chown alice:alice /quotadata/alice
chown bob:bob /quotadata/bob
Important Note: When you create the directories, each one counts as 1 inode in the user's quota. This means users start with 1 inode used (the directory itself), not 0.
Method 1: Setting Limits with setquota (Recommended)
Syntax:
setquota -u <username> <block-soft> <block-hard> <inode-soft> <inode-hard> <filesystem>
Remember: Block values are in 1 KB units!
Example 1: Set 5 GB limit for Alice
# Calculate blocks:
# 5 GB soft = 5 Γ 1024 Γ 1024 = 5,242,880 blocks
# 6 GB hard = 6 Γ 1024 Γ 1024 = 6,291,456 blocks
setquota -u alice 5242880 6291456 10000 12000 /quotadata
What this means:
Block soft: 5 GB (5,242,880 KB)
Block hard: 6 GB (6,291,456 KB)
Inode soft: 10,000 files
Inode hard: 12,000 files
Quick calculation helper:
| Desired Limit | Block Value | Formula |
| 100 MB | 102,400 | 100 Γ 1024 |
| 500 MB | 512,000 | 500 Γ 1024 |
| 1 GB | 1,048,576 | 1024 Γ 1024 |
| 5 GB | 5,242,880 | 5 Γ 1024 Γ 1024 |
| 10 GB | 10,485,760 | 10 Γ 1024 Γ 1024 |
| 50 GB | 52,428,800 | 50 Γ 1024 Γ 1024 |
| 100 GB | 104,857,600 | 100 Γ 1024 Γ 1024 |
Example 2: Set 500 MB limit for Bob (for testing)
setquota -u bob 512000 614400 5000 6000 /quotadata
Verify the limits were set:
quota -u alice
quota -u bob
Method 2: Setting Limits with edquota (Interactive)
Edit quota interactively:
edquota -u alice
Your default editor opens with this:
Disk quotas for user alice (uid 1001):
Filesystem blocks soft hard inodes soft hard
/dev/sdb1 4 5242880 6291456 1 10000 12000
Understanding the columns:
| Column | Meaning | Your Action |
| Filesystem | The disk | Don't change |
| blocks (current) | Current usage in KB (4 KB = directory) | Don't change (auto-updated) |
| soft (blocks) | Soft limit in KB | Edit this |
| hard (blocks) | Hard limit in KB | Edit this |
| inodes (current) | Current file count (1 = directory) | Don't change |
| soft (inodes) | Soft file limit | Edit this |
| hard (inodes) | Hard file limit | Edit this |
Edit the values, then save and exit:
In vim:
Esc,:wq,EnterIn nano:
Ctrl+O,Enter,Ctrl+X
Checking User Quotas
As root, check any user:
# Basic quota check
quota -u alice
Output:
Disk quotas for user alice (uid 1001):
Filesystem blocks quota limit grace files quota limit grace
/dev/sdb1 4 5242880 6291456 1 10000 12000
Note: The user starts with:
4 blocks = ~4 KB (the
/quotadata/alicedirectory itself)1 inode = The directory counts as 1 file
Human-readable format:
quota -s -u alice
Output:
Disk quotas for user alice (uid 1001):
Filesystem blocks quota limit grace files quota limit grace
/dev/sdb1 4K 5120M 6144M 1 10000 12000
As the user themselves:
# Switch to user
su - alice
# Check own quota
quota
Output:
Disk quotas for user alice (uid 1001):
Filesystem blocks quota limit grace files quota limit grace
/dev/sdb1 4 5242880 6291456 1 10000 12000
# Switch back to root
exit
Setting Grace Periods
Grace periods apply to the entire filesystem, not individual users.
Method 1: Interactive editing
edquota -t
You'll see:
Grace period before enforcing soft limits for users:
Time units may be: days, hours, minutes, or seconds
Filesystem Block grace period Inode grace period
/dev/sdb1 7days 7days
Common grace period values:
| Value | Meaning | When to Use |
| 7days | 1 week | Standard production (recommended) |
| 3days | 3 days | Faster cleanup required |
| 1hour | 60 minutes | Testing or very strict policies |
| 30minutes | 30 minutes | Emergency scenarios |
Edit and save as desired.
Method 2: Command-line (in seconds)
# Set 2 days grace (172800 seconds)
# Syntax: setquota -t <block-grace-seconds> <inode-grace-seconds> <filesystem>
setquota -t 172800 172800 /quotadata
# Set 1 hour grace (for testing)
setquota -t 3600 3600 /quotadata
# Set 7 days grace (standard)
setquota -t 604800 604800 /quotadata
Verify grace period:
repquota -a | head -5
Output:
*** Report for user quotas on device /dev/sdb1
Block grace time: 7days; Inode grace time: 7days
Block limits File limits
User used soft hard grace used soft hard grace
Viewing All User Quotas
Simple report:
repquota /quotadata
All filesystems:
repquota -a
Human-readable sizes:
repquota -asu
Output:
*** Report for user quotas on device /dev/sdb1
Block grace time: 7days; Inode grace time: 7days
Block limits File limits
User used soft hard grace used soft hard grace
----------------------------------------------------------------------
root -- 24K 0 0 3 0 0
alice -- 4K 5120M 6144M 1 10000 12000
bob -- 4K 500M 600M 1 5000 6000
Understanding the status indicators:
| Indicator | Meaning |
-- | Within limits (normal) |
+- | Over soft limit, grace period active |
++ | Over soft limit, grace period expired |
+ | Over hard limit (shouldn't happen) |
Copy alice's limits to both
edquota -p alice -u david eve
**Verify:**
```bash
quota -u david
quota -u eve
Output for each:
Disk quotas for user david (uid 1003):
Filesystem blocks quota limit grace files quota limit grace
/dev/sdb1 4 5242880 6291456 1 10000 12000
Setting Group Quotas
Create a group:
groupadd developers
usermod -aG developers alice
usermod -aG developers bob
Set group quota:
# 20 GB for entire group
setquota -g developers 20971520 25165824 50000 60000 /quotadata
Check group quota:
quota -g developers
Disabling and Enabling Quotas
Temporarily turn off quota (for maintenance):
quotaoff -v /quotadata
Output:
/dev/sdb1 [/quotadata]: user quotas turned off
/dev/sdb1 [/quotadata]: group quotas turned off
Turn back on:
quotaon -v /quotadata
Output:
/dev/sdb1 [/quotadata]: user quotas turned on
/dev/sdb1 [/quotadata]: group quotas turned on
Check current status:
quotaon -p /quotadata
Output:
user quota on /quotadata (/dev/sdb1) is on
group quota on /quotadata (/dev/sdb1) is on
Part 6: Advanced Scenarios & Troubleshooting
Scenario 1: Testing Block Quota (Disk Space Limit)
Let's see quota enforcement in action!
Setup: Give bob a small limit for testing
# 50 MB soft, 60 MB hard
setquota -u bob 51200 61440 5000 6000 /quotadata
Test as bob:
# Switch to bob
su - bob
# Go to bob's directory
cd /quotadata/bob
# Check current quota
quota
Output:
Disk quotas for user bob (uid 1002):
Filesystem blocks quota limit grace files quota limit grace
/dev/sdb1 4 51200 61440 1 5000 6000
β β
4 KB used 1 inode (directory)
Test 1: Create 40 MB file (within soft limit)
# Create 40 MB file
dd if=/dev/zero of=file40mb bs=1M count=40
Output:
40+0 records in
40+0 records out
41943040 bytes (42 MB) copied, 0.123456 s, 340 MB/s
# Check quota usage
quota
Output:
Disk quotas for user bob (uid 1002):
Filesystem blocks quota limit grace files quota limit grace
/dev/sdb1 40964 51200 61440 2 5000 6000
β β
40 MB + 4 KB directory 2 files (dir + file40mb) β
Test 2: Add 15 MB more (exceeds soft, triggers grace)
# Create another 15 MB file
dd if=/dev/zero of=file15mb bs=1M count=15
# Check quota - grace timer starts
quota
Output:
Disk quotas for user bob (uid 1002):
Filesystem blocks quota limit grace files quota limit grace
/dev/sdb1 56324 51200 61440 7days 3 5000 6000
β β β
55 MB total Grace started! 3 files total β οΈ
Test 3: Try to exceed hard limit (blocked immediately)
# Try to create 10 MB file (would exceed 60 MB hard limit)
dd if=/dev/zero of=file10mb bs=1M count=10
Output:
dd: error writing 'file10mb': Disk quota exceeded
5+0 records in β Only wrote partial data
4+1 records out
4943872 bytes (4.9 MB) copied, 0.0234 s, 211 MB/s
What happened:
Bob tried to write 10 MB
Total would be 55 MB + 10 MB = 65 MB
Hard limit is 60 MB
Kernel allowed writing up to 60 MB, then stopped β
Clean up:
# Check current usage
du -sh /quotadata/bob
# Delete files to go back under soft limit
rm file15mb file10mb
# Verify grace period reset
quota
Output:
Disk quotas for user bob (uid 1002):
Filesystem blocks quota limit grace files quota limit grace
/dev/sdb1 40964 51200 61440 2 5000 6000
β
Grace reset! β
# Switch back to root
exit
Scenario 2: Testing Inode Quota (File Count Limit)
Setup: Give alice a small inode limit
# 5 GB space, but only 10 files allowed (soft), 15 max (hard)
setquota -u alice 5242880 6291456 10 15 /quotadata
Test as alice:
su - alice
cd /quotadata/alice
# Check quota
quota
Output:
Filesystem blocks quota limit grace files quota limit grace
/dev/sdb1 4 5242880 6291456 1 10 15
β
1 inode (directory itself)
Test 1: Create 9 files (reaches soft limit with directory)
# Create 9 small files
for i in {1..9}; do
echo "File $i" > file$i.txt
done
# Check quota
quota
Output:
Filesystem blocks quota limit grace files quota limit grace
/dev/sdb1 8 5242880 6291456 10 10 15
β
At soft limit (1 dir + 9 files = 10) β
Test 2: Create 10th file (exceeds soft, starts grace)
# Create one more file
echo "File 10" > file10.txt
# Check quota
quota
Output:
Filesystem blocks quota limit grace files quota limit grace
/dev/sdb1 8 5242880 6291456 11 10 15 7days
β β
Over soft Grace started! β οΈ
Test 3: Create files until hard limit
# Create more files (11, 12, 13, 14)
for i in {11..14}; do
echo "File $i" > file$i.txt
done
# Check quota - now at hard limit
quota
Output:
Filesystem blocks quota limit grace files quota limit grace
/dev/sdb1 12 5242880 6291456 15 10 15 7days
β
At hard limit (1 dir + 14 files = 15)
# Try to create 15th file (exceeds hard limit)
echo "File 15" > file15.txt
Output:
bash: file15.txt: Disk quota exceeded β
Check final state:
quota
Output:
Filesystem blocks quota limit grace files quota limit grace
/dev/sdb1 12 5242880 6291456 15 10 15 7days
β
At hard limit (can't create more)
# List files
ls -1 | wc -l
14 β 14 files (plus 1 directory = 15 inodes total)
Clean up:
# Delete some files to go back under soft limit
rm file{10..14}.txt
# Check quota
quota
Output:
Filesystem blocks quota limit grace files quota limit grace
/dev/sdb1 8 5242880 6291456 10 10 15
β
Grace reset! β
exit
Scenario 3: Grace Period Expiration Test
Setup: Set very short grace period for testing
# Set 2 minute grace period
setquota -t 120 120 /quotadata
# Verify
repquota -a | head -3
Output:
*** Report for user quotas on device /dev/sdb1
Block grace time: 2minutes; Inode grace time: 2minutes
Test sequence:
# As bob (who has 50 MB soft, 60 MB hard)
su - bob
ls
# remove everything from bob dir
rm*
cd /quotadata/bob
# Create 55 MB file (exceeds soft limit)
dd if=/dev/zero of=bigfile bs=1M count=55
# Check quota - grace starts
quota
Output:
Filesystem blocks quota limit grace files quota limit grace
/dev/sdb1 56324 51200 61440 2minutes 2 5000 6000
β
Timer started at current time
# Wait exactly 2 minutes (use watch to monitor)
watch -n 10 quota
# Watch the grace countdown: 2minutes -> 1minute -> 30seconds -> ...
# After 2 minutes, try to create new file
dd if=/dev/zero of=smallfile bs=1M count=2
Output:
dd: error writing 'smallfile': Disk quota exceededβ
1+0 records in
0+0 records out
Here is a technical point that file (smallfile) will be created but itβs size will be remain 0 byte because βinodeβ values is are between 5000 to 6000 you can see below
ls -lh
total 55M
-rw-r--r--. 1 bob bob 55M Nov 5 00:04 bigfile
-rw-r--r--. 1 bob bob 0 Nov 5 00:05 smallfile
# here you can see 55M is bigfile size and smallfile size is 0
Important understanding:
Grace expiration does NOT delete files
It only PREVENTS new writes until usage drops below soft limit
Restore and clean up:
# Delete the big file
rm bigfile smallfile
# Check quota - now can write again
quota
Output:
Filesystem blocks quota limit grace files quota limit grace
/dev/sdb1 4 51200 61440 1 5000 6000
β
No grace timer (back to normal) β
# Reset grace to normal
exit
setquota -t 604800 604800 /quotadata # 7 days
Troubleshooting Common Issues
Issue 1: Quota Not Enforcing
Symptoms: User can exceed limits without getting errors
Diagnosis:
# Step 1: Check if quota is actually turned on
quotaon -p /quotadata
# Should show: user quota on ... is on
# If shows: user quota on ... is off
quotaon /quotadata
# Step 2: Check if filesystem is mounted with quota options
mount | grep /quotadata
# Should show: usrquota,grpquota in options
# If missing:
mount -o remount,usrquota,grpquota /quotadata
# Step 3: Check if user actually has limits set
quota -u username # user name like bob alice etc so command will be "quota -u bob"
# If all zeros:
setquota -u username 5242880 6291456 10000 12000 /quotadata
#Username cluld be any name so use valid username instead of username
Issue 2: "Cannot find filesystem to check"
Error message:
quotacheck: Cannot find filesystem to check or filesystem not mounted with quota option.
Solution:
# With modern ext4 built-in quota, DON'T use quotacheck!
# Just use quotaon directly:
quotaon /quotadata
# If you still get errors:
# 1. Verify filesystem has quota feature
tune2fs -l /dev/sdb1 | grep quota
# 2. Check mount options
mount | grep quotadata
# 3. Remount if needed
mount -o remount,usrquota,grpquota /quotadata
# 4. Try quotaon again
quotaon /quotadata
Issue 3: Grace Period Shows "none" But User Can't Write
Symptom:
quota -u bob
Output:
Filesystem blocks quota limit grace files quota limit grace
/dev/sdb1 56324 51200 61440 none 2 5000 6000
User says "I can't write files!" but grace shows "none"
Explanation:
This is lazy enforcement. The grace field updates only when kernel checks it.
Solution:
# As the user, try to actually write something
su - bob
cd /quotadata/bob
echo "test" > testfile
# NOW the grace field updates to show the real status
quota
# If over soft for longer than grace period, it will now show expired
Prevention: Train users to check quota BEFORE they hit limits, not after.
Issue 4: User Deletes Files But Quota Still Shows High Usage
Symptom:
# User deletes 10 GB of files
rm -rf /quotadata/alice/olddata/ # old data could be any file or dir
# But quota still shows high usage
quota -u alice
# Shows: 15 GB used (should be 5 GB now)
Possible causes:
- Files still open by processes
# Check for open files (as root)
lsof +D /quotadata/alice
# If processes are holding files:
# Kill those processes or wait for them to close files
- Hidden files or directories
# Check for hidden files
du -sh /quotadata/alice
du -sh /quotadata/alice/.*
# Find large files
find /quotadata/alice -type f -size +100M -exec ls -lh {} \;
- Quota database needs refresh (rare with built-in quota)
# Turn off and on quota
quotaoff /quotadata
quotaon /quotadata
# Check again
quota -u alice
Issue 5: After Reboot, Quota Not Working
Cause: Filesystem not in /etc/fstab or quotaon not run at boot
Solution:
# Check fstab
grep quotadata /etc/fstab
# Should show:
/dev/sdb1 /quotadata ext4 defaults,usrquota,grpquota 0 2
# If missing, add it:
echo "/dev/sdb1 /quotadata ext4 defaults,usrquota,grpquota 0 2" >> /etc/fstab
# Test without rebooting
systemctl daemon-reload
mount -a
quotaon /quotadata
# Verify
quotaon -p /quotadata
Create systemd service to ensure quotaon at boot (optional):
cat > /etc/systemd/system/quota-enable.service << 'EOF'
[Unit]
Description=Enable disk quotas
After=local-fs.target
[Service]
Type=oneshot
ExecStart=/usr/sbin/quotaon -a
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable quota-enable.service
systemctl start quota-enable.service
Issue 6: SELinux Blocking Quota Operations
Symptoms: Quota commands fail with "Permission denied" even as root
Check for SELinux denials:
# Check recent SELinux denials
ausearch -m avc -ts recent | grep quota
# Check SELinux context of quota filesystem
ls -Zd /quotadata
# Restore proper context
restorecon -Rv /quotadata
# If still issues, check SELinux mode
getenforce
# Temporarily set to permissive for testing (not for production!)
setenforce 0
# Try quota operations again
# If they work now, SELinux is the issue
# Set back to enforcing
setenforce 1
# Create proper SELinux policy (beyond scope of this guide)
Issue 7: Quota Report Shows Wrong Block Sizes
Symptom:
repquota -a
# Shows values that don't match what you set
Understanding:
# Quota reports in 1 KB blocks
# Filesystem uses 4 KB blocks by default
# When you see:
blocks used: 4096
# This means:
4096 KB = 4 MB (quota perspective)
# NOT:
4096 blocks Γ 4 KB = 16 MB (filesystem perspective)
Always use the -s flag for human-readable output:
repquota -s -u /quotadata
# Shows: 4M (much clearer!)
Issue 8: "Files used" Shows 1 Even with Empty Directory
Symptom:
# User has empty directory but quota shows 1 file
quota -u alice
Output:
Filesystem blocks quota limit grace files quota limit grace
/dev/sdb1 4 5242880 6291456 1 10000 12000
β
Why 1 when directory is empty?
Explanation:
This is normal behavior. The directory /quotadata/alice itself is a file (type: directory) and counts as 1 inode.
# Verify
ls -la /quotadata/alice
Output:
total 8
drwxr-xr-x. 2 alice alice 4096 Nov 3 10:00 . β This is the directory (1 inode)
drwxr-xr-x. 5 root root 4096 Nov 3 09:55 ..
Every directory counts as 1 inode, so users always start with at least 1 file in their quota count.
Tutorial ends here now next things are bonus .
Advanced Testing: Quota Across Different File Types
Setup:
# Give bob varied limits
setquota -u bob 512000 614400 100 120 /quotadata
# 500 MB soft, 600 MB hard, 100 files soft, 120 files hard
Test different scenarios:
su - bob
cd /quotadata/bob
# Scenario A: Many tiny files (hits inode limit first)
for i in {1..99}; do
echo "tiny" > tiny$i.txt
done
quota # Shows 100 inodes (1 dir + 99 files), minimal blocks
# Scenario B: Few large files (hits block limit first)
rm tiny*.txt # Clean up first
dd if=/dev/zero of=large1 bs=1M count=400 # 400 MB
dd if=/dev/zero of=large2 bs=1M count=150 # 150 MB
quota # Shows ~550 MB blocks (over soft), only 3 inodes (1 dir + 2 files)
# Scenario C: Mix of both
rm large*
for i in {1..10}; do
dd if=/dev/zero of=mixed$i bs=1M count=50 2>/dev/null # 10 files Γ 50 MB = 500 MB
done
quota # Shows ~500 MB blocks (at soft), 11 inodes (1 dir + 10 files)
Key lesson: Different usage patterns hit different limits!
Part 7: Production Best Practices
1. Planning Your Quota Strategy
Questions to answer before implementing:
| Question | Why It Matters | Example Answer |
| How many users? | Determines total capacity needed | 200 users |
| What's the use case? | Influences limit sizes | Development servers: more space<br>Email servers: many small files |
| Average expected usage per user? | Baseline for soft limit | 10 GB per developer |
| Peak usage allowance? | Determines hard limit | 15 GB (50% over average) |
| How long to clean up? | Sets grace period | 7 days (one work week) |
| Growth rate? | Future capacity planning | 20% annual growth |
Sample calculation:
Scenario: Development team server
Users: 50 developers
Average usage: 10 GB per user
Peak allowance: 15 GB per user
Grace period: 7 days
Calculation:
ββ Minimum capacity: 50 Γ 10 GB = 500 GB
ββ With peak allowance: 50 Γ 15 GB = 750 GB
ββ With 20% growth buffer: 750 GB Γ 1.2 = 900 GB
ββ Recommended purchase: 1 TB disk
Per-user quotas:
ββ Soft limit: 10 GB (10,485,760 blocks)
ββ Hard limit: 15 GB (15,728,640 blocks)
ββ Inode soft: 50,000 files
ββ Inode hard: 75,000 files
ββ Grace: 7 days
2. Quota Sizing Formulas
Conservative approach (recommended for most cases):
Soft Limit = Expected Average Usage
Hard Limit = Soft Limit Γ 1.5
Grace Period = 7 days
Inode Soft = Estimated file count Γ 2
Inode Hard = Inode Soft Γ 1.5
Aggressive approach (limited resources):
Soft Limit = Expected Average Usage Γ 0.8
Hard Limit = Expected Average Usage
Grace Period = 3 days
Inode Soft = Estimated file count
Inode Hard = Inode Soft Γ 1.2
Generous approach (plenty of space):
Soft Limit = Expected Average Usage Γ 1.5
Hard Limit = Expected Average Usage Γ 2
Grace Period = 14 days
Inode Soft = Estimated file count Γ 3
Inode Hard = Inode Soft Γ 1.5
Example implementations:
# Conservative (10 GB expected usage)
setquota -u user 10485760 15728640 100000 150000 /quotadata
# Aggressive (10 GB expected usage)
setquota -u user 8388608 10485760 50000 60000 /quotadata
# Generous (10 GB expected usage)
setquota -u user 15728640 20971520 150000 225000 /quotadata
3. Monitoring and Alerting
Daily monitoring script:
#!/bin/bash
# Save as: /usr/local/sbin/quota_monitor.sh
# Configuration
FILESYSTEM="/quotadata"
ALERT_EMAIL="admin@company.com"
WARN_THRESHOLD=80 # Warn at 80% of soft limit
CRITICAL_THRESHOLD=95 # Critical at 95% of soft limit
# Generate report
REPORT_FILE="/tmp/quota_report_$(date +%Y%m%d).txt"
echo "Disk Quota Report - $(date)" > "$REPORT_FILE"
echo "======================================" >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
# Check users over warning threshold
echo "Users over ${WARN_THRESHOLD}% of quota:" >> "$REPORT_FILE"
repquota -u "$FILESYSTEM" | awk -v threshold="$WARN_THRESHOLD" '
NR > 5 && $4 > 0 {
usage_pct = ($3 / $4) * 100
if (usage_pct >= threshold) {
printf "%-15s %10s / %10s (%5.1f%%)\n", $1, $3, $4, usage_pct
}
}
' >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
# Check users in grace period
echo "Users in grace period:" >> "$REPORT_FILE"
repquota -u "$FILESYSTEM" | grep -E "[0-9]days|[0-9]hours" >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
# Check users over hard limit (shouldn't happen, but check anyway)
echo "Users at hard limit:" >> "$REPORT_FILE"
repquota -u "$FILESYSTEM" | awk 'NR > 5 && $3 >= $5 && $5 > 0 {print $0}' >> "$REPORT_FILE"
# Send email if any issues found
if grep -q "%" "$REPORT_FILE"; then
mail -s "Quota Alert - $(date +%Y-%m-%d)" "$ALERT_EMAIL" < "$REPORT_FILE"
fi
# Keep report for 30 days
find /tmp -name "quota_report_*.txt" -mtime +30 -delete
Make it executable and schedule:
chmod +x /usr/local/sbin/quota_monitor.sh
# Add to cron (run daily at 8 AM)
crontab -e
# Add this line:
0 8 * * * /usr/local/sbin/quota_monitor.sh
Real-time monitoring dashboard (simple):
#!/bin/bash
# Save as: /usr/local/sbin/quota_dashboard.sh
watch -n 60 '
echo "=== Disk Quota Dashboard ==="
echo "Last update: $(date)"
echo ""
echo "=== Top 10 Space Users ==="
repquota -su /quotadata | sort -k3 -n -r | head -15 | tail -10
echo ""
echo "=== Users in Grace Period ==="
repquota -su /quotadata | grep -E "[0-9]days|[0-9]hours"
echo ""
echo "=== Overall Filesystem Usage ==="
df -h /quotadata
'
4. User Communication Templates
Email Template 1: Warning (80% of soft limit)
Subject: Disk Quota Warning - Action Recommended
Hello [Username],
Your disk usage on /quotadata is approaching the recommended limit:
Current Usage: 8.2 GB
Soft Limit: 10 GB (82% used)
Hard Limit: 15 GB
Recommendation:
Please review your files and remove unnecessary data to stay under 10 GB.
What happens if you exceed 10 GB:
- You'll have 7 days to reduce usage
- After 7 days, you won't be able to create new files until usage drops below 10 GB
Need help?
- Reply to this email
- Check our wiki: https://wiki.company.com/disk-quota
- Contact IT: helpdesk@company.com
Thank you for keeping our systems running smoothly!
IT Team
Email Template 2: Critical (in grace period)
Subject: URGENT: Disk Quota Exceeded - 7 Days to Comply
Hello [Username],
Your disk usage has exceeded the recommended limit:
Current Usage: 12.5 GB
Soft Limit: 10 GB (EXCEEDED)
Hard Limit: 15 GB
Grace Period: 7 days remaining
REQUIRED ACTION:
You MUST reduce your usage below 10 GB within 7 days.
What happens if you don't:
After 7 days, you will NOT be able to save any new files until your usage is below 10 GB.
How to check your quota:
$ quota
How to find large files:
$ du -sh ~/quotadata/* | sort -h
Need a quota increase?
Submit a request: https://helpdesk.company.com/quota-request
This is automated notification #1 of 3.
IT Team
Email Template 3: Grace expired
Subject: CRITICAL: Disk Quota Grace Period Expired
Hello [Username],
Your grace period has expired. You can no longer create new files.
Current Usage: 12.5 GB
Soft Limit: 10 GB
Hard Limit: 15 GB
Grace Period: EXPIRED
To restore write access:
1. Delete files to get below 10 GB
2. Verify with: quota
3. Try creating a test file
Large file cleanup commands:
# Find files over 100 MB
$ find ~/quotadata -type f -size +100M -ls
# Find old files (over 180 days)
$ find ~/quotadata -type f -mtime +180 -ls
Need immediate assistance?
Contact IT: helpdesk@company.com or ext. 1234
IT Team
5. Backup and Disaster Recovery
Backup quota settings (not just data):
#!/bin/bash
# Save as: /usr/local/sbin/backup_quota_settings.sh
BACKUP_DIR="/backup/quota"
DATE=$(date +%Y%m%d)
mkdir -p "$BACKUP_DIR"
# Backup quota report (shows current usage and limits)
repquota -a > "$BACKUP_DIR/quota_report_$DATE.txt"
# Backup quota limits in machine-readable format
{
echo "# Quota backup created on $(date)"
echo "# Format: username:uid:soft_blocks:hard_blocks:soft_inodes:hard_inodes"
repquota -u /quotadata | awk 'NR > 5 && $1 != "root" {
print $1":"$4":"$5":"$7":"$8
}'
} > "$BACKUP_DIR/quota_limits_$DATE.csv"
# Keep last 90 days
find "$BACKUP_DIR" -name "quota_*" -mtime +90 -delete
echo "Quota settings backed up to $BACKUP_DIR"
Restore quota settings after disaster:
#!/bin/bash
# Save as: /usr/local/sbin/restore_quota_settings.sh
BACKUP_FILE="/backup/quota/quota_limits_20251103.csv"
FILESYSTEM="/quotadata"
if [ ! -f "$BACKUP_FILE" ]; then
echo "Backup file not found!"
exit 1
fi
echo "Restoring quota settings from $BACKUP_FILE..."
# Read CSV and restore each user
while IFS=: read -r username uid soft_blocks hard_blocks soft_inodes hard_inodes; do
# Skip comments
[[ "$username" =~ ^#.* ]] && continue
# Check if user exists
if id "$username" &>/dev/null; then
setquota -u "$username" "$soft_blocks" "$hard_blocks" "$soft_inodes" "$hard_inodes" "$FILESYSTEM"
echo "Restored quota for: $username"
else
echo "Skipped $username (user doesn't exist)"
fi
done < "$BACKUP_FILE"
echo "Quota restoration complete!"
repquota "$FILESYSTEM"
Schedule backups:
# Add to cron (daily at 2 AM)
crontab -e
# Add:
0 2 * * * /usr/local/sbin/backup_quota_settings.sh
6. Security Best Practices
Prevent quota manipulation:
# Only root should modify quotas
chmod 700 /usr/sbin/*quota*
# Protect fstab
chmod 644 /etc/fstab
chown root:root /etc/fstab
# Audit quota changes
auditctl -w /usr/sbin/setquota -p x -k quota_set
auditctl -w /usr/sbin/edquota -p x -k quota_edit
auditctl -w /etc/fstab -p wa -k fstab_changes
# Make audit rules permanent
echo "-w /usr/sbin/setquota -p x -k quota_set" >> /etc/audit/rules.d/quota.rules
echo "-w /usr/sbin/edquota -p x -k quota_edit" >> /etc/audit/rules.d/quota.rules
echo "-w /etc/fstab -p wa -k fstab_changes" >> /etc/audit/rules.d/quota.rules
# Reload audit rules
augenrules --load
# View quota-related audit logs
ausearching Limits to Multiple Users
**Copy alice's limits to another user:**
```bash
edquota -p alice -u charlie



