Ubuntu清理旧内核,释放/boot空间
/boot 被旧内核占满会导致 apt 报错并停止配置软件包。查看当前运行内核,安全删除旧 linux-image,避免误删正在使用的版本。
/boot 被旧内核占满时 apt 为什么会停止工作
在 Ubuntu 上,每次内核更新都会将一组新文件写入 /boot,并保留之前的文件。因此,容量较小的 /boot 分区会被占满,apt 无法完成安装。修复分为两步。先确定系统中哪些软件包是内核,以及当前启动的是哪个内核;然后使用 apt autoremove --purge 删除其余内核。
操作顺序很重要。正在运行的内核是绝不能删除的软件包。此时系统可能已经处于 apt 完全无法运行的状态。请先进行诊断。
实际的失败表现
一个内核版本会在 /boot 中安装两个较大的文件:压缩内核(vmlinuz-<version>)和 initramfs(初始 RAM 文件系统,initrd.img-<version>,内核挂载真实 root 文件系统前会先解包的一个小型归档文件)。initramfs 会在安装时于本机上构建,因此安装所需的是可用磁盘空间,而不只是下载带宽。磁盘没有剩余空间时,构建会失败,随之导致软件包安装失败。
update-initramfs: Generating /boot/initrd.img-6.8.0-64-generic
gzip: stdout: No space left on device
E: mkinitramfs failure gzip 1
update-initramfs: failed for /boot/initrd.img-6.8.0-64-generic with 1.
dpkg: error processing package linux-image-6.8.0-64-generic (--configure):
installed linux-image-6.8.0-64-generic package post-installation script subprocess returned error exit status 1版本字符串会因您的系统而异。压缩程序名称来自 COMPRESS= 中的 /etc/initramfs-tools/initramfs.conf,因此较新的镜像可能将文件命名为 zstd,而较旧的镜像可能命名为 gzip。能够确认此问题的两行是 No space left on device,以及其下方的 dpkg: error processing package 行。
之后,该软件包会处于半配置状态。之后每次运行 apt 都会再次尝试配置它,以相同方式失败,并以 E: Sub-process /usr/bin/dpkg returned an error code (1) 结束。这里的影响不止是磁盘空间不足:unattended-upgrades 会按计划运行,遇到相同错误后停止。服务器表面上运行正常,却会悄然停止应用安全补丁。这还意味着您尝试安装任何无关软件时,安装都会因同一行错误失败,而问题看起来像是由当时正在添加的软件引起的。因此,在 Ubuntu 上安装 Tailscale 失败时,首先按 apt 错误排查是有价值的。如果 apt update 在执行到这里之前就失败了,那是另一个问题,通常是deb822 源迁移后出现重复条目导致的。
检查 /boot 是否为独立分区
删除任何内容前,先确认实际要释放的是哪个文件系统中的空间。
findmnt /boot
findmnt -T /boot
df -h /boot /第一条命令仅在 /boot 是独立挂载点时输出一行。第二条命令始终会输出,并指出实际承载 /boot 的文件系统。如果它们显示的文件系统与 / 相同,则 /boot 只是根文件系统中的一个目录,不会单独耗尽空间:根文件系统已满,旧内核只是多个占用空间的因素之一。此时,sudo apt clean 会清空 /var/cache/apt/archives 下已下载的 .deb 文件,从而释放空间。对于具有实际 /boot 分区的计算机,apt clean 不会释放该分区中的任何空间,因为缓存位于另一个文件系统中。
现在获取后续操作所依据的数值。
df -h /boot
ls -lh /boot/vmlinuz-$(uname -r) /boot/initrd.img-$(uname -r)将 Avail 列与这两个文件的大小进行比较。initrd 较大。下一次内核更新还需要为另一对大致相同大小的文件预留空间,因此如果 Avail 小于当前 initrd,下一次更新就会失败。
查找当前运行的内核
uname -r
cat /var/run/reboot-required.pkgsuname -r会输出当前加载到内存中的内核发行版本字符串。将该字符串复制到其他位置。这是绝对不能删除的版本。
只有在某个软件包要求重启时,第二个文件才会存在。其中的 linux-image 行表示磁盘上已安装更新的内核,但当前尚未使用,因为安装完成后系统还没有重启。如果条件允许,请先重启,再执行清理。apt会保护当前运行的内核和最新的内核,因此在旧内核上运行时执行清理,会比实际需要多保留一个版本。
列出内核软件包并查看其状态
dpkg --list | grep -E 'linux-(image|modules|headers|tools)' | awk '{print $1, $2}'第一列是 dpkg 的状态代码。ii表示已安装且已配置。iF表示已安装但仅完成部分配置,这正是前文升级失败后留下的状态。rc表示软件包已删除,但其配置仍保留在磁盘上。它不占用 /boot 中的空间,可以安全清除。
第二列表示软件包的类型。名称中包含版本号的软件包(例如 linux-image-6.8.0-64-generic)是一个具体的内核。名称中不包含版本号的软件包(例如 linux-image-generic、linux-headers-generic 或 linux-generic)是元软件包。元软件包不包含内核。它的作用是依赖最新的带版本号内核,以便 apt upgrade 拉取新内核。删除元软件包后,系统将不再接收内核更新,而且之后不会发出任何警告。
这些软件包系列的作用如下。linux-image-* 在 /boot 中保存压缩后的内核。linux-modules-* 和 linux-modules-extra-* 在 /lib/modules 下保存驱动程序。linux-headers-* 在 /usr/src 中保存构建所需的头文件。因此,清除头文件会释放根文件系统空间,而不会释放 /boot 空间。如果问题是 /boot 分区已满,应查找镜像软件包。
ls -1 /boot/vmlinuz-*
ls -1 /lib/modules/这两组列表应彼此对应,也应与 dpkg --list 的输出一致。/lib/modules 中没有对应已安装软件包的目录,说明有人曾手动删除文件,留下了残留目录。
apt 如何决定保留哪些内核
apt autoremove 不会删除其认定为受保护的内核,受保护集合包括当前正在运行的内核。不同 Ubuntu 版本的保留策略有所变化,因此应从您自己的计算机读取策略,不要依赖任何地方记录的数字。
apt-config dump | grep -i -e neverautoremove -e versionedkernel
ls -l /etc/apt/apt.conf.d/01autoremove /etc/apt/apt.conf.d/01autoremove-kernelsAPT::NeverAutoRemove 是 apt autoremove 拒绝处理的软件包名称模式列表。APT::VersionedKernelPackages 是 apt 用来识别版本化内核软件包的名称前缀列表。在会生成 /etc/apt/apt.conf.d/01autoremove-kernels 的版本中,每次安装内核软件包时,/etc/kernel/postinst.d/apt-auto-removal 都会重写该文件,因此手动编辑没有作用:下一次安装内核时,您的修改就会被覆盖。在文件不存在的版本中,apt 会在内部应用相同的保护规则。无论哪种情况,apt-config dump 都会显示当前系统生效的规则;对于您的版本,应以该输出为准。
可以安全执行的清理操作
sudo apt update
sudo apt autoremove --purge --dry-run--dry-run不会修改磁盘内容,只会准确列出实际执行时将要删除的内容。请阅读此列表。出现以下两种情况时应停止操作。待删除列表中包含 linux-generic 或 linux-image-generic 等元软件包,表示某个机制将其标记为自动安装;删除它会导致内核不再更新。待删除列表中包含 uname -r 输出的字符串,表示正在运行的内核未受到保护。这种情况不应发生,继续操作前必须调查原因。
如果列表正确,请正式执行清理。
sudo apt autoremove --purge
df -h /boot--purge 还会删除遗留配置,而不仅是软件包本身。它只能额外释放少量空间,但可以避免 dpkg --list 持续累积 rc 行,从而使下一次审计更易于阅读。
然后确认启动菜单已重新构建。删除内核软件包时会自动运行 update-grub,因此菜单应只引用仍然存在的文件。
sudo grep -o 'vmlinuz-[^ ]*' /boot/grub/grub.cfg | sort -u
ls -1 /boot/vmlinuz-*第一个输出中的每个版本都必须出现在第二个输出中。启动菜单项如果指向已不存在的文件,正常运行的服务器就可能在 GRUB 提示符处停止。这是导致VPS 在内核更新后无法启动的一种情况;与其在救援控制台中处理,不如在此处提前避免,后者要容易得多。
为什么 apt autoremove 有时什么也不删除
apt autoremove 只会删除标记为自动安装的软件包,也就是作为其他软件包依赖项安装的软件包。您使用 apt install linux-image-6.8.0-40-generic 自行安装的内核会被标记为手动安装;无论它多么旧,autoremove 都不会删除它。
apt-mark showmanual | grep -E '^linux-'该输出中的任何带版本号的内核都不会被 autoremove 处理。请使用您自己的列表中的版本字符串,将它们重新标记为自动安装:
sudo apt-mark auto linux-image-6.8.0-40-generic linux-modules-6.8.0-40-generic
sudo apt autoremove --purge --dry-run保留标记为手动安装的元软件包。它们本来就应该是手动安装的,因为这些软件包是您主动选择安装的。
有意删除一个指定的内核
有时您希望立即删除某个特定版本,而不是等到策略允许时再删除。指定映像包,让 apt 处理其余依赖关系。
sudo apt purge linux-image-6.8.0-40-genericapt 在执行任何操作前会打印删除列表,因为 linux-modules-extra-* 依赖映像包,必须在同一个事务中删除。该列表是实际的安全检查点,您可以在这里发现某个元包是否会与计划删除的版本一起被移除。如果列表中包含任何意外内容,请回答 n。随后使用 sudo apt autoremove --purge,清理现在已失去存在必要的模块包和头文件包。
切勿删除正在运行的内核
已加载到内存中的内核会在其文件被删除后继续运行,因此起初看不出任何故障。真正出问题的是内核尚未加载的内容。清除 linux-modules-$(uname -r) 会删除 /lib/modules/$(uname -r)/,因此后续加载模块时会失败:
modprobe: FATAL: Module nf_tables not found in directory /lib/modules/6.8.0-64-generic从此之后,重新加载防火墙配置会失败,挂载此内核自启动以来尚未处理过的文件系统类型也会失败。同时,/boot/vmlinuz-$(uname -r) 已被删除,因此启动菜单不再提供当前正在运行的内核,下一次重启时系统会启动到其他内核。此时机器仍在处理网络流量,但已经无法启动。每次都要将 uname -r 与删除列表进行核对。
/boot 空间过满,导致 apt 完全无法运行
这是最常把用户引导到本页的情况。apt autoremove 需要 dpkg 先完成半配置状态的内核软件包配置,而该步骤会重新构建 initramfs;initramfs 需要写入 /boot,但其中没有可用空间。手动打破这个循环一次。
uname -r
ls -1 /boot/initrd.img-*选择一个版本不是 uname -r 所显示字符串的 initrd,然后删除这个文件。
sudo rm /boot/initrd.img-6.8.0-40-generic
sudo apt --fix-broken install
sudo apt autoremove --purge
sudo update-grub每一行都有明确用途。rm 是有意设置的例外,它会让 dpkg 认为某个文件存在,尽管该文件实际不存在。现在已经有空间写入 initramfs,apt --fix-broken install 会完成此前失败的配置。随后,autoremove --purge 会删除您刚才删除其文件的软件包,以及其他旧版本,从而使 dpkg 与磁盘上的实际内容重新一致。update-grub 会根据实际存在的文件重新生成菜单。在执行 rm 和 update-grub 之间不要重启,因为此时菜单仍可能指向刚才删除的文件。如果 dpkg 报告操作被中断,sudo dpkg --configure -a 会执行与 apt --fix-broken install 相同的修复。
dnf 系统中的相同操作
如果您的 VPS 运行 Fedora 或 Rocky Linux 等 RHEL 重构版,处理机制正好相反。Debian 和 Ubuntu 通过 apt 的自动删除规则保护内核,将清理操作留给您手动执行,或由 unattended-upgrades 触发;而 dnf 强制执行一个名为 installonly_limit 的数量限制,只要安装新内核会超出该限制,就会自动删除最旧的内核。使用 grep installonly_limit /etc/dnf/dnf.conf 和 man 5 dnf.conf 查看当前生效的值,使用 sudo dnf remove --oldinstallonly 清理现有的待删除内核。正在运行的内核同样会受到保护。有关这两个软件包管理器之间更完整的命令对应关系,请参阅 dnf 和 apt 命令对应关系。
避免问题再次发生
依赖人工记忆的清理最终一定会失败,因此应将它配置到安装内核的软件包管理流程中。打开 /etc/apt/apt.conf.d/50unattended-upgrades,查找以下配置项。系统提供的文件已经以注释形式包含这些配置项:
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";取消这些配置项的注释,不要在文件末尾追加第二份。apt 配置中,同一配置项的最后一次赋值会生效,因此重复配置会导致文件内容相互矛盾,也无法确定实际使用的值。检查解析器最终采用的配置,并观察一次没有执行任何更改的运行:
apt-config dump | grep -i 'Unattended-Upgrade::Remove'
sudo unattended-upgrade --dry-run --debug
sudo tail -n 40 /var/log/unattended-upgrades/unattended-upgrades.log日志可以证明这一点。它会记录每次运行,因此因磁盘空间不足而失败的升级,早在有人发现系统未及时安装补丁之前,就会出现在日志中。该配置的其余部分请参阅Ubuntu 上的自动安全更新。
下一个内核发布前,需要检查一个数值。检查方法与本指南开头使用的那对命令相同:
df -h /boot
ls -lh /boot/initrd.img-$(uname -r)如果 Avail 没有明显大于该文件大小,下一个内核就会按上述方式失败。因此应立即修复,不要等到升级过程中再处理。将此检查与其他VPS 磁盘健康检查一起执行,只需一分钟。版本升级前尤其需要检查,因为将 Ubuntu 24.04 升级到 26.04会在流程早期安装新内核,而当 /boot 空间不足时,do-release-upgrade 将拒绝继续。
FAQ
为什么 Ubuntu 会保留旧内核,而不是将其删除?
因为内核无法启动时,您将没有其他内核可供选择。保留上一版本后,即使更新失败,也可以从 GRUB 菜单恢复,而不必使用服务商的救援控制台。apt 因此会保护一组内核软件包,避免其被自动删除,并始终包括当前正在运行的内核。运行 apt-config dump | grep -i neverautoremove,查看当前版本确切保护的匹配模式,因为不同版本的策略可能有所变化。
在生产服务器上运行 apt autoremove --purge 是否安全?
可以,但前提是先查看试运行结果。运行不会写入任何内容的 sudo apt autoremove --purge --dry-run,然后检查输出列表。如果列表包含类似 linux-generic 或 linux-image-generic 的元软件包,请停止操作,因为删除这些软件包会导致后续内核无法更新。如果列表包含 uname -r 输出的版本字符串,也请停止操作。如果两者都不存在,则待删除的是旧内核和孤立依赖项。
apt autoremove 没有删除任何内容,而 /boot 仍然已满。现在该怎么办?
旧内核很可能被标记为手动安装,而 autoremove 只处理标记为自动安装的软件包。运行 apt-mark showmanual | grep -E '^linux-'。其中列出的任何带版本号的内核,都曾在某个时间点被手动安装。使用 sudo apt-mark auto linux-image-<version> 将其标记为自动安装,然后再次运行试运行;或者使用 sudo apt purge linux-image-<version> 直接清除该版本。
可以手动删除 /boot 中的文件吗?
只有在需要单次处理,并且 /boot 已满到 apt 无法配置损坏的内核软件包时,才应这样做。删除一个版本不是 uname -r 输出结果的 initrd.img-<version> 文件,然后立即运行 sudo apt --fix-broken install、sudo apt autoremove --purge 和 sudo update-grub。删除文件后不执行这些后续步骤,会导致 dpkg 记录文件已经不存在的软件包,并让 GRUB 菜单项继续指向缺失文件。这样一来,机器会在下一次重启时失败,而不是在您犯错的当下失败。