自架服務 VPS Linux 備份 Restic systemd

備份這件事,rsync 已經不夠用了:Restic 用去重、加密、增量重做 VPS 備份

rsync 是同步工具,不是備份工具。Restic 把加密、去重、版本化、雲端整合收進同一支二進位檔,本文涵蓋 0.18 版的儲存格式設計、與 Borg 的取捨、systemd Timer 排程、保留策略、還原演練到監控整合,把 VPS 備份建到真正可還原的程度。

備份這件事,大多數人在第一次伺服器爆炸前都不會認真看待。打開 crontab、每天 rsync -avz /data backup-server:/backup/,覺得這樣就算備份了。

但這不是備份,這只是把資料複製一份。誤刪同步過去之後就回不來;勒索病毒加密原始檔的同時,也加密了 rsync 出來的副本;異地儲存的 SSH 金鑰一旦外洩,整批備份檔案立刻變成攻擊者的提款機。

正式的備份系統需要去重、加密、版本化、可還原驗證、自動排程。Restic 把這些事情收進同一支工具裡,2026 年的 0.18 版已經夠成熟到可以直接放上生產環境。

rsync 為什麼不適合當備份方案

rsync 是同步工具,不是備份工具。這兩件事的差別在於「歷史」。

一個健康的備份系統,必須能讓使用者回到任何一個過去的時間點還原資料。rsync 預設會把目標端覆寫成跟來源一致,意思是上週某個檔案被誤改、誤刪,下次 rsync 跑完,這個錯誤就被永久同步到備份端。--backup 旗標雖然能保留舊版本,但管理複雜度極高,沒有去重,磁碟空間爆炸只是時間問題。

加密也是個問題。rsync 透過 SSH 傳輸時是加密的,但備份端的檔案本身是明文。備份伺服器一旦被入侵,等於資料外洩。Restic 採用的是 client-side encryption,連備份儲存空間的擁有者都讀不到內容。

最後是去重。同一個檔案的不同版本之間,rsync 看到的是兩個獨立檔案;Restic 看到的是 4MB 一塊的 chunk,相同的 chunk 只存一次。一個 100GB 的目錄,每天備份跑滿一年,Restic 實際用掉的儲存空間可能只有 150GB。rsync 配合 hardlink 也能做到類似效果,但複雜度和邏輯漏洞都遠超合理範圍。

Restic 的儲存格式只有三種物件

Restic 的儲存格式只用 snapshot、tree、blob 三種物件。snapshot 是某個時間點的備份指標,tree 描述目錄結構,blob 是實際的檔案內容切片。每個 blob 用 SHA-256 雜湊命名,全 repository 範圍去重——同一段內容只存一次,無論它在多少個檔案、多少次 snapshot 裡出現。

寫入儲存空間之前,所有資料都會用 AES-256-CTR 加密、Poly1305 簽章。repository 密碼透過 scrypt 衍生出主金鑰,主金鑰再加密每個 blob 的內容金鑰。只要密碼夠強,備份檔案放在誰的雲端都不會構成資料外洩風險。

snapshot 本質上是不可變的。新的備份不會修改舊的 blob,只會新增新的 chunk 跟新的 snapshot 指標。除非主動執行 forgetprune,否則歷史紀錄不會消失。配上儲存端的物件鎖定(例如 S3 Object Lock 或 Backblaze B2 file retention),可以做到真正能抵擋勒索病毒的不可變備份。

Restic 跟 Borg 該選哪一個

兩個都是去重加密的開源備份工具。Borg 比較早出,效能略勝一籌,壓縮選項更豐富。Restic 比較晚出,但內建原生 S3、B2、Azure Blob、Google Cloud Storage 支援,不需要額外 wrapper。

對 2026 年新建立的備份流程,Restic 是更直接的選擇,理由有三個:

現代備份策略越來越多以物件儲存為目的地,Restic 跟物件儲存的整合是原生的。Borg 需要透過 borgbackup-S3 之類的 wrapper,多一層整合就多一層出錯機會。

跨平臺支援上,Restic 是純 Go 寫的,Linux、macOS、Windows、FreeBSD 都有原生二進位檔;Borg 在 Windows 上只能透過 WSL,對混合環境不友善。

最後是多客戶端共用 repository。Restic 設計上就支援多臺機器同時寫入同一個 repo,整個 fleet 之間的去重會自動進行。Borg 雖然有相關修補,但官方仍建議一臺機器一個 repo。

效能上 Borg 確實仍有優勢,但對絕大多數 VPS 工作負載來說,差距已經不足以構成決策因素。

備份目的地的選擇

備份的第一原則:不能跟原始資料放在同一臺機器上。VPS 硬碟壞掉的時候,本地備份跟原始檔案一起陪葬。

實務上推薦的目的地有三種:

物件儲存(S3、B2、R2、Wasabi):Restic 原生支援,價格便宜,可靠性高。Backblaze B2 跟 Cloudflare R2 沒有取回費用,比 AWS S3 標準等級實惠很多。跨境傳輸延遲不是問題的話,這是最省事的選擇。

另一臺 VPS 的 SFTP:兩臺機器分散在不同機房,互相備份。延遲低、頻寬通常充足,但需要自己管理那臺機器的可用性。

rclone 中繼:當儲存端是 WebDAV、OneDrive、Google Drive 之類 Restic 不直接支援的協定時,可以用 rclone 當橋接。設定稍微複雜,但選項變得非常多。

備份頻寬會吃對外流量。如果 VPS 採流量計費,需要評估每日增量大小跟資費的關係——Restic 的 chunk 去重會讓增量小很多,通常一個 50GB 的服務每天新增資料量在 100MB 以下。

從零建立第一個備份

Debian/Ubuntu 直接用套件管理器安裝:

1
2
apt install restic
restic self-update

self-update 會把套件管理器裝的舊版升級到最新二進位檔。Restic 的版本更新節奏快,套件庫的版本通常落後好幾個版本,影響效能跟相容性。

以 Backblaze B2 為例,初始化 repository:

1
2
3
4
5
6
export B2_ACCOUNT_ID=your_key_id
export B2_ACCOUNT_KEY=your_application_key
export RESTIC_REPOSITORY=b2:my-backup-bucket:/server01
export RESTIC_PASSWORD_FILE=/root/.restic-password

restic init

密碼放在 /root/.restic-password 並設定 chmod 600,避免出現在 shell 歷史紀錄裡。這個密碼一旦遺失,整個 repository 將永遠無法解密。建議用 1Password、Bitwarden 之類的工具離線備份這串密碼。

第一次備份:

1
2
3
restic backup /etc /home /var/lib/docker/volumes \
--exclude='/var/lib/docker/volumes/*/_data/*.log' \
--tag daily

--exclude 可以避開不必要的大檔(log、快取、臨時檔),減少 chunk 處理時間跟儲存用量。--tag 之後可以用 restic snapshots --tag daily 篩選。

用 systemd Timer 排程

cron 也能跑,但 systemd Timer 在錯過排程後可以補跑、有 journal 紀錄、能跟其他 unit 串接相依,幾乎所有現代 Linux 發行版都應該優先用 timer。

/etc/systemd/system/restic-backup.service

1
2
3
4
5
6
7
8
9
10
11
12
[Unit]
Description=Restic backup
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
EnvironmentFile=/etc/restic/env
ExecStart=/usr/bin/restic backup /etc /home /var/lib/docker/volumes --tag daily
ExecStartPost=/usr/bin/restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune
Nice=19
IOSchedulingClass=idle

/etc/systemd/system/restic-backup.timer

1
2
3
4
5
6
7
8
9
10
[Unit]
Description=Daily restic backup

[Timer]
OnCalendar=03:30
RandomizedDelaySec=30m
Persistent=true

[Install]
WantedBy=timers.target

Persistent=true 確保機器當機過、錯過排程後,下次啟動會立刻補跑。RandomizedDelaySec 避免 fleet 裡所有機器同時打物件儲存的 API rate limit。Nice=19IOSchedulingClass=idle 讓備份在系統閒置時才搶 CPU 跟磁碟。

啟用:

1
2
3
systemctl daemon-reload
systemctl enable --now restic-backup.timer
systemctl list-timers restic-backup.timer

保留策略不是越久越好

restic forgetrestic prune 是兩件事。forget 移除 snapshot 指標,prune 真的把不再被任何 snapshot 引用的 blob 刪掉。沒跑 prune,磁碟用量只會一路漲。

合理的保留策略應該根據 RPO(Recovery Point Objective)反推。常見的範本是 GFS(Grandfather-Father-Son):

1
2
3
4
--keep-daily 7       # 過去 7 天每天留一份
--keep-weekly 4 # 過去 4 週每週留一份
--keep-monthly 12 # 過去 12 個月每月留一份
--keep-yearly 3 # 過去 3 年每年留一份

這份範本能在約 25 個 snapshot 內覆蓋三年的還原能力。實際儲存用量取決於去重率,通常落在原始資料量的 1.2 到 2 倍之間。

prune 是高 IO 的操作,可能跑數小時。生產環境建議每週跑一次,搭配 --max-unused 10% 控制觸發門檻,平衡儲存成本跟 IO 影響。

沒測試過的備份等於沒備份

備份系統最常見的失敗模式不是備份跑不起來,而是備份跑了很多年,結果發現還原不出來。原因可能是密碼忘了、權限錯了、依賴的元資料沒一起備、還原指令從來沒人練習過。

每個季度應該做一次完整還原演練。挑一臺乾淨的 VPS 或本地 VM,用備份還原出一份完整資料,啟動服務,確認業務邏輯能跑。

Restic 提供兩個輕量的常態檢查:

1
2
restic check                          # 檢查 metadata 完整性
restic check --read-data-subset=5% # 隨機抽 5% data blob 驗證雜湊

check --read-data-subset=5% 每週跑一次,可以及早發現儲存端的位元損壞或加密金鑰錯誤。完整的 --read-data 會把整個 repo 重新下載校驗,每季跑一次足夠。

把備份狀態接上監控

備份失敗如果沒人發現,就跟沒備份一樣。最簡單的做法是接 Healthchecks.io 或自架版本:

1
2
3
4
5
6
[Service]
ExecStartPre=-/usr/bin/curl -fsS -m 10 --retry 5 \
https://healthchecks.example.com/ping/<uuid>/start
ExecStart=/usr/local/bin/restic backup ...
ExecStartPost=/usr/bin/curl -fsS -m 10 --retry 5 \
https://healthchecks.example.com/ping/<uuid>

只要備份失敗或漏跑,Healthchecks 就會發 email、Telegram 或 webhook 通知。重點是這個監控本身要部署在備份目標所在地以外的機器,避免單點故障同時殺掉備份跟告警。

Restic 0.18 開始強化 --json 輸出,方便接 Prometheus 或自家儀表板:

1
restic stats --mode raw-data --json | jq '.total_size'

把這個數字推上 Grafana 之後,repository 大小的異常變化(突然暴漲、停止增長)會被看見。

把備份當成基礎設施的一部分

備份不是「設定一次就忘了」的東西,而是一條需要持續驗證的流程。Restic 把加密、去重、增量、雲端整合這幾件本來要拼湊好幾個工具才能做完的事,收進一支二進位檔,搭配 systemd Timer 跟物件儲存,能在一個下午內把整套流程建立起來。

剩下的就是堅持每季做還原演練、確保監控還活著、密碼還記得。資料的命運不該交給天意。

NCSE Network 的 VPS 主機座落於臺灣是方電訊機房,採用 Intel Gold CPU 與 NVMe SSD,提供穩定的 I/O 表現,是 Restic 備份來源端的理想選擇。如需規劃跨機房備援架構,歡迎前往 ncse.tw 了解 VPS 與 IP Transit 服務。

需要穩定的雲端主機?

NCSE Network 提供企業級 VPS,7 天免費試用,臺灣是方電訊機房,99% SLA 保證。

查看 VPS 方案 →