自架郵件伺服器使用 Postfix 時,郵件可能因為收件方伺服器暫時不可用、網路問題、DNS 解析失敗、被判定為垃圾郵件等原因而滯留在佇列(Queue)中。這些卡住的信件會持續重試發送,不僅浪費系統資源,還可能影響正常郵件的投遞效率。本文逐步說明如何查看、診斷與清理 Postfix 佇列。
目錄
一、查看佇列狀態:postqueue
Postfix 提供 postqueue 指令來查看目前佇列中的信件狀態。
查看佇列摘要:
postqueue -p
輸出範例:
-Queue ID- --Size-- ----Arrival Time---- -Sender/Recipient-------
3xJ4X2Z1 5000 Tue Aug 25 10:30:01 [email protected]
[email protected]
8kL9mP3q 3200 Tue Aug 25 09:15:33 [email protected]
[email protected]
– 2 Kbytes in 2 Requests.
欄位說明:
| 欄位 | 說明 |
|---|---|
| Queue ID | 信件唯一識別碼,用於後續操作(postcat、postsuper) |
| Size | 信件大小(位元組) |
| Arrival Time | 信件進入佇列的時間 |
| Sender/Recipient | 寄件者與收件者地址 |
只查看佇列數量:
postqueue -p | tail -1
輸出如 -- 2 Kbytes in 5 Requests.,表示佇列中有 5 封信件。
強制嘗試重新投遞:
如果問題已排除(例如 DNS 恢復、收件方伺服器恢復),可以強制 Postfix 立即嘗試投遞佇列中的信件:
postqueue -f
二、查看信件內容:postcat
當信件卡在佇列中,需要了解信件內容和投遞失敗的原因時,可以使用 postcat 查看完整信件。
查看單一信件:
postcat -q 3xJ4X2Z1
輸出包含:
- 信件標頭(Headers):寄件者、收件者、主旨、日期等
- 信件內容(Body):實際郵件內容
- Postfix 內部資訊:進入佇列時間、嘗試投遞次數、上次嘗試時間、失敗原因等
查看信件的失敗原因:
postcat 輸出的最後一段通常會包含 reason 或 diagnostic-code 欄位,說明投遞失敗的原因,例如:
diagnostic-code: host remote.com[203.0.113.50] said: 450 4.1.2
<[email protected]>: Recipient address rejected: User unknown
常見失敗原因:
| 錯誤碼 | 說明 | 處理方式 |
|---|---|---|
| 450 | 暫時性失敗(收件方暫時不可用) | 可等待重試,通常會自動恢復 |
| 550 | 永久性失敗(收件者不存在) | 應刪除該信件,無法投遞 |
| 451 | 本地錯誤(資源不足) | 檢查磁碟空間、記憶體 |
| 452 | 容量不足 | 檢查信件大小限制設定 |
| Connection timed out | 連線逾時 | 檢查網路、防火牆、DNS |
| 421 4.7.0 | Gmail 判定信件內容或連結可疑,已封鎖該信件 | 檢查信件內容是否含可疑連結,參考 Google 說明,調整內容後重新發送 |
三、刪除佇列信件:postsuper
確認信件無法投遞或為垃圾信件後,可以使用 postsuper 刪除。
刪除單一信件:
postsuper -d 3xJ4X2Z1
刪除所有信件(謹慎使用):
postsuper -d ALL
⚠️ 此指令會刪除佇列中所有信件,包括正在重試的正常信件,請確認後再執行。
只刪除已超過保留期限的信件:
Postfix 預設保留信件 5 天(maximal_queue_lifetime = 5d),超過後會自動退信。如果想手動清除:
# 刪除超過保留期限的信件
postsuper -d ALL delayed
四、大量刪除多組 Queue ID
實務上常遇到需要刪除多封特定信件,但 postsuper -d 一次只能刪除一個 Queue ID。以下提供幾種大量刪除的方式:
方法一:逐一刪除(適用少量)
postsuper -d 3xJ4X2Z1
postsuper -d 8kL9mP3q
postsuper -d 2mN7kR5x
方法二:用 for 迴圈批次刪除
for id in 3xJ4X2Z1 8kL9mP3q 2mN7kR5x; do
postsuper -d $id
done
方法三:從檔案讀取 Queue ID 大量刪除
將要刪除的 Queue ID 寫入檔案,每行一個:
# 建立 ID 清單檔案
cat > /tmp/queue_ids.txt << 'EOF'
3xJ4X2Z1
8kL9mP3q
2mN7kR5x
5pQ9wL4t
EOF
逐行讀取並刪除
while read id; do
postsuper -d “$id”
done < /tmp/queue_ids.txt
方法四:依條件篩選後大量刪除
例如刪除所有寄給特定網域的卡住信件:
#!/bin/bash
# delete_queue_by_domain.sh
# 用法: ./delete_queue_by_domain.sh example.com
TARGET_DOMAIN=“$1”
if [ -z “$TARGET_DOMAIN” ]; then
echo “用法: $0 <收件網域>”
echo “範例: $0 spam-domain.com”
exit 1
fi
echo “正在搜尋寄往 $TARGET_DOMAIN 的信件…”
取得符合條件的 Queue ID
IDS=$(postqueue -p | grep “$TARGET_DOMAIN” | awk ‘{print $1}’ | sed ‘s/*//g’)
if [ -z “$IDS” ]; then
echo “沒有找到寄往 $TARGET_DOMAIN 的信件”
exit 0
fi
COUNT=$(echo “$IDS” | wc -l)
echo “找到 $COUNT 封信件,開始刪除…”
for id in $IDS; do
postsuper -d “$id” 2>/dev/null
echo “已刪除: $id”
done
echo “完成!共刪除 $COUNT 封信件。”
方法五:互動式大量刪除腳本
以下腳本提供互動式操作,可先預覽要刪除的信件再確認執行:
#!/bin/bash
# bulk_queue_manager.sh
# Postfix 佇列大量管理工具
echo “===== Postfix 佇列管理工具 =====”
echo “”
echo “1. 查看佇列摘要”
echo “2. 依網域篩選並刪除”
echo “3. 依寄件者篩選並刪除”
echo “4. 刪除所有佇列信件”
echo “5. 匯出 Queue ID 清單”
echo “”
read -p "請選擇操作 (1-5): " choice
case $choice in
1)
echo “”
postqueue -p
;;
2)
read -p "輸入收件網域 (如 spam.com): " domain
IDS=$(postqueue -p | grep “$domain” | awk ‘{print $1}’ | sed ‘s/*//g’)
if [ -z “$IDS” ]; then
echo “沒有找到寄往 $domain 的信件”
exit 0
fi
echo “”
echo “找到以下信件:”
postqueue -p | grep “$domain”
echo “”
read -p "確認刪除?(y/N): " confirm
if [ “$confirm” = “y” ] || [ “$confirm” = “Y” ]; then
for id in $IDS; do
postsuper -d “$id” 2>/dev/null
done
echo “刪除完成!”
else
echo “已取消”
fi
;;
3)
read -p "輸入寄件者 Email: " sender
IDS=$(postqueue -p | grep “$sender” | awk ‘{print $1}’ | sed ‘s/*//g’)
if [ -z “$IDS” ]; then
echo “沒有找到來自 $sender 的信件”
exit 0
fi
echo “”
echo “找到以下信件:”
postqueue -p | grep “$sender”
echo “”
read -p “確認刪除?(y/N): " confirm
if [ “$confirm” = “y” ] || [ “$confirm” = “Y” ]; then
for id in $IDS; do
postsuper -d “$id” 2>/dev/null
done
echo “刪除完成!”
else
echo “已取消”
fi
;;
4)
echo “⚠️ 此操作將刪除所有佇列信件!”
postqueue -p
echo “”
read -p “確認刪除所有信件?(yes/N): " confirm
if [ “$confirm” = “yes” ]; then
postsuper -d ALL
echo “所有信件已刪除!”
else
echo “已取消”
fi
;;
5)
OUTPUT=”/tmp/postfix_queue_ids_$(date +%Y%m%d_%H%M%S).txt”
postqueue -p | grep -v “^-” | grep -v “^$” | awk ‘{print $1}’ | sed ‘s/*//g’ > “$OUTPUT”
echo “已匯出至: $OUTPUT”
echo “共 $(wc -l < “$OUTPUT”) 封信件”
;;
*)
echo “無效選項”
;;
esac
使用方式:
chmod +x bulk_queue_manager.sh
sudo ./bulk_queue_manager.sh
五、預防信件卡住的設定建議
清理佇列只是治標,調整 Postfix 設定才能治本:
縮短重試次數與保留時間:
在 /etc/postfix/main.cf 中設定:
# 卡住信件最多保留 2 天(預設 5 天)
maximal_queue_lifetime = 2d
bounce_queue_lifetime = 1d
重試間隔(逐漸拉長)
minimal_backoff_time = 300s
maximal_backoff_time = 4000s
限制信件大小:
# 限制單封信件最大 20MB
message_size_limit = 20480000
設定退信通知:
# 超過保留期限後自動退信給寄件者
bounce_notice_recipient = postmaster
delay_notice_recipient = postmaster
修改設定後重新載入:
postfix reload
六、日常維運建議
- 定期檢查佇列:每天或每週執行
postqueue -p檢查是否有異常滯留信件 - 設定監控告警:當佇列超過一定數量(如 100 封)時發送告警通知
- 記錄清理日誌:每次清理佇列時記錄刪除了哪些信件,方便日後追蹤
- 分析卡住原因:透過
postcat查看失敗原因,如果是 DNS 或網路問題需優先修復基礎設施
結語
Postfix 佇列管理是自架郵件伺服器的日常維運工作之一。掌握 postqueue、postcat、postsuper 三個核心指令,搭配本文提供的腳本工具,就能有效率地處理卡住的信件。更重要的是分析卡住的根因,調整 Postfix 設定或修復基礎設施,避免同樣的問題反覆發生。