Hướng Dẫn Xử Lý Tình Trạng Đầy Inode Trên Linux [A-Z]
Có một kiểu lỗi khiến bạn mất cả buổi: ứng dụng báo No space left on device, nhưng df -h vẫn thấy còn hàng GB. Lần đầu gặp, mình cứ nghĩ ổ hỏng. Sau mới biết filesystem hết inode, không phải hết block. Bài này mình viết lại đúng cách xử lý trên server production: chẩn đoán, tìm thủ phạm, xóa an toàn và phòng ngừa.
1. Inode là gì? Vì sao đầy inode khác đầy dung lượng?
Inode là nơi filesystem lưu metadata của mỗi file: quyền, chủ sở hữu, kích thước, timestamp, và con trỏ tới các block dữ liệu. Mỗi file, mỗi thư mục, mỗi symlink đều chiếm một inode. File 1 KB hay 1 GB cũng đều tốn một inode như nhau.
Vì vậy, filesystem có thể hết inode trong khi block vẫn còn rất nhiều. Bạn ghi 10 triệu file rác 1 KB, dung lượng chỉ vài GB, nhưng inode có thể cạn sạch.
Hai lệnh cần phân biệt:
df -h # dung lượng block còn trống
df -i # số inode còn trống: IUsed, IFree, IUse%
Khi server báo No space left on device, đừng chỉ chạy df -h. Chạy cả df -i. Nếu dung lượng còn nhưng IUse% = 100%, bạn đang bị đầy inode.

2. Dấu hiệu server đã cạn inode
Đầy inode thường đến khá đột ngột và rất dễ bị đoán nhầm thành lỗi ổ cứng. Mấy dấu hiệu mình hay gặp:
- Ứng dụng, web server hoặc database báo
No space left on device,Disk quota exceeded, hoặc không ghi được log. df -hvẫn còn GB trống nhưng ghi file nào cũng lỗi.touch /tmp/testtrả vềNo space left on device.- Không tạo được session mới, mail treo trong queue, container không khởi động được.
df -icho thấyIUse%chạm 100% ở ít nhất một mount point.
Kiểm tra nhanh:
df -i
# Ví dụ:
# Filesystem Inodes IUsed IFree IUse% Mounted on
# /dev/vda1 2621440 2621440 0 100% /
# tmpfs 512000 120 511880 1% /run
Nếu dòng / có IUse% = 100%, bạn không tạo thêm được file mới trên partition gốc, dù df -h vẫn báo còn trống.
3. Những thủ phạm thường gặp
Trên server production, đầy inode hầu như luôn đến từ một trong các nguồn sau. Điểm chung: chúng sinh ra rất nhiều file nhỏ trong thời gian ngắn.
- Mail queue kẹt: Postfix, Exim, Sendmail gửi không được do lỗi DNS, relay hoặc blacklist. Mail nằm lại trong
/var/spool/postfix,/var/spool/exim. - PHP session và file tạm:
/var/lib/php/sessions,/tmpchứa hàng triệu filesess_*. - Log không rotate:
/var/logđầy log.1, log.2, file .gz cũ. - Docker overlay2:
/var/lib/docker/overlay2tích tụ layer rác, log container không giới hạn. - Cache ứng dụng: Laravel, Symfony, WordPress để lại cache trong
storage,wp-content/cache. - File rác, backup cũ: thư mục upload, file tmp ứng dụng không dọn.
4. Cách xử lý đầy inode từng bước
Quy trình này áp dụng cho Ubuntu 20.04+, Debian 11+, CentOS 7+, Rocky Linux. Mình sắp theo thứ tự từ chẩn đoán đến xác minh.
- Kiểm tra inode bằng
df -idf -iĐọc cộtIUse%. Từ 90% trở lên là cần theo dõi. 100% là đang có sự cố. Ghi lạiMounted onđể biết partition nào bị đầy. - Xác định partition bị đầy Nếu
/đầy, ảnh hưởng rộng. Nếu chỉ/var,/homehoặc/var/lib/dockerđầy, phạm vi hẹp hơn.df -i /var /home /tmp 2>/dev/null - Tìm thư mục chứa nhiều file Dùng
du --inodesnếu filesystem hỗ trợ:sudo du --inodes -xS / 2>/dev/null | sort -rn | head -20Nếu không có--inodes, đếm bằngfind:sudo find /var -xdev -type f 2>/dev/null | wc -lHoặc đếm từng thư mục con cấp 1:for d in /var/*/; do echo -n "$d " sudo find "$d" -xdev -type f 2>/dev/null | wc -l done | sort -k2 -rn | head - Soi các thư mục nghi ngờ Kiểm tra file đang được process giữ trước khi xóa:
sudo lsof +L1 | head # hoặc sudo lsof | grep deleted | head
/var/spool/postfix,/var/spool/exim— mail queue./var/lib/php/sessions— session PHP./tmp,/var/tmp— file tạm./var/log— log không rotate./var/lib/docker/overlay2— layer Docker./home/*/public_html/wp-content/cache— cache CMS.
- Xóa an toàn để giải phóng inode Nguyên tắc: xóa theo lô, không xóa cả thư mục hệ thống, không xóa file đang mở. Ví dụ xóa session PHP cũ hơn 2 ngày:
sudo find /var/lib/php/sessions -type f -name 'sess_*' -mtime +2 -deleteXóa log nén cũ hơn 30 ngày:sudo find /var/log -type f -name '*.gz' -mtime +30 -deleteXóa file trong/tmpkhông được truy cập 7 ngày:sudo find /tmp -xdev -type f -atime +7 -delete - Xác minh sau khi xử lý
df -i df -h touch /tmp/inode_check && echo OK && rm /tmp/inode_checkNếuIUse%giảm vàtouchthành công, sự cố đã được xử lý. Theo dõi thêm 24 giờ để chắc file không tích tụ lại.

5. Mail queue kẹt gây đầy inode
Đây là kiểu sự cố mình gặp nhiều nhất. Khi MTA bị lỗi relay, sai DNS hoặc bị blacklist, mail không gửi được sẽ nằm lại trong queue. Mỗi mail là một file nhỏ. Vài trăm nghìn đến vài triệu file là chuyện bình thường.
Kiểm tra queue:
sudo mailq | tail -1
# -- 512340 Kbytes in 892341 Requests.
sudo find /var/spool/postfix -type f | wc -l
sudo du --inodes -xS /var/spool/postfix
Trước khi xóa, phải xử lý nguyên nhân gốc: DNS, relay host, blacklist. Nếu chỉ xóa mà không fix, queue sẽ đầy lại sau vài giờ.
# Postfix — xóa toàn bộ mail trong queue, chỉ khi chắc chắn
sudo postsuper -d ALL
# Hoặc xóa mail cũ hơn 7 ngày trong deferred queue
sudo find /var/spool/postfix/deferred -type f -mtime +7 -delete
# Debian/Ubuntu: dọn mail deferred
sudo postsuper -d ALL deferred
Với Exim, dùng exim -bp để liệt kê queue. Muốn xóa một mail, dùng exim -Mrm <message-id>. Đừng xóa trực tiếp file trong spool nếu bạn không rành cấu trúc Exim.
6. PHP session và cache file tích tụ
PHP mặc định lưu session dưới dạng file trong /var/lib/php/sessions trên Debian/Ubuntu, hoặc /var/lib/php/session trên CentOS/RHEL. Garbage collector chỉ chạy theo xác suất, nên khi traffic tăng đột biến, session cũ không được dọn và tích tụ rất nhanh.
sudo find /var/lib/php/sessions -type f -name 'sess_*' | wc -l
sudo ls -la /var/lib/php/sessions | head -5
session.gc_maxlifetime trong php.ini quyết định session sống bao lâu. Mặc định 1440 giây thường quá ngắn cho ứng dụng cần login lâu. Nhưng nếu đặt quá dài mà không dọn định kỳ, inode sẽ cạn.
php -i | grep session.gc_maxlifetime
# Dọn session cũ hơn 2 ngày
sudo find /var/lib/php/sessions -type f -name 'sess_*' -mtime +2 -delete
Trên CentOS/RHEL có thể dùng tmpwatch:
sudo tmpwatch -m 48 /var/lib/php/sessions
sudo tmpwatch -m 168 /tmp
Trên Ubuntu/Debian hiện đại, dùng systemd-tmpfiles. Ví dụ tạo file /etc/tmpfiles.d/php-sessions.conf:
# /etc/tmpfiles.d/php-sessions.conf
d /var/lib/php/sessions 1733 root root 2d
Dòng trên nghĩa là dọn file trong thư mục sau 2 ngày. Kiểm tra bằng systemd-tmpfiles --clean. Với cache Laravel, Symfony, WordPress, thêm cron xóa cache cũ theo ngày.
7. Docker overlay2 và log container
Docker dùng overlay2 làm storage driver mặc định. Mỗi lần build image, mỗi layer, mỗi container restart đều tạo file trong /var/lib/docker/overlay2. Log container mặc định không giới hạn cũng là thủ phạm quen thuộc.
sudo du --inodes -xS /var/lib/docker/overlay2 | sort -rn | head
sudo find /var/lib/docker/overlay2 -type f | wc -l
sudo find /var/lib/docker/containers -name '*-json.log' -size +100M
Giới hạn log driver trong /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "5"
}
}
Sau khi sửa, chạy sudo systemctl restart docker. Lưu ý: container đang chạy phải được tạo lại để áp dụng cấu hình mới.
Dọn dẹp an toàn:
# Xóa image, container, network, build cache không dùng
sudo docker system prune -a
# Chỉ xóa build cache
sudo docker builder prune
# Xóa volume mồ côi, cẩn thận với dữ liệu
sudo docker volume prune
Đừng xóa trực tiếp file trong /var/lib/docker bằng rm. Docker có metadata riêng. Xóa tay dễ làm hỏng state và khiến container không khởi động được.
8. Phòng ngừa đầy inode trên server production
Dọn được một lần chưa đủ. Quan trọng hơn là đừng để nó tái diễn.
- Giám sát
df -i: đưa vào Zabbix, Prometheus, Netdata. Cảnh báo ở 80% và 90%. - Logrotate cho mọi log: kiểm tra
/etc/logrotate.d/, đảm bảo log nào cũng có config. - Dọn session/cache định kỳ: cron hoặc systemd-tmpfiles, đừng để tồn đọng.
- Giới hạn log Docker: đặt
max-sizevàmax-filetrongdaemon.json. - Chọn filesystem phù hợp: ext4 cấp phát inode cố định lúc
mkfs; XFS cấp phát động. Workload nhiều file nhỏ như mail, Docker, cache nên cân nhắc XFS. Nếu dùng ext4, có thể tăng inode bằngmkfs.ext4 -i 8192hoặc-T small, nhưng đánh đổi là block dành cho dữ liệu ít hơn. - Đừng nhầm
inotify.max_user_watchesvới inode: nó không giải quyết đầy inode. Ứng dụng watch quá nhiều file có thể tốn RAM kernel, nhưng không phải nguyên nhân làm cạn inode.
Ví dụ script cảnh báo đơn giản:
#!/bin/bash
THRESHOLD=85
df -i | awk -v t=$THRESHOLD 'NR>1 && $5+0 >= t {print "WARN: "$1" on "$6" IUse="$5}'
9. Vài lỗi hay gặp khi dọn inode
Xóa file nhưng df -i không giảm
Triệu chứng: rm hàng loạt file nhưng IUse% vẫn 100%.
Nguyên nhân: File vẫn đang được process giữ. Kernel chưa giải phóng inode.
Cách xử lý: tìm process đang giữ file rồi restart service.
sudo lsof +L1
# hoặc
sudo lsof | grep deleted | head
Restart PHP-FPM, nginx, app worker hoặc service tương ứng để kernel thu hồi inode.
Dọn xong vài giờ lại đầy
Triệu chứng: giải phóng được vài trăm nghìn inode, 2–4 giờ sau lại cạn.
Nguyên nhân: chưa xử lý gốc. Mail queue tiếp tục nhận mail lỗi, PHP session tiếp tục sinh file, cron tạo log mới.
Cách xử lý: kiểm tra cron, ứng dụng sinh file, mail queue. Tạm thời chặn nguồn sinh file bằng cách vô hiệu cron hoặc dừng MTA cho đến khi fix xong.
find hoặc du treo khi chạy trên thư mục lớn
Triệu chứng: find /var/spool/postfix -type f chạy hàng chục phút chưa xong.
Nguyên nhân: thư mục chứa hàng triệu file, I/O bị nghẽn.
Cách xử lý: chia nhỏ phạm vi, chạy song song, hoặc dùng -maxdepth.
sudo find /var/spool/postfix/deferred -mindepth 1 -maxdepth 1 -type d | \
xargs -P 4 -I {} sh -c 'echo -n "{} "; find "{}" -type f | wc -l'
Nếu vẫn treo, tạm dừng service đang ghi để giảm tải I/O trong lúc dọn. Với XFS, có thể dùng xfs_db để kiểm tra nhanh, nhưng chỉ chạy khi unmount hoặc ở chế độ read-only.
Lời kết
Xử lý đầy inode không khó nếu đi đúng thứ tự: xác nhận bằng df -i, truy vết bằng du --inodes và find, xóa an toàn theo lô, rồi xử lý nguyên nhân gốc. Ba điểm cần nhớ: đầy inode khác đầy dung lượng; file bị process giữ sẽ không giải phóng inode cho đến khi restart; và phòng ngừa bằng monitoring + logrotate quan trọng hơn chữa cháy.
All rights reserved