Ubuntu 清理舊核心,釋放 /boot 空間
`/boot` 被舊 linux-image 填滿時,apt 會停止設定套件。查看可安全移除的核心,保留目前開機使用的版本,並修復套件安裝。
apt 為何會在 /boot 被舊核心填滿後停止運作
在 Ubuntu 上,每次更新 kernel 都會在 /boot 寫入一組新的檔案,並保留先前的檔案。因此,小型的 /boot 分割區會被填滿,apt 也無法完成安裝。修復分成兩個步驟。先確認系統上的哪些套件是 kernel,以及目前開機使用的是哪個 kernel,再使用 apt autoremove --purge 移除其餘套件。
操作順序很重要。正在執行的 kernel 是絕對不可移除的套件,而且系統可能已經處於 apt 完全無法執行的狀態。請先進行診斷。
實際的失敗情況
每個 kernel 版本都會在 /boot 安裝 2 個大型檔案:壓縮後的 kernel(vmlinuz-<version>)以及 initramfs(initial RAM filesystem,initrd.img-<version>;這是 kernel 掛載實際 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,較舊的映像檔則可能使用另一個名稱。能識別這個問題的 2 行是 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 sources 遷移後產生重複項目。
確認 /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 的大小,下次更新就會失敗。
找出目前執行中的 kernel
uname -r
cat /var/run/reboot-required.pkgsuname -r 會顯示目前載入記憶體中的 kernel release string。將該字串複製到其他位置。這是唯一不可移除的版本。
第二個檔案只有在某個套件要求重新開機時才會存在。其中的 linux-image 行表示磁碟上已安裝較新的 kernel,但尚未使用,因為該版本安裝完成後機器尚未重新開機。如果可以,請先重新開機,再進行清理。apt 會保留目前執行中的 kernel 及最新版本,因此在舊 kernel 上執行清理時,會比必要情況多保留一個版本。
列出 kernel 套件並查看其狀態
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,代表某個特定的 kernel。名稱不含版本的套件,例如 linux-image-generic、linux-headers-generic 或 linux-generic,則是 meta package。它不包含 kernel,唯一用途是相依於最新的版本化 kernel,讓 apt upgrade 能拉取新的 kernel。移除 meta package 後,系統將不再接收 kernel 更新,而且之後不會顯示任何警告。
各套件系列的用途如下。linux-image-* 在 /boot 中保存壓縮的 kernel。linux-modules-* 和 linux-modules-extra-* 在 /lib/modules 下保存驅動程式。linux-headers-* 在 /usr/src 下保存建置標頭檔,因此清除 headers 只會釋放 root filesystem 的空間,不會釋放 /boot 空間。如果問題是 /boot 分割區已滿,應尋找的是 image packages。
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 這類 meta package,表示某個項目將其標記為自動安裝;移除後會中斷 kernel 更新。移除清單中若出現 uname -r 的字串,表示目前執行中的 kernel 未受到保護。這不應發生,必須先調查原因再繼續。
如果清單內容正確,請正式執行清理。
sudo apt autoremove --purge
df -h /boot--purge 的操作也會刪除殘留的設定,而不只是移除 package。額外釋出的空間不多,但可避免 dpkg --list 持續累積 rc 行,讓下一次稽核更容易閱讀。
接著確認 boot menu 已重新建立。移除 kernel package 時會自動執行 update-grub,因此 menu 應只參照仍然存在的檔案。
sudo grep -o 'vmlinuz-[^ ]*' /boot/grub/grub.cfg | sort -u
ls -1 /boot/vmlinuz-*第一個輸出中的每個版本都必須出現在第二個輸出中。若 menu entry 指向已不存在的檔案,正常運作的伺服器就可能在 GRUB 提示字元處停止啟動。這是導致 kernel 更新後無法啟動的 VPS 的其中一種原因;相較於事後從 rescue console 修復,事先避免這個問題容易得多。
為什麼 apt autoremove 有時不會移除任何項目
apt autoremove 只會移除標記為自動安裝的套件,也就是作為其他套件相依套件而安裝的套件。您使用 apt install linux-image-6.8.0-40-generic 自行安裝的 kernel 會標記為手動安裝;無論版本多舊,autoremove 都不會處理它。
apt-mark showmanual | grep -E '^linux-'輸出中的任何特定版本 kernel 都不會被 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請保留標記為手動安裝的 meta packages。它們本來就應該是手動安裝,因為這些套件是您明確要求安裝的。
刻意移除指定的 kernel
有時需要立即移除特定版本,而不是等到清理政策允許時再處理。指定 image package,讓 apt 自行判斷其餘項目。
sudo apt purge linux-image-6.8.0-40-genericapt 會在執行任何動作前列出移除清單,因為 linux-modules-extra-* 依賴 image package,必須在同一筆 transaction 中一併移除。這份清單是實際的安全檢查,也能讓你發現是否有 meta package 與原本要移除的版本一起被帶走。若清單中出現任何非預期項目,請回答 n。接著執行 sudo apt autoremove --purge,找出目前已失去存在必要的 module 與 header package。
為何絕對不能移除正在執行的 kernel
已載入記憶體的 kernel 會在其檔案遭刪除後繼續執行,因此一開始看不出任何問題。真正失效的是 kernel 尚未載入的功能。清除 linux-modules-$(uname -r) 會刪除 /lib/modules/$(uname -r)/,因此下一次載入 module 時會失敗:
modprobe: FATAL: Module nf_tables not found in directory /lib/modules/6.8.0-64-generic從這時起,重新載入 firewall 會失敗;嘗試掛載此 kernel 自開機後尚未處理過的 filesystem type 也會失敗。同時,/boot/vmlinuz-$(uname -r) 已不存在,因此 boot menu 不再提供目前正在執行的 kernel,下一次重新開機時會進入其他 kernel。這台機器仍持續處理 network traffic,但實際上已無法開機。每次移除前,都要核對 uname -r 是否列在移除清單中。
When /boot is too full for apt to run at all
This is the state that sends people looking for this page. apt autoremove needs dpkg to finish configuring the half-configured kernel package first, and that step rebuilds an initramfs, which needs space in a /boot that has none. Break the loop by hand, once.
uname -r
ls -1 /boot/initrd.img-*Pick one initrd whose version is not the string uname -r gave you, and delete that single file.
sudo rm /boot/initrd.img-6.8.0-40-generic
sudo apt --fix-broken install
sudo apt autoremove --purge
sudo update-grubEach line has a reason. The rm is a deliberate exception that leaves dpkg believing a file exists when it does not. apt --fix-broken install completes the configuration that failed, now that there is room for the initramfs. autoremove --purge then removes the package whose file you deleted along with the other old versions, which puts dpkg back in step with the disk. update-grub rebuilds the menu from the files that actually exist. Do not reboot between the rm and the update-grub, because in that window the menu can still point at the file you just deleted. If dpkg complains that it is interrupted, sudo dpkg --configure -a does the same repair as apt --fix-broken install.
dnf 系統上的相同作業
如果 VPS 執行 Fedora 或 Rocky Linux 等 RHEL 重建版,處理機制則相反。Debian 和 Ubuntu 透過 apt 的自動移除規則保護 kernel,清理由您手動處理,或由 unattended-upgrades 觸發;而 dnf 會強制套用名為 installonly_limit 的數量上限,當安裝新 kernel 會超過上限時,立即自動移除最舊的 kernel。使用 grep installonly_limit /etc/dnf/dnf.conf 和 man 5 dnf.conf 讀取目前套用的值,並使用 sudo dnf remove --oldinstallonly 清除現有的待處理項目。執行中的 kernel 同樣會受到保護。如需了解兩個套件管理器之間更完整的對應關係,請參閱 dnf 與 apt 指令對照。
再次避免發生
依賴人工記得執行的清理工作最終一定會失效,因此應將它設定在安裝 kernel 的元件中。開啟 /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 的自動安全性更新。
下一個 kernel 安裝前,還有一個數值需要檢查;這就是本指南開頭使用的同一組命令:
df -h /boot
ls -lh /boot/initrd.img-$(uname -r)如果 Avail 沒有明顯大於該檔案,下一個 kernel 就會完全按照上述說明失敗。因此請現在修正,不要等到升級期間才處理。這項檢查只需 1 分鐘,應與其他 VPS 磁碟健康檢查 一併執行。版本升級前尤其需要注意,因為 將 Ubuntu 24.04 升級至 26.04 會在程序早期安裝新的 kernel,而 do-release-upgrade 在 /boot 空間不足時會拒絕繼續。
FAQ
為什麼 Ubuntu 會保留舊版 kernel,而不刪除它們?
因為如果某個 kernel 無法開機,就沒有其他版本可供選擇。保留前一個版本後,更新失敗時可以從 GRUB 選單復原,不必依賴供應商的 rescue console。apt 因此會避免自動移除一組 kernel 套件,且一定會保留目前正在使用的版本。執行 apt-config dump | grep -i neverautoremove 查看該版本發行版實際保護的 pattern,因為不同版本的政策可能不同。
在 production server 上執行 apt autoremove --purge 安全嗎?
可以,但必須先閱讀 dry run 結果。執行 sudo apt autoremove --purge --dry-run;此命令不會寫入任何內容,請檢查輸出的清單。如果清單包含 linux-generic 或 linux-image-generic 之類的 meta package,請停止操作,因為移除這類套件會使後續 kernel 更新停止。如果清單包含 uname -r 輸出的版本字串,也請停止操作。若兩者皆未出現,清單中的就是舊版 kernel 和孤立的相依套件。
apt autoremove 沒有移除任何內容,但 /boot 仍然已滿。接下來怎麼辦?
舊版 kernel 幾乎確定被標記為 manual,而 autoremove 只會處理標記為 automatic 的套件。執行 apt-mark showmanual | grep -E '^linux-'。其中列出的任何含版本號 kernel,都表示它曾在某個時間點由手動方式安裝。使用 sudo apt-mark auto linux-image-<version> 將它標記為 automatic,然後再次執行 dry run;或者使用 sudo apt purge linux-image-<version> 直接 purge 該版本。
可以手動刪除 /boot 中的檔案嗎?
只有在 /boot 已滿到 apt 無法設定損壞的 kernel 套件時,才應有意地執行一次這種操作。刪除單一個 initrd.img-<version> 檔案,且其版本不得是 uname -r 的輸出;接著立即執行 sudo apt --fix-broken install、sudo apt autoremove --purge 和 sudo update-grub。刪除檔案後未執行這些後續步驟,會導致 dpkg 記錄檔案已不存在的套件,也會讓 GRUB 選單保留指向遺失檔案的項目。如此一來,機器會在下一次重新開機時失敗,而不是在發生錯誤的當下失敗。