VPS 磁盘突然 100% 爆满?老哥手把手教你从 df 到清理,5 大空间杀手逐个干掉
2026-08-14 · DevCraft Studio
明明没放什么文件,df -h 却显示磁盘 100% 爆满导致服务崩溃?本文讲清日志、Docker、被删仍占空间的文件等"看不见的空间去哪了",并给出 journalctl、docker system prune、ncdu、lsof 等可直接复制的排查清理命令。
一、先别慌:为什么"明明没放东西"磁盘却爆了
不知道你有没有经历过这种半夜血压拉满的场面:网站突然 500,SSH 登上去第一句就看到 No space left on device,数据库写不进、Nginx 建不了临时文件,整个服务全瘫。你心里嘀咕:"我这台 VPS 就跑个小博客,几 G 的东西,40G 的盘怎么可能满?"
这其实是每个玩 VPS 的老哥迟早要踩的坑。Linux 的"磁盘占用"对小白来说是个黑盒——你眼睛看得到的文件,往往只占真正占用的一小部分。那些真正吃空间的,是你看不见、或者"以为删了但其实没删掉"的东西。常见的元凶就这几类:
- 日志文件无限膨胀:Systemd 的 journal 日志、Nginx/Apache 的 access.log 几亿行,你从不看,它默默长到几 G 甚至十几 G。
- Docker 镜像和虚悬镜像:跑容器时拉的一堆镜像、构建缓存、没人用的 volume,悄咪咪吃掉几 G。
- 被删掉但还被进程占用的"幽灵文件":文件被 rm 了,但有个进程还开着它的句柄,空间不会释放,重启那个进程才释放。这是 df 显示满、du 却算不出来最常见的原因。
- inode 耗尽:盘还有空间,但 inode(文件索引节点)用光了,照样写不进任何文件。常见于 /tmp、PHP session 目录、邮件队列堆了几十万个小文件。
- 缓存与旧内核:apt 的包缓存、snap 旧版本、更新后没清的旧内核,都是隐藏的空间吸血鬼。
所以排查的正确姿势不是上来一顿 rm -rf,而是先搞清楚空间到底被谁吃了,再精准清理。下面是一套我从 df 一路查到清理的标准流程。
二、标准排查流程:从 df 到定位到清理
记住一个铁律:df 看分区整体,du 看目录明细,lsof 看进程持有的已删文件。三者配合才能还原真相。
第一步:df -h 看哪个分区爆了
df -h
# 只看某个挂载点,比如根分区或 /var
df -h /
df -h /var输出里 Use% 那一列 100% 的就是罪案现场。注意一定要看挂载点——有时候是 / 满了,有时候是 /var 或 /home 单独的分区满了。
第二步:df -i 看 inode 有没有耗尽
df -i看 IUse% 列。如果这里接近 100% 而容量还有剩余,恭喜你中招"inode 耗尽",问题不是大文件而是海量小文件。常见于 /var/spool/postfix、/tmp、PHP 的 session 目录。
第三步:du 一层层往下挖
# 看根分区下各一级目录占用,按大小排序取前 20
du -h --max-depth=1 / 2>/dev/null | sort -hr | head -20
# 钻进最大的那个目录再往下挖
du -h --max-depth=1 /var 2>/dev/null | sort -hr | head -20
du -h --max-depth=1 /var/log 2>/dev/null | sort -hr | head -10小技巧:sort -hr 的 -h 是按人类可读单位(K/M/G)正确排序的,千万别漏掉 -h,否则 1.2G 会排在 900M 前面算错,让你追错目录。
第四步:ncdu 可视化交互排查(强烈推荐)
du 一层层敲有点累,老哥们更爱用 ncdu——一个基于 ncurses 的可视化磁盘分析工具,方向键导航、回车下钻,谁占空间一目了然。
# 装一下(Debian/Ubuntu)
apt install -y ncdu
# 扫描根分区,耐心等它数完
ncdu /
# 也可以只扫某个怀疑的目录
ncdu /var进去之后:方向键移动,Enter 进目录,d 直接删除文件,q 退出。这是定位"空间去哪了"最快的方式,没有之一。新手第一次用 ncdu 基本都会惊呼"原来是你占了这么多"。
第五步:lsof 抓"幽灵文件"(df 满但 du 算不出时必用)
# 找出被删但进程还打开着、仍占用空间的文件
lsof +L1 | grep deleted
# 或者更全一点
lsof | grep deletedSIZE/OFF 列就是它占着没释放的空间。这种文件你用 ls 根本看不到(目录项已经没了),但 df 算得进去。解决办法是重启持有它的那个进程:
# 比如是 nginx 的日志句柄没释放
systemctl restart nginx
# 如果是某个服务,重启对应服务即可;实在不敢重启就 reboot重启之后空间立马回来,比删文件还灵。这就是"df 满 du 不算"谜题的标准答案。
三、罪魁祸首 1:Systemd 日志文件膨胀
现在绝大多数 VPS(Ubuntu 18.04+、Debian 9+、CentOS 7+)都用 systemd,它的 journal 日志默认不限制大小。一旦某个服务疯狂报错(比如你配错的某个 daemon 每秒写 50 行错误),journal 能在几天内悄悄涨到 2~5G,你平时根本不会去看 /var/log/journal。
先看看它现在吃得有多胖:
journalctl --disk-usage临时瘦身,二选一:
# 只保留最近 100M 的日志
journalctl --vacuum-size=100M
# 或者只保留最近 7 天
journalctl --vacuum-time=7d但这只是治标,下次还会涨。要治本得改配置,编辑 /etc/systemd/journald.conf:
# 限制 journal 最大占用,比如 500M
SystemMaxUse=500M
# 始终为系统保留至少 1G 空闲空间,满了就丢旧日志
SystemKeepFree=1G
# 日志最长保留 1 个月(按需)
MaxRetentionSec=1month改完让 journald 重新加载:
systemctl restart systemd-journald配好这几行,你的 VPS 以后再也不会被日志撑爆,属于"装好就忘"的省心配置。
四、罪魁祸首 2:Docker 镜像、虚悬镜像与构建缓存
如果你这台 VPS 跑了 Docker,那它极有可能是最大的空间吞噬者。镜像、停止的容器、虚悬镜像(dangling,就是构建失败或换版后留下的
先看 Docker 到底吃了多少:
docker system df它会清晰列出 Images / Containers / Volumes / Build Cache 各自的占用。清理从最安全到最狠:
# 1. 清掉停止的容器、无用网络、虚悬镜像、构建缓存(不动正在用的)
docker system prune
# 2. 连未被任何容器引用的镜像也一起删(慎用,会删掉你没在跑的镜像)
docker system prune -a
# 3. 顺手清掉没人用的 volume(数据无价,先确认没重要数据!)
docker volume prune我自己的习惯是每月跑一次 docker system prune -a 配合 docker volume prune,能轻松腾出几 G。如果你有 CI/构建频繁跑,建议写个定时任务:
# 写入 /etc/cron.weekly/docker-clean,每周自动清(保留正在用的)
cat > /etc/cron.weekly/docker-clean <<'EOF'
#!/bin/bash
docker system prune -af --filter "until=72h" >/dev/null 2>&1
EOF
chmod +x /etc/cron.weekly/docker-clean这样只清 72 小时前没在用的东西,基本不会误伤正在跑的服务。
五、罪魁祸首 3:Nginx/Apache 的 access.log 狂飙
网站一旦被爬虫盯上、或者被人扫漏洞,access.log 能以每分钟几万行的速度狂飙,几天写满几十 G 不是梦。更坑的是:你直接 rm 掉这个日志文件,空间不会释放——因为 Nginx 还攥着那个文件句柄,直到进程重启。
正确做法是"截断"而不是"删除":
# 瞬间把文件清空为 0 字节,进程句柄不变,空间立刻释放
truncate -s 0 /var/log/nginx/access.log
truncate -s 0 /var/log/nginx/error.log
# 等价写法:重定向清空
> /var/log/nginx/access.log但截断只是救急,真正的解决是上 logrotate 让日志自动切割、压缩、过期删除。给 Nginx 建个配置 /etc/logrotate.d/nginx:
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}这个配置每天切一次、留 14 天、自动 gzip 压缩,切完给 Nginx 发 USR1 信号让它重新打开日志文件。生效后你再也不会看到单日志文件涨到几十 G。kill -USR1 这个信号是关键——它让 Nginx 不重启用新文件,比 truncate 优雅得多。
另外几个省空间的小动作:
- 把不重要的访问日志直接关掉或降到最低级别,访问量大的静态站可以只记 error。
- 日志上外部收集(如用 Vector / rsyslog 转发到对象存储),本地只留少量。
- Apache 用户同理,配
/etc/logrotate.d/apache2并 reload。
六、其他常见的隐藏空间杀手
除了上面三大主角,下面这些也经常偷偷吃空间,顺手清掉绝不亏:
APT / YUM 包缓存
# Debian/Ubuntu:清掉下载缓存的 .deb 包,能回收几 G
apt clean
apt autoremove --purge
# CentOS/RHEL
yum clean allSnap 旧版本(Ubuntu 桌面/部分云镜像)
Snap 默认保留一堆旧版本快照,能占好几 G。限制它只留 2 个版本:
snap set system refresh.retain=2
# 手动删禁用的旧版本
snap list --all | awk '/disabled/{print $1, $3}' | while read name rev; do snap remove "$name" --revision="$rev"; done更新后没清的旧内核
每个旧内核 200~400M,更新一年能攒一堆。让 apt 自动处理最安全:
apt autoremove --purge
# 千万别手删 /boot 和 /lib/modules 下的文件,会搞崩启动Core dump 与崩溃文件
一个进程崩了可能把几 G 内存 dump 到 /var/crash 或 core 文件。检查并清理:
du -sh /var/crash 2>/dev/null
rm -f /var/crash/*家目录里的缓存和回收站
du -sh ~/.cache ~/.thumbnails ~/.local/share/Trash 2>/dev/null
# 找家目录下大于 500M 的文件
find ~ -type f -size +500M 2>/dev/null七、总结 / 避坑清单
说到底,VPS 磁盘爆满 90% 都是"日志 + Docker + 幽灵文件"这三样。记住下面这张清单,下次半夜告警你就能 5 分钟搞定:
- 先诊断再动手:
df -h看分区、df -i看 inode、du -h --max-depth=1或ncdu定位目录,别上来就 rm。 - df 满但 du 算不出:90% 是被删但进程还打开的文件,用
lsof +L1 | grep deleted找到,重启对应进程释放。 - journal 日志:
journalctl --vacuum-size=100M急救,并改/etc/systemd/journald.conf的SystemMaxUse永久封顶。 - Docker:
docker system df看占用,docker system prune -a清虚悬镜像,定期docker volume prune。 - Web 日志:用
truncate -s 0截断而非删除;上 logrotate 自动切割,postrotate 发kill -USR1让 Nginx 重新开文件。 - 治本靠监控:每台上新机先配磁盘告警(如 Node Exporter + Prometheus,或最简单 crontab 里
df -h | mail),别等 100% 才发现问题。 - 别手删系统文件:
/boot、/lib/modules、正在跑的日志句柄,删错直接开不了机。
把这几点养成习惯,你的 VPS 基本告别"磁盘突然爆满"的惊魂夜。省下的不只是空间,还有半夜被告警叫醒的血压。
延伸阅读
更多相关攻略推荐:【VPS 硬件选型指南 (CPU 篇) 04】AMD EPYC 还是、【发行版选型 07】Arch Linux 深度科普:滚动发布、pac、【VPS 进阶玩法精选 014】ARM 架构 VPS 值不值得上?A、【对象存储 01】对象存储怎么选:Backblaze B2 vs C、【TCP优化 01】美国 VPS 跑不快?多半是没开 BBR,一个命。