Ubuntu清理旧内核并释放/boot空间
Ubuntu的/boot被旧linux-image包填满后,apt会反复报错并停止配置。检查df -h、当前运行内核和可安全删除的版本,仅保留正在使用的内核。
/boot 被旧内核填满后 apt 为什么会停止工作
在 Ubuntu 上,每次内核更新都会在 /boot 中写入一组新文件,并保留之前的文件。因此,容量较小的 /boot 分区会被填满,apt 无法完成安装。修复分为两步。先确定系统上的内核软件包,以及当前启动所使用的内核;然后使用 apt autoremove --purge 删除其余内核。
操作顺序很重要。正在运行的内核对应的软件包不能删除,而系统可能已经处于 apt 完全无法运行的状态。请先进行诊断。
实际故障表现
安装一个内核版本时,系统会在 /boot 中写入两个较大的文件:压缩内核(vmlinuz-<version>)和 initramfs(初始 RAM 文件系统,initrd.img-<version>,这是内核挂载真实根文件系统前解包的小型归档文件)。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版本字符串会因系统而异。压缩程序名称来自 /etc/initramfs-tools/initramfs.conf 中的 COMPRESS=,因此较新的镜像可能将文件命名为 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请保留标记为手动安装的元软件包。它们本来就应标记为手动安装,因为这是您明确要求安装的内容。
有意删除指定的内核
有时您需要立即删除某个特定版本,而不是等到策略允许时再删除。指定 image 软件包,让 apt 处理其余工作。
sudo apt purge linux-image-6.8.0-40-genericapt 执行任何操作前会先输出删除列表,因为 linux-modules-extra-* 依赖 image 软件包,必须在同一事务中删除。该列表是实际的安全检查点,您可以据此发现某个 meta 软件包是否与要删除的版本一并被删除。如果列表中包含任何意外内容,请回答 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 将拒绝继续。如果您的 LTS 服务器尚未收到该升级,原因是发布时间安排,而不是系统故障。Ubuntu 会等到26.04.1 点版本发布后才开放 LTS 到 LTS 的升级,这样您就有一个明确的时间窗口,可以先整理好 /boot。
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 无法配置损坏的内核软件包时,才应有意执行一次此操作。删除一个 initrd.img-<version> 文件,并确认其版本不是 uname -r 的输出,然后立即运行 sudo apt --fix-broken install、sudo apt autoremove --purge 和 sudo update-grub。删除文件后不执行这些步骤,会使 dpkg 记录仍然存在但文件已被删除的软件包,也会使 GRUB 菜单项继续指向不存在的文件。这样一来,机器会在下次重启时失败,而不是在您犯错的当下失败。