Debian unattended-upgrades 為何沒有作用?
Debian 預設未啟用 unattended-upgrades。正確設定 Origins-Pattern,檢查 apt-daily timers,並確認它確實能自行套用更新。
新安裝的 Debian 上,為什麼 unattended-upgrades 沒有作用
在 Debian 上,即使已安裝 unattended-upgrades,也可能一次更新都不會執行,因為安裝套件與啟用套件是兩個不同步驟。套件會在設定自身之前提出 debconf 問題,而 Debian installer 會將該問題的答案儲存為 false。Ubuntu 對同一個問題的回答則相反,因此相同的套件在 Ubuntu 上看似正常,在 Debian 上卻像是故障。
系統不會明確指出這個問題。開機時不會顯示錯誤,登入時不會顯示警告,也沒有可供查看的 log,因為負責寫入該 log 的程式碼從未被呼叫。啟用它只需執行一個命令。本指南其餘內容會說明啟用後仍可能阻止更新的 4 個因素:可從哪些 repositories 執行升級、systemd timers 實際何時觸發、如何得知執行失敗,以及是否允許機器自行重新開機。
變更任何設定前,先檢視系統目前的判斷
dpkg -l unattended-upgrades | tail -n 1
sudo apt install -y debconf-utils
sudo debconf-show unattended-upgrades
cat /etc/apt/apt.conf.d/20auto-upgrades
apt-config dump APT::Periodicdebconf-show 會輸出 unattended-upgrades/enable_auto_updates 中儲存的值。該行開頭的 * 表示這個值是由某個設定所指定,而不是採用套件預設值。在 Debian installer 建立的機器上,這個設定來源就是安裝程式。
cat 可能會輸出 cat: /etc/apt/apt.conf.d/20auto-upgrades: No such file or directory。單憑這點就能解釋為何沒有任何動作:沒有該檔案,就沒有週期性金鑰,因此永遠不會排程任何工作。
apt-config dump 才是關鍵指令。APT 會依檔名順序讀取 /etc/apt/apt.conf.d/ 中的檔案並合併設定,因此在 99local 中設定的一般值,會覆寫 20auto-upgrades 中相同的值。只有沒有副檔名或使用 .conf 副檔名的檔案才會生效,因此像 99local.bak 這樣的檔案會被略過。讀取單一檔案只能得知該檔案的內容。apt-config dump 則會顯示 APT 實際採用的設定。
有兩個金鑰決定是否會執行任何動作:
APT::Periodic::Update-Package-Lists會更新套件清單,這正是apt update手動執行的工作。APT::Periodic::Unattended-Upgrade會實際執行升級。
它們的值不是 true 或 false,而是以天數表示的間隔。"1" 表示「如果過去 1 天內尚未執行,就執行這項工作」;"7" 表示每週執行;"0" 表示永不執行。APT::Periodic::Unattended-Upgrade "0"; 是有效設定,但會永遠不執行任何工作,而且不會顯示錯誤。如果輸出中該金鑰的值是 0,或完全沒有這個金鑰,原因就找到了。
啟用方式:使用 dpkg-reconfigure,或自行寫入金鑰
sudo apt update
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades這個問題的優先級很低,因此一般安裝時不會顯示。dpkg-reconfigure會顯示低優先級的問題,不論你設定的 debconf 優先級為何,因此 --priority=low 只是明確指定它原本就會執行的行為。應避免使用的旗標是 --default-priority,它會關閉這項功能,讓問題再次隱藏。請在對話框中回答 yes。接著,套件的 postinst 會根據你的回答寫入 /etc/apt/apt.conf.d/20auto-upgrades。
透過 script 建置的機器沒有可供回答的對話框,因此請先設定答案:
echo 'unattended-upgrades unattended-upgrades/enable_auto_updates boolean true' \
| sudo debconf-set-selections
sudo dpkg-reconfigure -f noninteractive unattended-upgrades
apt-config dump APT::Periodic你也可以直接寫入這兩個金鑰:
printf 'APT::Periodic::Update-Package-Lists "1";\nAPT::Periodic::Unattended-Upgrade "1";\n' \
| sudo tee /etc/apt/apt.conf.d/20auto-upgrades
apt-config dump APT::Periodic這會立即生效,但也會留下隱患。debconf 中的答案仍然是舊值,因此下一次執行 dpkg-reconfigure 或重新安裝套件時,套件會根據 debconf 重新寫入檔案,並在完全沒有輸出的情況下撤銷你的修改。請同時設定這兩者,或設定 debconf,讓 postinst 負責管理檔案。
這是它與另一個系列真正的差異。另一個系列的安裝程式會替你啟用相同的套件,因此 Ubuntu unattended-upgrades 設定會從一台已經自行套用修補程式的機器開始,將時間用於調整設定。以下內容適用於兩者。
Debian 實際上會替你安裝哪些更新?
啟用 timer 不等於同意安裝所有更新。每個套件來源都包含 release metadata:origin、label、suite、codename 及 site。unattended-upgrades 會查看每個可升級套件的候選版本,讀取該版本來源的 metadata,只有在來源符合 Unattended-Upgrade::Origins-Pattern 中的項目,或符合較舊的 Unattended-Upgrade::Allowed-Origins 清單時,才會安裝。沒有符合項目就不會升級,這是預設設計。
先列出你的比對模式,再列出用來比對的 metadata:
apt-config dump Unattended-Upgrade::Origins-Pattern
grep -H -E '^(Origin|Label|Suite|Codename|Components):' /var/lib/apt/lists/*Release
apt-cache policygrep -H 會在每行保留檔名,因此你能看出每一段 metadata 屬於哪個來源。這些欄位的值就是比對模式右側的內容。模式中的 ${distro_codename} 會在執行時替換成目前所使用版本的 codename,因此同一個檔案可跨越版本升級繼續使用。
請將自己的模式與旁邊的 metadata 一起閱讀,因為「我只會取得安全性修正,還是也會取得 point release 更新」的答案,只寫在這兩份清單中:
- 指定
label=Debian-Security的模式會符合 security archive。Debian 的安全性公告會發布到這裡。 - 指定
-updatessuite 的模式會符合 stable-updates。這裡包含 Debian 在 point release 之間發布的內容,例如時區資料。 - 指定未加其他標記的 release suite,會在 point release 變更發布時一併套用,這會增加變更頻率,也需要你進行更多測試。
- 你自行加入的 repository 在寫入對應模式前完全不會符合任何項目。
最後一點常讓人意外。第三方 repository 有自己的 origin 和 label,因此 unattended-upgrades 看到候選版本後,會發現其來源不符合任何項目,接著略過。是否為它加入模式值得審慎決定,因為 vendor repository 可能在同一個 suite 中發布新的 major version。這表示你已同意讓該軟體在深夜自動進行 major upgrade。
unattended-upgrades 也不會將系統移轉到其他 Debian 版本。它只會在目前使用的版本內升級套件。從一個 stable release 升級到下一個版本,仍然需要由你自行安排並手動執行。
修改清單時,請記住 APT 會附加清單內容。另一個檔案中的第二個 Origins-Pattern 區塊會加入原本隨套件提供的清單,而不是取代它。如果你的目的是取代原有內容,請先清除原設定:
#clear Unattended-Upgrade::Origins-Pattern;
Unattended-Upgrade::Origins-Pattern {
"origin=Debian,codename=${distro_codename},label=Debian-Security";
"origin=Debian,codename=${distro_codename}-security,label=Debian-Security";
};請將本機修改放在新的檔案中,並讓該檔案排序在隨套件提供的檔案之後,例如 /etc/apt/apt.conf.d/52unattended-upgrades-local。50unattended-upgrades 是由套件管理的設定檔,因此編輯後,日後套件升級若提供了不同版本,程序會暫停並詢問如何處理你的版本。使用獨立檔案則不會發生衝突。
趁現在再列出兩個值得檢查的設定鍵。Unattended-Upgrade::Allowed-Origins 是相同概念的舊格式,以 origin:archive 配對表示,目前仍會讀取。因此,從教學文章複製設定時,常會同時存在兩份清單,卻無法明確判斷實際符合的是哪一份。Unattended-Upgrade::Package-Blacklist 儲存套用於套件名稱的正規表示式;其中過於寬鬆的表示式可能會封鎖遠超過預期的套件。以下 dry run 會列出實際套用的內容。
為什麼沒有在預期時間執行?
這項工作由兩個 systemd timers 驅動,且兩者的職責不同。apt-daily.timer 會啟動 apt-daily.service,負責更新套件清單並下載套件。apt-daily-upgrade.timer 會啟動 apt-daily-upgrade.service,而這個單元才會呼叫 unattended-upgrade。兩者都會以不同參數執行 /usr/lib/apt/apt.systemd.daily。如果停用或 mask 第二個 timer,兩個週期性工作都可能讀取 1,但始終不會安裝任何內容。
systemctl list-timers 'apt-daily*' --all
systemctl is-enabled apt-daily.timer apt-daily-upgrade.timer
systemctl cat apt-daily-upgrade.timerlist-timers 會針對每個單元提供 NEXT、LEFT、LAST 和 PASSED。沒有 NEXT 的 timer 不會執行。若 is-enabled 顯示 masked,表示有人強制停用了該單元;你在 apt.conf.d 中撰寫的任何內容都不會改變這個狀態。
現在查看 systemctl cat 顯示的 [Timer] 區段。OnCalendar 是 timer 最早可能觸發的時間。RandomizedDelaySec 會在該時間之後加入隨機等待,避免一批 Debian 電腦在同一秒連線到相同的 mirror。因此,NEXT 欄顯示的時間可能與 OnCalendar 不一致,昨天的執行時間也可能落在不同分鐘。這是預期行為。Persistent=true 表示電腦在排定時間關機時,會在下次開機後不久執行工作,而不是略過當天。
若要調整時間範圍,請覆寫單元,不要直接編輯它:
sudo systemctl edit apt-daily-upgrade.timer[Timer]
OnCalendar=
OnCalendar=03:30
RandomizedDelaySec=30m空白的 OnCalendar= 列是必要的,因為值為清單的單元設定會累加:省略這一列會保留套件提供的排程,並再增加一個排程。systemctl edit 會替你重新載入 systemd,因此請使用 systemctl list-timers 'apt-daily*' 確認結果,並查看新的 NEXT。
你不必等待 timer 觸發即可測試這些設定:
sudo systemctl start apt-daily-upgrade.service
journalctl -u apt-daily-upgrade.service --since -1h有些人查看 /etc/cron.daily 時會對一點感到困惑:APT 仍會提供 /etc/cron.daily/apt-compat,供未使用 systemd 的系統使用。請使用 cat 讀取它。在使用 systemd 的系統上,它會提早結束,因此不會重複執行工作。
驗證是否運作:unattended-upgrade --dry-run --debug
sudo unattended-upgrade --dry-run --debug命令使用單數形式,而套件名稱使用複數形式。Debian 12 和 13 也提供 /usr/bin/unattended-upgrades,因此其中的複數形式不再以 command not found 結尾;但文件記載的命令,以及本指南全程使用的命令,都是單數形式的 unattended-upgrade。
這個命令幾乎可以回答所有「為什麼略過該套件」的問題,因為它會輸出自身的判斷依據。在輸出的前段,它會列出依照模式計算出的來源:
Allowed origins are: ...接著,每個候選套件各列出一行,其中包含它將安裝之版本的來源記錄:
Checking: curl ([<Origin component:'main' archive:'stable' origin:'Debian' label:'Debian' site:'deb.debian.org' isTrusted:True>])最後列出它會處理的套件;如果主機沒有任何待處理項目,則會顯示:
No packages found that can be upgraded unattended and no pending auto-removals將前兩段資訊放在一起,便能直接判斷原因。找出你預期會升級之套件的 Checking: 行。逐欄比較其來源記錄與上方列出的允許來源。只要有一個欄位不相符,最常見的是 label 或 archive,就是該套件遭略過的完整原因。
--dry-run 只會在記憶體中標記套件,不會安裝任何內容,因此可以隨時重複執行。它仍會附加內容至 /var/log/unattended-upgrades/unattended-upgrades.log。
如果來源相符,但套件仍遭保留,請依序檢查以下項目:
apt-mark showhold會列出你或其他工具釘選的套件。unattended-upgrades 不會處理遭保留的套件。- 升級可能需要移除或新增其他套件。除非相關索引鍵允許,否則 unattended-upgrades 會避免這類變更;請與
sudo apt-get -s upgrade比較,該命令會在不套用這些安全規則的情況下,顯示相同的判斷結果。 - dpkg 可能在中斷的執行後處於半設定狀態。使用
sudo dpkg --configure -a修正後,再次檢查。 /var沒有可用空間,因此無法下載或解包任何內容。使用df -h /var檢查。/boot塞滿舊核心,導致下一次核心升級失敗。使用df -h /boot檢查,並輸出apt-config dump Unattended-Upgrade::Remove-Unused-Kernel-Packages,確認是否已啟用清理功能。
如果在計時器執行期間手動執行 apt,就會看到 Could not get lock /var/lib/dpkg/lock-frontend。這則訊息表示 unattended-upgrades 正在正常運作。請等待它完成。
如何得知執行失敗的時間?
先查看日誌,因為不論是否設定其他項目,日誌都會存在:
sudo tail -n 40 /var/log/unattended-upgrades/unattended-upgrades.log
sudo tail -n 40 /var/log/unattended-upgrades/unattended-upgrades-dpkg.log
journalctl -u apt-daily-upgrade.service --since -7d第一個檔案是決策日誌,記錄檢查了哪些項目、選擇了哪些項目,以及安裝了哪些項目。第二個檔案包含原始的 dpkg 輸出;如果套件的 postinst 指令碼執行失敗,通常可在這裡看到。若升級是在機器關機時執行,還會出現獨立的關機日誌。
郵件是最常見的報告管道:
Unattended-Upgrade::Mail "you@example.com";
Unattended-Upgrade::MailReport "on-change";MailReport 接受 always、on-change 和 only-on-error。only-on-error 聽起來是較嚴謹的選擇,但對無人登入的伺服器而言通常不適合,因為機器完全停止升級後也不會傳送錯誤。如此一來,正常運作的主機和已停止運作的主機都同樣保持沉默。on-change 則會在每次安裝項目時傳送郵件,因此郵件也能證明 timer 仍在運作。
只有機器能傳送郵件時,郵件才能離開機器。unattended-upgrades 會將訊息交給本機郵件系統,因此需要 postfix 等 MTA(mail transfer agent),或 msmtp 等相容於 sendmail 的 relay client。使用 command -v sendmail 和 command -v mail 進行檢查。如果兩者都不存在,報告就無法送達;升級仍會成功,而整個失敗狀況也不會被發現。此外,請注意新的 VPS IP 位址沒有傳送信譽,因此直接寄往公開信箱的郵件經常會被歸入垃圾郵件。透過你已在使用的郵件供應商進行 relay,比自行執行郵件伺服器更可靠。
如果不想執行郵件服務,可以監看現有監控系統所記錄的日誌時間戳記:
stat -c '%y %n' /var/log/unattended-upgrades/unattended-upgrades.log如果時間戳記在一週內沒有更新,表示 timer 已停止,不論設定檔中寫了什麼。這項檢查應與 例行 Linux 伺服器維護檢查 一起執行。
伺服器應該自行重新開機嗎?
Unattended-Upgrade::Automatic-Reboot "false";
Unattended-Upgrade::Automatic-Reboot-WithUsers "true";
Unattended-Upgrade::Automatic-Reboot-Time "02:00";將 Automatic-Reboot 設為 true 後,unattended-upgrades 會不經確認直接重新開機,但前提是執行完成後檔案 /var/run/reboot-required 存在。在 Debian 12 和 13 中,unattended-upgrades 套件會透過核心 hook /etc/kernel/postinst.d/unattended-upgrades 自行建立這個標記檔;該 hook 會在安裝核心套件時執行。函式庫升級不會觸發它。不要假設該檔案一定存在。下次核心升級後請檢查:
ls -l /var/run/reboot-required /var/run/reboot-required.pkgs
uname -r
dpkg -l 'linux-image-*' | grep '^ii'如果該檔案在你的機器上從未出現,Automatic-Reboot "true" 就永遠不會觸發。你可能會在幾個月內一直以為自己已因核心更新而重新開機,但實際上仍在執行舊核心。對最新安裝的 linux-image 套件執行 uname -r,即可確認結果。
Automatic-Reboot-Time 會將重新開機排程到指定時間,而不是立即執行。將 Automatic-Reboot-WithUsers 設為 false 後,只要有人登入就會略過重新開機;對一直有工作階段保持登入的伺服器而言,這代表伺服器永遠不會重新開機。
這項決策關係到重新啟動後的狀態,而不是重新開機本身。手動啟動的服務不會自動恢復。開機時需要輸入密碼的加密磁碟區不會掛載。彼此相依的兩台機器可能以錯誤順序恢復。對於可接受在 02:00 中斷一分鐘的無狀態 Web 伺服器,可以啟用自動重新開機。需要人工處理的環境則應維持停用,改為監控標記檔,讓人員自行決定時機。介於兩者之間的是 needrestart;它會重新啟動仍使用已升級函式庫的服務,因此能處理核心以外的所有情況。其預設模式會在重新啟動前詢問,因此在無人值守的執行環境中依賴它之前,請先閱讀 /etc/needrestart/needrestart.conf。
Debian testing 與 unstable 的運作方式也相同嗎?
以上內容說明的是 Debian stable。安全修正會從具有獨立標籤的個別套件庫發布。這種架構才能讓「僅安裝安全更新」成為可設定的選項。其他套件組的建置方式不同,因此從 stable 伺服器複製的設定檔,實際效果通常不如原作者預期。在持續變動的套件組上啟用自動升級,也代表可能在無人介入的情況下進行主要版本變更。這是另一項需要明確同意的設定。如果你正在評估這個選擇,在伺服器上執行 Debian stable、testing 或 unstable 說明了各套件組的特性。在 Red Hat 系列中,相同工作使用不同工具,也採用不同術語;Rocky Linux 與 AlmaLinux 上的 dnf-automatic 則透過自己的 timer 與設定檔執行。
自動升級可縮短修正發布與安裝之間的時間差。但它不會告訴你哪些項目仍然暴露在風險中,因此應搭配檢查伺服器上已知的 CVE。CVE 是 common vulnerabilities and exposures 的縮寫,也是追蹤修正所使用的公開識別碼。
五分鐘檢查
apt-config dump APT::Periodic會列印兩個非零值的金鑰。systemctl list-timers 'apt-daily*'會列印兩個計時器的NEXT時間。sudo unattended-upgrade --dry-run --debug會列印允許的來源,其中包含適用於您代號的安全性套件庫。sudo systemctl start apt-daily-upgrade.service會完成,且/var/log/unattended-upgrades/unattended-upgrades.log的時間戳記會更新。- 一週後,同一份日誌會列出它安裝的套件。
前四項通過,表示機器已完成設定。第五項通過,表示設定確實運作。
FAQ
為什麼 Debian 會安裝 unattended-upgrades,卻將其停用?
此套件會提出 debconf 問題 unattended-upgrades/enable_auto_updates,並根據答案寫入 /etc/apt/apt.conf.d/20auto-upgrades。Debian 安裝程式會為該問題儲存 false,因此套件若是某項工作的一部分或相依套件,安裝後便會設定為不執行任何動作。執行 sudo debconf-show unattended-upgrades 可查看儲存的答案,再以 sudo dpkg-reconfigure --priority=low unattended-upgrades 加以變更。此問題的優先級較低,一般安裝不會顯示;但 dpkg-reconfigure 預設會顯示低優先級問題,因此即使未指定該旗標,這裡仍會出現。
如何在不等待 timer 的情況下測試 unattended-upgrades?
執行 sudo unattended-upgrade --dry-run --debug。它會列出可接受的來源;每個可升級套件各輸出一行 Checking:,並附上該套件的來源記錄,也會列出預計安裝的套件,但不會實際安裝任何內容。若要測試實際執行流程,請執行 sudo systemctl start apt-daily-upgrade.service,然後一併查看 journalctl -u apt-daily-upgrade.service --since -1h 與 /var/log/unattended-upgrades/unattended-upgrades.log。
unattended-upgrades 會同時安裝一般更新與安全性修正嗎?
只有在符合某個模式時才會。當套件來源符合 Unattended-Upgrade::Origins-Pattern 或舊版 Unattended-Upgrade::Allowed-Origins 中的項目時,才會安裝套件升級;security archive、stable-updates suite,以及自行新增的任何 repository,都是獨立的項目。在自己的機器上輸出 apt-config dump Unattended-Upgrade::Origins-Pattern 與 apt-config dump Unattended-Upgrade::Allowed-Origins,再與 grep -H -E '^(Origin|Label|Suite|Codename):' /var/lib/apt/lists/*Release 比較。它也不會將系統升級到其他 Debian 版本。
為什麼升級不會在 timer 設定的時間執行?
apt-daily-upgrade.timer 會在 OnCalendar 的基礎上設定 RandomizedDelaySec,因此 systemd 會在該時間範圍內隨機選擇時間,而不是在行事曆指定的時間執行。這能將負載分散到所有指向相同 mirror 的 Debian 機器。systemctl list-timers 'apt-daily*' 會顯示實際選定的時間。若要移動該時間範圍,請執行 sudo systemctl edit apt-daily-upgrade.timer,並提供一行空白的 OnCalendar=,接著輸入自訂值。
我應該為安全性更新啟用自動重新開機嗎?
只有在非預期重新開機不會造成問題的環境中才應啟用。每次執行後,只要存在 /var/run/reboot-required,Unattended-Upgrade::Automatic-Reboot "true" 就會在不要求確認的情況下重新開機。在 Debian 12 和 13 中,該標記檔案由 unattended-upgrades 套件提供的 kernel hook 建立,因此會在 kernel 升級後出現,而不會在 library 升級後出現。請先在 kernel 升級後確認該檔案會出現在自己的機器上,再信任此設定。若機器開機時需要輸入 passphrase,或執行有人員手動啟動的服務,請保持 false,並針對標記檔案設定警示,讓人員決定重新開機的時機。