✅ How to Fix ISPConfig Manual SQL Backup Not Working (While Auto Backup Works)

Problem Overview

In ISPConfig 3.3 multi‑server setups, you may encounter the following issue:

  • ✅ Scheduled (Automatic) SQL backups work normally
  • ❌ Manual SQL backup does nothing
  • ❌ No error message appears
  • ❌ No backup folder is created
  • ✅ Some websites may work, others do not

This behavior can be confusing because everything seems healthy — but manual backups simply don’t trigger.

This article explains the root cause and provides the permanent fix.


Why This Happens

ISPConfig handles backups differently:

✅ Automatic Backup

Runs locally on the SQL server via cron and does not rely on datalog synchronization.

❌ Manual Backup

Uses this flow:

  1. The ISPConfig UI writes a task into sys_datalog (on the Master server)
  2. The SQL server reads the datalog entry
  3. server.sh processes the task
  4. Backup runs

If the datalog entry cannot be properly written or transmitted, the manual backup will silently fail.


The Real Cause: max_allowed_packet Too Small

In most cases, the issue is caused by a MariaDB configuration problem:

max_allowed_packet

On Debian 12/13 and many CentOS installs, the default is:

16M

In some cases, even:

1M

That is too small for ISPConfig 3.3 multi‑server environments.

Why?

Because sys_datalog may contain:

  • SSL certificate blobs
  • Large JSON configuration data
  • Backup metadata
  • Website configuration structures

If a datalog record exceeds max_allowed_packet, MariaDB silently rejects the transfer.

Result:

  • Small websites → manual backup works
  • Larger websites → manual backup does nothing
  • Auto backup still works

How to Check Your Current Value

Run on BOTH Master and SQL server:

mysql -u root -p -e "SHOW VARIABLES LIKE 'max_allowed_packet';"

If you see:

16777216  (16M)

or smaller → this is your problem.


✅ The Permanent Fix (Debian 13 / MariaDB 10.11+)

Step 1 — Edit MariaDB Configuration

On Debian 13, edit:

/etc/mysql/mariadb.conf.d/50-server.cnf

Find the [mysqld] section and set:

max_allowed_packet = 256M
wait_timeout = 300
interactive_timeout = 300

Save the file.


Step 2 — Restart MariaDB

systemctl restart mariadb

Verify:

mysql -u root -p -e "SHOW VARIABLES LIKE 'max_allowed_packet';"

You should now see:

268435456

Step 3 — Reset Stuck Datalog Entries

On the Master server:

mysql -u root -p dbispconfig

Run:

UPDATE sys_datalog SET processing = 0;

Then on the SQL server:

/usr/local/ispconfig/server/server.sh

After Fixing

Manual SQL backups should now:

  • ✅ Work for all websites
  • ✅ Create backup folders immediately
  • ✅ No longer silently fail

Why 256M Is Recommended

For ISPConfig multi‑server setups:

SettingRecommended Value
max_allowed_packet256M
wait_timeout300
interactive_timeout300

256M ensures large SSL or configuration payloads do not break datalog synchronization.


Important Notes

✅ You must update BOTH Master and SQL servers
✅ Restart MariaDB after changes
✅ Clear stuck datalog entries

If you only update one server, the problem may persist.


Final Thoughts

If you see:

  • Manual SQL backup does nothing
  • No error in UI
  • Auto backup works
  • Some sites succeed, others fail

The issue is almost certainly MariaDB’s max_allowed_packet.

Increasing it to 256M resolves the problem permanently in almost all ISPConfig 3.3 multi‑server environments.

Debian 13 + ISPConfig 3.3 + Slave Installation and config

1️⃣ Install Debian 13

最小安裝完成後:

apt update
apt upgrade -y

2️⃣ Enable Root Login

nano /etc/ssh/sshd_config

加入或修改:

PermitRootLogin yes

重新啟動 SSH:

systemctl restart ssh

3️⃣ Set Static IP

nano /etc/network/interfaces

加入:

auto ens18
iface ens18 inet static
    address 10.0.0.x
    netmask 255.255.255.0
    gateway 10.0.0.1
    dns-nameservers 8.8.8.8 8.8.4.4
    up ip route add 10.y.0.0/24 via 10.0.0.z
    down ip route del 10.y.0.0/24 via 10.0.0.z

確認無拼字錯誤。


4️⃣ Reboot

reboot

5️⃣ Set DNS

nano /etc/resolv.conf

內容:

nameserver 8.8.8.8
nameserver 8.8.4.4

6️⃣ Test Network

ping google.com

若可解析並 ping 通,表示 DNS 正常。


7️⃣ Edit Hosts

nano /etc/hosts

加入:

10.0.0.w    master.ispconfig.domain

(確認 IP 與實際 master 相符)


8️⃣ Install ISPConfig

cd /tmp
wget -O - https://get.ispconfig.org | sh -s -- --interactive

9️⃣ Installation Mode

Installation mode (standard,expert) [standard]: expert

🔟 Join Multiserver Setup

Shall this server join an existing ISPConfig multiserver setup (y,n) [n]: y

11️⃣ MySQL Master Information

MySQL master server hostname []:
master.ispconfig.domain
MySQL master server root password []:

⚠ 必須:

  • 知道 master 的 MySQL root password
  • 並且 master MySQL 必須允許 slave IP 連線

在 master 上確認:

GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.0.0.x' IDENTIFIED BY 'password' WITH GRANT OPTION;
FLUSH PRIVILEGES;

12️⃣ Keep MySQL Root Password

安裝過程中會顯示:

  • MySQL root password

請保存。


13️⃣ Remove Install Logs

rm /root/ispconfig-install-log/setup-*

避免密碼留在系統中。


14️⃣ Set Default PHP Version

update-alternatives --config php

選擇:

php 7.4

否則 slave cronjob 會無法與 master 正常連線。

確認:

php -v

✅ Installation Complete

登入:

https://slave.ispconfig.domain:8080

使用 master 帳號登入。


WordPress Installation

  1. ISPConfig → Sites → Add Website
  2. 建立 Database
  3. 上傳 WordPress
  4. 設定 wp-config.php
  5. 完成安裝

如果你需要,我可以:

  • 幫你排成「正式教學文件格式」
  • 或整理成「公司內部 SOP 文件版」
  • 或轉成 PDF 版教學稿

告訴我你要哪種格式。

Rocky Linux 10 + TPM 自動解鎖完整標準流程(從裸機開始)

🔹 第一階段:BIOS 設定

進入 BIOS:

✅ 開啟:

  • TPM 2.0
  • Secure Boot(建議開啟)
  • UEFI 模式(不要 Legacy)

儲存離開。


🔹 第二階段:安裝 Rocky Linux 10(含 LUKS2)

用 Rocky Linux 10 ISO 開機。

在安裝畫面:

1️⃣ 選擇語言 → Installation Destination

2️⃣ Storage Configuration

選:

Custom

Automatic (然後勾選 Encrypt my data)

✅ 必須勾選:

Encrypt my data

3️⃣ 設定 LUKS 密碼

系統會要求設定:

👉 這個密碼非常重要
👉 這就是日後主機壞掉時的救援密碼

請妥善保存。


4️⃣ 建議分割方式(標準企業配置)

/boot        (1GB, 不加密)
EFI          (600MB)
/            (加密)

✅ root 分割區必須加密


5️⃣ 完成安裝並重開機

此時開機會:

👉 要求輸入 LUKS 密碼
(這是正常的)


🔹 第三階段:啟用 TPM 自動解鎖


登入 root。


✅ Step 1:安裝套件

dnf install -y clevis clevis-luks clevis-dracut clevis-systemd tpm2-tools

✅ Step 2:確認 TPM 存在

ls /dev/tpm*

應看到:

/dev/tpm0
/dev/tpmrm0

✅ Step 3:確認 LUKS 位置

lsblk -f

找到:

rl-root

然後確認版本:

cryptsetup luksDump /dev/mapper/rl-root | grep Version

必須是:

Version: 2

✅ Step 4:將 TPM 綁定到 LUKS

執行:

clevis luks bind -d /dev/mapper/rl-root tpm2 '{}'

會要求輸入:

👉 您安裝時的 LUKS 密碼

成功會顯示:

Successfully bound

✅ Step 5:重建 initramfs(非常重要)

dracut -f

這一步會把 clevis 打包進開機映像。



🔹 第四階段:測試


重新開機:

reboot

如果成功:

✅ 不會要求輸入密碼
✅ 自動開機



🔹 第五階段:測試備援


進 BIOS:

👉 暫時關閉 TPM

開機:

✅ 應要求輸入密碼
✅ 輸入後可進入系統

再重新開啟 TPM。



🔹 如果主機壞掉


把硬碟接到另一台 Linux:

cryptsetup luksOpen /dev/sdX rescue

輸入原本密碼即可。

TPM slot 會失效,但密碼 slot 還在 ✅


🔹最終安全架構

LUKS2
 ├─ Slot 0 → 密碼(備援)
 ├─ Slot 1 → TPM2 自動解鎖

🔹 安全等級說明

情境結果
硬碟被偷無法解鎖 ✅
主機被偷可開機 ⚠
主機壞掉可用密碼救援 ✅

🔹 進階強化(企業級)

如果您是(金融相關)

建議下一步:

  • 加入 PCR 綁定
  • 限制 Secure Boot 狀態變更即鎖死
  • 設定 TPM policy
  • 設定自動 recovery SOP

🔹您現在的狀況總結

✔ 已成功安裝
✔ 已啟用 LUKS2
✔ 結構正確
✔ 可以直接做 TPM 綁定

Rocky Linux 安裝多版本 PHP 及切換 CLI PHP 教學

本文示範如何在 Rocky Linux 9 / 10 上:

  • ✅ 安裝多個 PHP 版本(7.4 / 8.1 / 8.2)
  • ✅ 同時並存
  • ✅ 切換 CLI PHP 預設版本
  • ✅ 啟用不同版本的 PHP-FPM

一、安裝 Remi Repository

Rocky Linux 預設只提供單一 PHP 版本,因此需要使用 Remi repo 來安裝多版本 PHP。

1️⃣ 安裝 Remi 與 EPEL

dnf install -y https://rpms.remirepo.net/enterprise/remi-release-$(rpm -E %rhel).rpm
dnf install -y epel-release
dnf update -y

2️⃣ 重設系統 PHP module

dnf module reset php -y

⚠️ 注意:不要使用 dnf module enable php:remi-xx
否則會變成單一 system PHP。


二、安裝多個 PHP 版本(並存模式)

我們使用 Remi 的 平行安裝版本(remi-safe)


✅ 安裝 PHP 7.4

dnf install -y php74 php74-php-cli php74-php-fpm php74-php-mysqlnd php74-php-gd php74-php-mbstring php74-php-xml php74-php-curl php74-php-zip

安裝完成後可用:

/usr/bin/php74

✅ 安裝 PHP 8.1

dnf install -y php81 php81-php-cli php81-php-fpm php81-php-mysqlnd php81-php-gd php81-php-mbstring php81-php-xml php81-php-curl php81-php-zip

✅ 安裝 PHP 8.2

dnf install -y php82 php82-php-cli php82-php-fpm php82-php-mysqlnd php82-php-gd php82-php-mbstring php82-php-xml php82-php-curl php82-php-zip

三、查看已安裝 PHP 版本

ls /usr/bin/php*

或:

alternatives --list | grep php

四、切換 CLI PHP 版本(推薦方法)

建議使用 alternatives 系統機制 切換 CLI PHP。


1️⃣ 註冊不同版本到 alternatives

如果尚未註冊:

alternatives --install /usr/bin/php php /usr/bin/php74 74
alternatives --install /usr/bin/php php /usr/bin/php81 81
alternatives --install /usr/bin/php php /usr/bin/php82 82

2️⃣ 選擇預設 CLI PHP

alternatives --config php

系統會顯示:

1   /usr/bin/php74
2   /usr/bin/php81
3   /usr/bin/php82

輸入對應號碼即可切換。


3️⃣ 確認切換成功

php -v

應顯示所選版本。


五、臨時使用指定版本(不切換系統)

如果只想執行某個版本,不需更改預設:

php74 -v
php81 -v
php82 -v

或執行腳本:

php81 script.php

六、啟用不同版本 PHP-FPM

多版本 FPM 可以同時存在。

例如啟動 PHP 7.4 FPM:

systemctl enable php74-php-fpm
systemctl start php74-php-fpm

查看狀態:

systemctl status php74-php-fpm

同樣方法可啟動 8.1 或 8.2。


七、檢查目前 CLI PHP 位置

which php
php -v

應顯示:

/usr/bin/php

八、總結

✅ 使用 Remi repo 安裝多版本 PHP
✅ 使用 remi-safe 平行安裝
✅ 使用 alternatives 切換 CLI
✅ FPM 可同時並存
✅ 不建議使用 module enable(除非只需要單一版本)

🛡 Mail Platform Disaster Recovery Drill Runbook

(Mailcow Multi‑Node Architecture)


1️⃣ 文件目的

本文件定義公司郵件平台(Mailcow 架構)的:

  • 災難復原流程
  • 定期演習標準
  • 目標恢復時間(RTO)
  • 操作步驟
  • 成功驗證標準

目標:

✅ 任何受訓工程師可在 45–60 分鐘內重建一台 mailcow 節點


2️⃣ 系統架構概覽

2.1 架構摘要

  • 4–5 台 Email Gateway(按 domain 分流)
  • 多台獨立 Mailcow Server
  • S3 Object Storage Snapshot 備份
  • Restic Snapshot
  • Rsync 冗餘備份
  • DB 同步機制

2.2 設計原則

  • Domain Sharding(每個 domain 固定歸屬)
  • 無 Shared Storage
  • 無跨節點強依賴
  • 可獨立重建

3️⃣ Recovery 目標定義

指標目標
RTO≤ 60 分鐘
RPO≤ 5 分鐘
MTTR每季統計

4️⃣ 演習類型分類


✅ Level 1:單節點完全損毀(每月)

模擬:

mailcow-3 完全不可用

目標:

  • 在新 VM 上重建 mailcow
  • 恢復資料
  • Gateway 切換
  • 完成收發測試

✅ Level 2:Gateway 故障(每季度)

模擬:

gateway-2 關閉

測試:

  • MX fallback
  • Queue retention
  • 是否有 mail drop

✅ Level 3:無預警 Chaos Drill(半年)

  • 隨機關閉一台 mailcow
  • 不通知工程師
  • 記錄反應時間

5️⃣ Level 1 演習完整流程(標準版)

以下為「單 mailcow 節點完全重建」標準操作流程。


✅ Step 1:宣告演習開始

  • 指定演習節點
  • 指定計時開始
  • 指定負責工程師
  • 指定觀察員

開始計時。


✅ Step 2:模擬節點失效

操作:

  • 關閉 VM
  • Block network

確認:

  • 無法 SSH
  • Gateway 出現 delivery defer

✅ Step 3:建立新 VM

標準規格:

  • CPU: ___
  • RAM: ___
  • Disk: ___
  • OS: Ubuntu ___

驗證:

ssh 正常

✅ Step 4:基礎環境準備

apt update
apt install docker docker-compose

Clone mailcow:

git clone https://github.com/mailcow/mailcow-dockerized

✅ Step 5:還原資料

5.1 還原 mail data volume

使用 restic:

restic restore latest --target /restore

或使用 S3 snapshot:

aws s3 sync ...

將:

/var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data

恢復。


5.2 還原資料庫

方式:

  • restore SQL dump
  • 啟用 DB replication

確認:

mysql -u root -p
show databases;

✅ Step 6:啟動 mailcow

docker-compose up -d

確認:

docker ps

所有 container 正常。


✅ Step 7:健康檢查

檢查:

  • Postfix
  • Dovecot
  • Rspamd
  • Nginx

測試:

✅ IMAP login
✅ SMTP auth
✅ Webmail
✅ 外部寄信


✅ Step 8:Gateway 切換

在 gateway:

  • 更新 routing IP
  • 修改 upstream

Reload postfix。


✅ Step 9:功能驗證

發送測試信:

  • 外部 → 內部
  • 內部 → Gmail
  • 內部 → 同 server

確認:

  • 無 queue 堆積
  • DKIM 正常
  • SPF pass
  • TLS 正常

✅ Step 10:記錄時間

記錄:

  • 開始時間
  • 完成時間
  • 卡住時間點
  • 改進建議

停止計時。


6️⃣ 演習後檢討會議

每次演習必須:

  • 討論瓶頸
  • 是否文件不清晰
  • 是否需要自動化
  • 是否某步驟過慢

7️⃣ 自動化改進方向

✅ 建立:

  • 一鍵 restore script
  • 基礎 VM provisioning script
  • 自動 DB restore
  • 自動 health check script

目標:

人只需要輸入 domain 名稱
系統自動完成 80% 重建


8️⃣ 成功標準

演習成功必須符合:

  • 60 分鐘內恢復
  • 無資料遺失
  • 收發正常
  • 用戶無感

9️⃣ 進階目標(未來)

  • Immutable mailcow node
  • Blue/Green 切換
  • 完全自動 rebuild
  • 15 分鐘 RTO

🔟 核心文化

可靠性 ≠ 有備份
可靠性 = 定期還原


✅ 最終目標

當新人加入公司:

  • 給他這份文件
  • 不協助
  • 他能 45–60 分鐘內完成

你嘅郵件平台就真正成熟。

一個中型 Mailcow Server 的備份反思與重建之路

前言

這篇文章不是教學文。

我不打算講操作步驟,也不會列出指令。

我想記錄的,是一段關於失敗、責任、重建,以及備份理念轉變的過程。

背景是一台中型規模的 Mailcow 郵件伺服器:

  • 約 400–800 個電子郵件帳戶
  • 約 1.5TB 郵件資料
  • 運行於 VPS 環境

這樣的規模,已經不能再用「簡單備份」去處理。


為什麼我對 Email 備份如此執著?

因為我曾經失敗過。

由於管理不善與判斷失誤,我曾經遺失客戶大約 70% 的郵件資料。

無論責任是否完全在我身上,我都不應該容許這件事發生。

那段時間,我每天承受客戶的質疑與投訴。
我失去了大部分客戶的信任。

那是我職業生涯中最大的污點。

從那一天開始,我對備份的理解徹底改變。

備份不再是技術流程。
而是一種責任。


第一個現實:傳統備份方式行不通

1.5TB 的郵件容量,在 VPS 環境下意味著:

  • 備份時間極長
  • I/O 壓力巨大
  • 官方 backup / restore 並不穩定
  • Restore 成本過高

更關鍵的是:

備份是否真的成功?

很多技術人員都經歷過這種情況——
真正需要備份的時候,才發現備份早已失敗。

而我使用的是自動化備份。
不是人手操作。

如果沒有驗證機制,「備份成功」只是心理安慰。


第二個問題:備份系統本身是否安全?

我曾經使用 NAS 作為備份伺服器。

但我開始問自己:

  • 公司自購硬體是否可靠?
  • RAID 是否等於安全?
  • NAS 本身會否成為單點故障?

如果主機與 NAS 同時出事,我還剩下什麼?

於是我決定把備份移往雲端。


Restic + S3:備份思維的第一次轉變

我開始研究 Restic,並將備份目標改為 S3 Object Storage。

Restic 的 Snapshot 機制令我非常欣賞:

  • 全程加密
  • 去重儲存
  • 支援 S3
  • 備份速度非常快

於是我產生一個重要想法:

我是否可以利用 Restic 這個機制,去建立一台可隨時重建的後備 Email Server?

這個想法的核心不是「不要後備伺服器」。

而是:

我是否可以不長期維持一台閒置後備機,而是在需要時,從雲端完整重建一台?

理論上流程是:

  1. 主伺服器使用 Restic 備份至 S3
  2. 發生災難
  3. 在新 VPS 上 Restore
  4. 重建 Mailcow 環境
  5. 重新上線

這是一種「可重建型備援」概念。

我為此寫了一整套 Backup / Restore Script,
一度覺得這是接近完美的方案。


第二次現實衝擊:Restore 的限制

Restic 備份非常快。

但 Restore 是完整還原。

這不是缺點,而是設計理念。

問題在於:

如果我希望後備機保持接近同步狀態,
就意味著頻繁 Restore。

大量、持續的完整還原,
會造成極高 SSD 寫入量。

長遠而言,硬碟壽命會急速消耗。

於是我明白:

Restic 非常適合作為 Disaster Recovery 工具
但不適合作為「高頻同步型備援」。


策略調整:分層備份思維

我保留 Restic + S3:

  • 每日一次完整備份
  • Restore 改為手動
  • 作為最終保險

即使主機、NAS、整個公司設備全部毀滅,
我仍然可以在雲端取回完整數據。

這給了我最基本的安全感。

但我仍然需要一種「高速同步型備援」。


回到基礎:rsync 的重新定位

我開始重新審視 rsync。

過去我避免使用它,因為:

  • Mailcow 結構複雜
  • 郵件為加密格式
  • 緩衝與暫存檔案混亂
  • 資料庫同步難度高

整體看起來幾乎無法處理。


AI 帶來的突破

這一次,我沒有單打獨鬥。

我開始利用 AI 協助我思考架構:

  • 如何處理憑證?
  • 如何安全同步 Maildir?
  • 如何排除不必要的緩衝檔?
  • 如何由主機觸發備份機自行恢復資料庫?
  • 如何確保資料一致性?

經過多次測試與調整,
整個流程終於穩定下來。

同步時間只需幾分鐘。
可高頻執行。
速度快。
可靠。

這才是真正符合我需求的方案。


最終架構:雙層設計

第一層:Restic + S3

  • 每日完整備份
  • 災難恢復
  • 可重建後備機

第二層:rsync 備援同步

  • 高頻同步
  • 快速切換
  • 低 SSD 壓力
  • 接近即時可用

這不是替代關係。

而是分層策略。


結語

我曾經失敗。

那段經歷讓我明白:

備份不是「技術是否正確」。
而是「當事情發生時,你是否對得起客戶」。

今天我最重要的改變,不是學會 Restic。
不是寫好 Script。
不是架好 S3。

而是我不再依賴運氣。
也不再依賴別人替我承擔責任。

備份,從來不是工具問題。

而是態度問題。

✅ Mailcow Restore 後郵件無法讀取

✅ 修復 Dovecot mail_crypt 加密金鑰步驟


📌 問題現象

在將 Mailcow 從 Server A 還原到 Server B 後出現:

  • SOGo 郵件列表空白
  • doveadm index 出現錯誤:
Decryption error: no private key available

📌 問題原因

Mailcow 預設啟用 Dovecot mail_crypt 功能。

郵件在寫入時會被加密(at-rest encryption)。

加密金鑰儲存在 Docker volume:

mailcowdockerized_crypt-vol-1

如果 restore 時沒有包含這個 volume:

郵件會存在
但無法解密
導致 SOGo 空白


✅ 解決方案總覽

Server A 的 crypt volume 複製到 Server B。


🟢 步驟一:在 Server A 打包 crypt volume

進入 mailcow 目錄:

bash

cd /opt/mailcow-dockerized

執行:

bash

docker run --rm \
  -v mailcowdockerized_crypt-vol-1:/data \
  -v $(pwd):/backup \
  busybox \
  tar czf /backup/crypt_backup.tar.gz -C /data .

確認檔案存在:

bash

ls -lh /opt/mailcow-dockerized/crypt_backup.tar.gz

🟢 步驟二:將備份檔傳送到 Server B

例如:

bash

scp /opt/mailcow-dockerized/crypt_backup.tar.gz root@ServerB:/root/

🟢 步驟三:在 Server B 停止 Dovecot

bash

cd /opt/mailcow-dockerized
docker compose stop dovecot-mailcow

🟢 步驟四:清空 Server B 原有 crypt volume

⚠️ 必須先清空,避免舊金鑰干擾

bash

docker run --rm \
  -v mailcowdockerized_crypt-vol-1:/data \
  busybox \
  rm -rf /data/*

🟢 步驟五:還原正確金鑰

bash

docker run --rm \
  -v mailcowdockerized_crypt-vol-1:/data \
  -v /root:/backup \
  busybox \
  tar xzf /backup/crypt_backup.tar.gz -C /data

🟢 步驟六:重新啟動服務

bash

docker compose start dovecot-mailcow
docker compose restart sogo-mailcow

🟢 (建議)重建 Index

為了避免使用者第一次登入很慢,建議執行:

bash

docker compose exec dovecot-mailcow doveadm index -A INBOX

✅ 驗證方式

確認不再出現:

Decryption error: no private key available

登入 SOGo 應可正常讀取郵件。


📌 重要備註

未來進行 Mailcow 備份時,必須包含以下 Docker volumes:

mailcowdockerized_vmail-vol-1
mailcowdockerized_vmail-index-vol-1
mailcowdockerized_crypt-vol-1   ✅ 必須包含

若缺少 crypt-vol-1

郵件將永久無法解密


✅ 技術補充

mail_crypt 使用:

  • 公鑰加密郵件
  • 私鑰解密郵件

若金鑰不匹配,即使 Maildir 完整:

郵件仍然無法讀取

✅ 結論

當 Mailcow restore 後出現:

Decryption error: no private key available

這不是郵件損壞問題,而是:

Dovecot mail_crypt 金鑰未同步

只需還原 crypt volume,即可完全修復。

使用 Mini PC 當 Proxmox 主機前,你一定要做的 BIOS / Kernel 設定

很多人為了省電、省空間,會選擇 Mini PC(像 Ryzen 7840HS、7940HS、Intel N100 之類)來跑:

  • Proxmox VE
  • ESXi
  • KVM
  • HomeLab
  • NAS + VM
  • 備份節點

👉 但如果你「沒有調整 BIOS 與 Kernel 參數」,
你可能會遇到:

  • ❌ 半夜自動重開機
  • ❌ 備份跑到一半整台 freeze
  • ❌ 完全沒有 kernel panic 記錄
  • ❌ journal log 直接中斷
  • ❌ 看起來像被拔電

而 log 裡 什麼都沒有

這不是錯覺。

這是 Mini PC 常見的 PCIe / C-state / Power Management 硬體層級 freeze 問題


為什麼會發生?

Mini PC 本質是「筆電級主機板」設計。

特點是:

  • Aggressive 省電策略
  • PCIe ASPM 深度省電
  • 深層 CPU C-State
  • IOMMU + PCIe 共享匯流排
  • 小型電源模組

當系統進入:

  • ✅ 高 I/O(vzdump 備份)
  • ✅ 高網路流量
  • ✅ NVMe 滿載
  • ✅ 多 VM 同時運作

就可能觸發:

PCIe Bus Hang
CPU 深層睡眠喚醒失敗
Root Complex Freeze

而這種 freeze:

  • 不會留下 kernel panic
  • 不會有 OOM
  • 不會有 MCE
  • watchdog 也來不及救

系統直接「硬死機」。


✅ Mini PC 當虛擬化主機的必要設定

請按照順序做。


🥇 Step 1:關閉 PCIe ASPM(最重要)

編輯:

/etc/default/grub

找到:

GRUB_CMDLINE_LINUX_DEFAULT="quiet"

改成:

GRUB_CMDLINE_LINUX_DEFAULT="quiet pcie_aspm=off"

然後執行:

update-grub
reboot

為什麼?

ASPM 是 PCIe 省電機制。

在桌機主機板通常沒問題。

但在 Mini PC 上:

  • 高流量 + 省電切換
  • 可能導致 PCIe Link 無法正確喚醒
  • 整條 bus freeze

這是最常見原因。


🥈 Step 2:限制 CPU C-State

如果 BIOS 有以下選項:

  • ✅ Disable Global C-State Control
  • ✅ Disable CPPC
  • ✅ Disable ASPM

請全部關閉。

如果 BIOS 沒有提供,

可以改用 kernel 參數:

processor.max_cstate=1 idle=nomwait

變成:

GRUB_CMDLINE_LINUX_DEFAULT="quiet pcie_aspm=off processor.max_cstate=1 idle=nomwait"

然後:

update-grub
reboot

為什麼?

Mini PC 為了省電會讓 CPU 進入:

  • C6
  • C10

但在高 I/O 中斷情況下:

👉 CPU 可能喚醒失敗
👉 整台 freeze


🥉 Step 3:測試性關閉 IOMMU(排除法)

如果還是會發生,

可以暫時加入:

amd_iommu=off

完整變成:

GRUB_CMDLINE_LINUX_DEFAULT="quiet pcie_aspm=off processor.max_cstate=1 idle=nomwait amd_iommu=off"

⚠️ 注意:

如果你有做 PCI Passthrough,不能關。


🔎 為什麼這些設定對 Mini PC 特別重要?

因為 Mini PC:

  • 使用筆電等級主板
  • PCIe 通道少
  • 多裝置共享 Root Port
  • 電源模組小
  • BIOS 通常未針對長時間高負載優化

當你把它當成:

24 小時虛擬化主機

你必須把它從「省電模式」改成「穩定模式」。


🔥 常見錯誤判斷

很多人會以為是:

  • ❌ igc 網卡 driver
  • ❌ NVMe 壞掉
  • ❌ RAM 問題
  • ❌ Proxmox bug

但如果 log 是「乾淨消失」,

那 90% 是 Power Management 問題。


✅ 建議 Mini PC 虛擬化標準設定

建議至少包含:

pcie_aspm=off
processor.max_cstate=1
idle=nomwait

✅ 結論

Mini PC 當 HomeLab / Proxmox 主機完全可行。

但前提是:

❗ 你要先把「筆電省電策略」關掉。

否則你會在:

  • 半夜備份
  • RAID rebuild
  • 大量 VM I/O

時遇到神秘重開機。

而 log 裡什麼都沒有。


如果你正在用 Mini PC 跑虛擬化,
建議今天就檢查你的設定。

穩定,比省 3W 電重要得多。

使用 iptables hashlimit 限制 HTTP 流量與安全回滾方法

在高流量或遭受爬蟲攻擊時,Web Server(如 Apache / Nginx)可能會因為大量連線而消耗過多 PHP-FPM 或系統資源。
Linux iptableshashlimit 模組可以有效限制「每個 IP 的連線速率」,是一種輕量且高效的防護方式。

本文說明:

  • hashlimit 的用途
  • 基本設定方法
  • 測試方式
  • 如何安全取消規則(回滾)

一、什麼是 hashlimit?

hashlimit 是 iptables 的一個 match module。

它的特色是:

✅ 針對「每個來源 IP」獨立計算流量
✅ 不會把所有訪客視為同一個來源
✅ 適合防止單一 IP 短時間內大量請求

-m limit 不同,limit 是全局限制,容易誤傷正常使用者。


二、基本 HTTP 限制範例

以下規則限制:

  • 每個 IP 每秒最多 30 個請求
  • 瞬間最多 80 個 burst
  • 超過後丟棄(DROP)

bash

iptables -A INPUT -p tcp --dport 80 \
-m hashlimit \
--hashlimit 30/sec \
--hashlimit-burst 80 \
--hashlimit-mode srcip \
--hashlimit-name http_limit \
-j ACCEPT

iptables -A INPUT -p tcp --dport 80 -j DROP

參數說明

參數說明
–hashlimit 30/sec每秒最多 30 個封包
–hashlimit-burst 80瞬間可達 80 個
–hashlimit-mode srcip以來源 IP 為單位計算
–hashlimit-name此限制規則的名稱

三、如何確認規則是否生效?

使用:

bash

iptables -L INPUT -n -v

可以看到每條規則的封包計數。

如需查看行號(刪除時會用到):

bash

iptables -L INPUT -n --line-numbers

四、如何安全取消規則?

✅ 方法一(推薦):使用行號刪除

先查看規則:

bash

iptables -L INPUT -n --line-numbers

假設顯示:

basic

num  target
1    ACCEPT  tcp dpt:80 hashlimit ...
2    DROP    tcp dpt:80

刪除方式:

bash

iptables -D INPUT 1
iptables -D INPUT 1

⚠ 注意:刪除第一條後,下面規則會往上移動。


✅ 方法二:使用完整規則刪除

bash

iptables -D INPUT -p tcp --dport 80 \
-m hashlimit \
--hashlimit 30/sec \
--hashlimit-burst 80 \
--hashlimit-mode srcip \
--hashlimit-name http_limit \
-j ACCEPT

iptables -D INPUT -p tcp --dport 80 -j DROP

⚠ 必須與建立規則時完全一致。


五、如果使用 iptables-persistent

若系統有安裝:

iptables-persistent

或使用:

netfilter-persistent

刪除規則後,請記得重新儲存:

bash

iptables-save > /etc/iptables/rules.v4

否則重開機後規則會恢復。


六、安全測試建議(避免誤封)

⚠ 在 Production 環境請勿直接啟用 DROP 規則。

建議測試流程:

1️⃣ 先只加入 hashlimit ACCEPT 規則
2️⃣ 觀察流量與錯誤日誌
3️⃣ 確認正常後再加入 DROP

或先於測試機驗證。


七、適用場景

  • WordPress 遭受爬蟲過度抓取
  • WooCommerce 頁面被大量請求
  • 想降低 PHP-FPM 壓力
  • 防止單一 IP 造成資源耗盡

八、注意事項

  • 若使用 Cloudflare,來源 IP 可能會變成 CDN IP
  • iptables 屬於 L3/L4 防火牆,無法判斷 URL
  • 規則順序非常重要

結語

hashlimit 是一種:

  • 高效
  • 核心層級
  • 不依賴應用程式

的流量控制方式。

但部署前務必測試,並確保有回滾方案,以避免影響正常使用者。

Linux 防火牆完整備份與還原教學(iptables + ipset)

在使用 Fail2Ban + ipset + iptables 的伺服器環境中,
很多人只備份了 iptables,卻忽略了 ipset

結果在災難還原時發現:

  • ✅ 規則還在
  • ❌ 被封鎖的 IP 全部消失

這篇教學會完整說明:

  1. 為什麼要同時備份 iptables 與 ipset
  2. 正確備份方式
  3. 正確還原順序
  4. 建議自動化備份腳本

🔎 一、iptables 與 ipset 差在哪?

在 Fail2Ban 環境中實際架構是:

Fail2Ban
   ↓
ipset(存放被封鎖 IP 清單)
   ↓
iptables(引用 ipset)
   ↓
Linux Kernel 防火牆

✅ iptables 負責「規則」

例如:

-m set --match-set f2b-http-404-scan src -j DROP

意思是:

如果來源 IP 在這個 set 裡 → 就 DROP


✅ ipset 負責「IP 名單」

例如:

add f2b-http-404-scan 65.20.67.134

真正被封鎖的 IP 存在 ipset 裡。


🚨 二、常見錯誤

很多人只做:

iptables-save > /root/iptables.backup

但這只會備份規則,不會備份 ipset 內的 IP。

結果:

  • 重開機後
  • 或手動 restore 後

IP 清單消失。


✅ 三、正確完整備份方式

必須備份兩個部分。


✅ 1️⃣ 備份 iptables

bash

iptables-save > /root/iptables.backup

✅ 2️⃣ 備份 ipset

bash

ipset save > /root/ipset.backup

這個檔案會包含:

sql_more

create f2b-http-404-scan hash:ip
add f2b-http-404-scan 65.20.67.134
add f2b-http-404-scan 1.2.3.4

這才是真正的封鎖名單。


✅ 四、正確還原方式(非常重要)

⚠️ 還原順序不能錯。


✅ Step 1:先還原 ipset

bash

ipset restore < /root/ipset.backup

✅ Step 2:再還原 iptables

bash

iptables-restore < /root/iptables.backup

❗ 為什麼順序不能反?

因為:

iptables 規則會引用 ipset。

如果 ipset 不存在,會出現錯誤:

iptables: Set f2b-http-404-scan doesn't exist

✅ 五、建議每日自動備份

可以建立一個腳本:

nano /root/backup-firewall.sh

內容如下:

bash

#!/bin/bash

DATE=$(date +%F)

iptables-save > /root/fw-$DATE.iptables
ipset save > /root/fw-$DATE.ipset

給執行權限:

bash

chmod +x /root/backup-firewall.sh

加入 crontab:

bash

crontab -e

每天凌晨 3 點備份:

basic

0 3 * * * /root/backup-firewall.sh

✅ 六、如果發生誤封全站怎麼辦?

假設錯誤規則導致:

0.0.0.0/0 tcp dpt:80 DROP

網站全部無法連線。

只要執行:

bash

ipset restore < /root/ipset.backup
iptables-restore < /root/iptables.backup

30 秒內救回網站。


✅ 七、關於 Fail2Ban 的補充

如果你使用:

banaction = iptables-ipset-proto6-allports

Fail2Ban 重啟後會自動重建 ipset。

因此:

✅ 一般重開機不會遺失封鎖
✅ 但手動清空 firewall 會遺失

所以備份仍然是好習慣。


✅ 八、總結

項目是否需要備份
iptables✅ 必須
ipset✅ 必須
只備份 iptables❌ 不完整

🎯 最重要一句話

iptables 只是規則
ipset 才是黑名單

兩個都要備份,才算完整防火牆備份。