Next.js网站构建失败并提示磁盘不足,如何检查容量、inode与日志增长
面向维护Next.js网站的站长和技术负责人,介绍构建出现ENOSPC时如何依次检查文件系统容量、inode、日志增长、配额及I/O状态,并说明安全清理原则与修复后的构建验证方法。

Next.js网站执行 npm run build、pnpm build 或 CI/CD 发布任务时,如果出现 ENOSPC: no space left on device,不应只根据“磁盘不足”四个字直接删除文件。Linux 中的 ENOSPC 既可能表示磁盘数据块已满,也可能是 inode 耗尽;如果错误发生在开发模式或文件监听阶段,还可能是 inotify 监听资源达到上限。除此之外,用户配额、容器存储层、只读文件系统和持续增长的日志也会产生相似现象。
建议先检查构建目录、/tmp 和运行用户主目录所在文件系统,再依次排查容量、inode、日志、已删除但未释放的文件以及文件系统和 I/O 状态。确认根因前,不要直接清空整个 .next、node_modules、Docker 数据目录或系统日志,以免扩大故障范围。
先确认错误发生在哪个文件系统
Next.js构建并非只向项目目录写入数据。根据包管理器、部署方式和项目配置,构建过程还可能使用以下位置:
- 项目中的
.next,或next.config.js、next.config.mjs中自定义的distDir /tmp或环境变量TMPDIR指定的临时目录- 当前用户的 npm、pnpm 或 Yarn 缓存目录
- CI Runner 的工作目录和缓存目录
- Docker 容器可写层或挂载卷
- PM2、systemd、Docker及应用自身的日志目录
以下命令适用于常见Linux环境。df、du 参数以 GNU coreutils 为例;Alpine、BusyBox或精简容器中的选项可能不同,应先执行 cat /etc/os-release 和命令帮助确认。
cat /etc/os-release
pwd
id
node -v
printf 'TMPDIR=%s\n' "${TMPDIR:-/tmp}"
df -hT "$PWD" "${TMPDIR:-/tmp}" "$HOME"
df -i "$PWD" "${TMPDIR:-/tmp}" "$HOME"
findmnt -T "$PWD"
重点不是只看根分区,而是确认实际写入路径对应哪个挂载点。例如,项目位于独立数据盘,但 /tmp 仍在根分区,那么根分区已满时,项目盘还有空间也无法完成构建。
常见报错可以按以下方式区分:
| 报错或现象 | 优先检查方向 | 判断依据 |
|---|---|---|
ENOSPC: no space left on device |
容量、inode、临时目录 | 同时检查 df -h 与 df -i |
EDQUOT: disk quota exceeded |
用户或项目配额 | 文件系统仍有空间,但当前用户不能继续写入 |
EROFS: read-only file system |
文件系统被只读挂载 | 检查挂载参数和内核日志 |
EIO: input/output error |
存储设备或文件系统异常 | 检查内核日志与I/O状态 |
System limit for number of file watchers reached |
inotify限制 | 通常发生在开发监听阶段,不等同于磁盘已满 |
| 构建长时间停顿、超时但无容量报错 | I/O延迟、内存压力或进程阻塞 | 结合 iostat、系统日志和构建输出判断 |
第一层:检查磁盘容量是否真的用尽
先查看目标文件系统的总量、已用量和可用量:
df -hT "$PWD"
df -hT "${TMPDIR:-/tmp}"
df -hT "$HOME"
如果项目所在分区的可用空间很少,需要进一步找出空间由哪些目录占用。建议先从项目和日志目录开始,不要一上来扫描整个根目录。du 扫描大量文件会产生额外I/O,业务繁忙时应谨慎执行。
du -xhd1 "$PWD" 2>/dev/null | sort -h
sudo du -xhd1 /var/log 2>/dev/null | sort -h
其中,-x 表示不跨越其他文件系统,能够避免扫描挂载在子目录中的其他磁盘。若项目位于 /srv/example,还可以继续逐层缩小范围:
du -xhd1 /srv/example 2>/dev/null | sort -h
du -xhd1 /srv/example/.next 2>/dev/null | sort -h
需要重点关注:
.next/cache、.next/server或自定义构建输出目录- 多个未清理的历史发布目录
- CI工作区中的旧构建产物
- npm、pnpm或Yarn缓存
- 应用日志、访问日志和错误日志
- 容器镜像、可写层以及未使用的构建缓存
df很满,但du找不到大文件
如果 df 显示空间已被占用,而各目录的 du 统计明显较小,常见原因是文件已经被删除,但进程仍然保持文件句柄。日志文件最容易出现这种情况:文件名已经不存在,空间却要等进程关闭句柄后才会释放。
安装了 lsof 时可以检查:
sudo lsof +L1
输出中应重点查看 SIZE/OFF、进程名、PID和原文件路径。如果确认是某个日志文件被删除后仍由服务占用,应通过对应的服务管理器执行日志重开、平滑重载或维护窗口内重启。
不要看到PID后直接使用 kill -9。强制终止可能中断正在处理的请求,也可能损坏尚未完成的写入。systemd、PM2和Docker的处理方式不同,应先确认实际运行方式:
ps -ef | grep -E 'next|node|pm2' | grep -v grep
systemctl list-units --type=service | grep -Ei 'next|node'
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'
以上命令只用于识别服务,不要同时对多个管理器执行重启。
第二层:容量还有剩余,但inode可能已经耗尽
inode用于记录文件和目录的元数据。一个分区即使还有较多数据容量,只要可用inode为零,也无法创建新文件。Next.js、包管理器和前端依赖通常包含大量小文件,因此旧版 node_modules、多份发布目录和构建缓存容易消耗inode。
使用以下命令检查:
df -i "$PWD"
df -i "${TMPDIR:-/tmp}"
df -i "$HOME"
如果 IUse% 已接近耗尽,或者 IFree 为零,就需要找出小文件最集中的目录。GNU du 支持按inode数量统计:
du --inodes -x -d1 "$PWD" 2>/dev/null | sort -n
也可以针对发布目录、缓存目录逐层检查:
du --inodes -x -d1 /srv/example 2>/dev/null | sort -n
du --inodes -x -d1 "$HOME/.npm" 2>/dev/null | sort -n
inode不足时,单纯删除一个体积很大的日志文件未必有效,因为它只释放一个inode。更有效的处理对象通常是:
- 已下线版本中的
node_modules - 无需保留的CI工作区
- 大量小型缓存文件
- 过期会话文件或临时文件
- 重复保存的历史发布目录
清理前应核对目录是否属于当前生产版本。采用软链接切换发布目录时,可以先查看链接目标:
readlink -f /srv/example/current
ls -ld /srv/example/current
当前版本及其回滚版本不应直接删除。对于其他历史版本,应先确认不被进程、定时任务或回滚机制引用,再根据既定保留策略处理。
第三层:定位持续增长的日志
如果释放空间后很快再次出现磁盘不足,问题通常不是“一次性文件太大”,而是某类日志持续写入。需要查明日志产生者和增长速度,而不是反复手动删除。
查找系统日志中的大文件
以下命令使用 GNU find,扫描 /var/log 时可能产生I/O开销:
sudo find /var/log -xdev -type f -printf '%s %p\n' 2>/dev/null \
| sort -nr \
| head -n 30
常见位置包括:
/var/log/nginx//var/log/syslog或/var/log/messages/var/log/journal/- 应用自行配置的日志目录
- PM2的
~/.pm2/logs/ - Docker默认JSON日志文件
检查systemd journal
使用systemd的系统可以查看journal占用量:
journalctl --disk-usage
如果Next.js网站由systemd服务管理,可先确认服务名,再查看近期日志。下面的服务名仅为变量示例,应替换为实际名称:
SERVICE=nextjs.service
journalctl -u "$SERVICE" --since "24 hours ago" --no-pager | tail -n 200
如果同一条异常每秒重复出现,应优先修复应用错误或健康检查配置。仅限制日志大小虽然能缓解磁盘压力,却不会消除错误源。
需要回收journal历史空间时,应先确认审计和故障留存要求,并将必要日志备份到其他文件系统。以下命令会删除超过保留时间的归档日志,不能作为未经确认的默认操作:
sudo journalctl --vacuum-time=14d
保留时间应按实际运维要求调整,执行后再次使用 journalctl --disk-usage 核对。
检查PM2日志
如果使用PM2运行Next.js,可先查看日志路径和近期输出:
pm2 list
pm2 logs --lines 100
du -h -d1 "$HOME/.pm2/logs" 2>/dev/null | sort -h
PM2日志增长通常与未配置轮转、应用反复重启或异常堆栈循环输出有关。不要在未备份的情况下直接执行清空全部日志的操作,应先确认故障证据是否已经保存,并配置与当前PM2环境兼容的轮转方案。
检查Docker日志与存储占用
容器内看到的磁盘空间和宿主机实际存储层可能不同,因此要分别检查容器和宿主机:
docker system df -v
docker ps --size
查看某个容器实际使用的日志文件:
CONTAINER=nextjs-app
docker inspect --format='{{.HostConfig.LogConfig.Type}}' "$CONTAINER"
docker inspect --format='{{.LogPath}}' "$CONTAINER"
不要直接删除 /var/lib/docker 下的文件,也不要在生产环境中未经筛选执行带 -a 或 --volumes 的清理命令。此类操作可能删除仍需回滚的镜像、构建缓存或持久化数据。更稳妥的方式是先识别占用来源,再配置Docker日志轮转,并评估重启Docker服务对现有容器的影响。
第四层:检查配额、只读挂载和I/O异常
用户配额导致“有空间却写不进去”
当 df -h 和 df -i 都正常,但构建日志出现 EDQUOT,应检查执行构建的用户是否受到磁盘配额限制:
quota -s
id
配额工具和配置方式取决于文件系统及系统环境。XFS项目配额、容器存储配额和托管平台工作区限制不能仅依靠普通 quota 命令判断,需要由具备权限的管理员检查挂载参数和配额策略。
文件系统变成只读
先确认项目所在挂载点及其选项:
findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS -T "$PWD"
如果选项中出现 ro,或者构建报 EROFS,继续检查内核日志:
sudo journalctl -k -b --no-pager \
| grep -Ei 'I/O error|read-only|EXT4-fs error|XFS.*error|buffer I/O'
某些系统限制普通用户读取内核日志,此时需要管理员权限。若日志中出现文件系统错误、设备重置或I/O错误,不要直接强制重新挂载为可写,也不要对已挂载文件系统运行 fsck。应先停止非必要写入、备份关键数据,再安排离线检查或存储层故障处理。
容量正常但构建异常缓慢
构建会读取大量依赖并生成许多小文件,磁盘高延迟可能使任务超时,即使空间没有耗尽。系统已安装 sysstat 时,可以观察:
iostat -xz 1 5
重点结合以下字段判断:
await:请求完成等待时间是否持续升高aqu-sz:设备队列是否持续堆积%util:设备是否长时间繁忙- 读写吞吐和请求数量是否与构建时间吻合
这些指标不能脱离设备类型和业务负载使用固定阈值判断。如果只有并发构建时队列明显上升,可以减少并行构建数量、错开备份任务或将构建工作区与高写入日志分离。如果同时出现内核I/O错误,则应优先处理存储或文件系统问题,而不是反复重跑构建。
清理Next.js构建目录时要区分“缓存”和“当前产物”
默认情况下,Next.js将构建结果写入 .next。其中不仅有可再生成的缓存,还可能包含当前运行实例需要的服务端文件、静态资源和增量缓存。直接删除整个 .next 可能导致正在运行的网站返回错误。
先检查项目是否修改了输出目录:
grep -R "distDir" next.config.js next.config.mjs next.config.ts 2>/dev/null
du -xhd1 .next 2>/dev/null | sort -h
处理原则如下:
- CI临时工作区中的
.next通常可在确认任务已结束后重建。 - 当前生产版本的
.next不应在服务运行期间直接删除。 .next/cache通常可以重新生成,但删除后会失去构建缓存,下一次构建可能更慢;运行时缓存行为还与Next.js版本和部署方式有关。node_modules不是简单的临时目录。删除后必须能够依赖锁文件重新安装,并确认私有包源和网络访问正常。- 历史发布目录应按版本保留策略清理,至少保留确认可用的回滚版本。
如果必须清理CI工作区,先校验变量和当前路径,避免路径为空或指向生产目录。以下示例只适用于已经确认可丢弃的CI工作区,rm -rf不可直接套用到在线目录:
WORKSPACE=/srv/ci-workspaces/example
cd "$WORKSPACE" || exit 1
test "$PWD" = "$WORKSPACE" || exit 1
printf '即将检查的目录:%s\n' "$PWD"
du -sh .next 2>/dev/null
确认目录无误、构建进程已经停止且不需要保留现场后,再按内部清理流程删除对应缓存或重建整个工作区。生产服务器更适合使用“新目录构建、验证后切换”的发布方式,避免构建和在线服务共同修改同一份 .next。
修复后如何确认Next.js网站已经恢复
释放空间或修复文件系统后,不能只看首页能够访问。应重新验证构建路径、inode、日志和退出状态。
先检查相关文件系统:
df -hT "$PWD" "${TMPDIR:-/tmp}" "$HOME"
df -i "$PWD" "${TMPDIR:-/tmp}" "$HOME"
然后使用实际包管理器重新执行构建。例如项目使用npm时:
npm run build
使用pnpm或Yarn的项目,应以锁文件和现有部署流程为准,不要临时更换包管理器。构建成功至少应满足:
- 命令退出状态为0
- 不再出现
ENOSPC、EDQUOT、EROFS或EIO - 配置的构建输出目录已生成
- 构建过程中目标分区和inode没有再次接近耗尽
- 应用启动后静态资源、服务端渲染和API路由可以正常访问
- systemd、PM2或容器日志中没有持续重复的新异常
可以在另一个终端观察构建期间的容量变化:
watch -n 2 'df -hT . /tmp "$HOME"; echo; df -i . /tmp "$HOME"'
watch 并非所有精简系统都默认提供。如果系统没有该命令,可以在构建前后分别记录 df 输出进行比较。不要只按当前使用率设置告警,还应记录一次完整构建新增了多少数据块和inode,并为日志轮转、临时文件及回滚版本预留空间。
后续监控至少应覆盖项目分区、/tmp、用户主目录、inode使用量、日志增长速度、Docker存储占用和磁盘I/O延迟。若空间释放后仍快速下降,应先找到持续写入的进程;若容量与inode均正常但仍报 ENOSPC,再核对错误是否明确指向文件监听限制:
sysctl fs.inotify.max_user_watches
sysctl fs.inotify.max_user_instances
inotify参数调整会影响系统资源占用,不应仅为绕过报错而盲目增大。只有确认问题发生在开发监听或文件监控阶段,并排除容量、inode和配额后,才应根据实际进程数量评估调整。