Debian 使用 Restic 建立每日增量備份:從安裝、systemd Timer 到還原驗證

分類:Linux


在 Linux NAS 上,如果已經有一份本地資料,希望每天建立具有歷史版本、去重、壓縮與加密功能的增量備份,Restic 是一個相當適合的工具。

本文以 Debian 為例,從零開始建立:

/data
  │
  │ Restic Backup
  ▼
/backup/restic

最後完成:

每日自動備份
    ↓
保留 7 個 daily
保留 4 個 weekly
保留 12 個 monthly
    ↓
每月 repository check + prune
    ↓
實際 restore + checksum 驗證

排程使用 systemd timer,而不是 cron。

Restic 是什麼?

Restic 是一套支援 snapshot、deduplication、compression 與 encryption 的備份工具。

假設 /data 有:

/data
├── Documents
├── Photos
└── Videos

第一次執行:

restic backup /data

會建立第一個 snapshot。

隔天再次執行:

restic backup /data

如果大部分檔案都沒有改變,Restic 不會重新儲存所有相同資料。

每個 snapshot 在使用者看來仍然是一份完整備份,但 repository 底層會重複利用已經存在的資料。

因此:

Snapshot Day 1
Snapshot Day 2
Snapshot Day 3

並不代表磁碟空間變成:

資料量 × 3

實際增加的空間主要來自新增或修改過的資料。

環境

本文使用以下目錄:

Source:
/data

Repository:
/backup/restic

Password File:
/root/.config/restic/passwd

備份策略:

Daily    7
Weekly   4
Monthly 12

排程:

每天 03:00
    ↓
Backup + Forget

每月 1 日 04:00
    ↓
Check + Prune

安裝 Restic

Debian:

apt update
apt install -y restic

確認版本:

restic version

建立 Restic 密碼檔

Restic repository 預設會加密。

先建立設定目錄:

mkdir -p /root/.config/restic
chmod 700 /root/.config/restic

建立密碼檔:

vim /root/.config/restic/passwd

裡面只放一行密碼,例如:

your-very-strong-password

設定權限:

chmod 600 /root/.config/restic/passwd

這個密碼非常重要。

Restic repository 即使完整存在,如果遺失密碼,也無法解密還原資料。

因此密碼應該另外保存一份,而且不要只存在被備份的同一台機器。

建立 Repository

假設備份 repository 放在:

/backup/restic

先建立目錄:

mkdir -p /backup/restic

初始化:

restic -r /backup/restic \
  --password-file /root/.config/restic/passwd \
  init

完成後可以測試:

restic -r /backup/restic \
  --password-file /root/.config/restic/passwd \
  snapshots

如果 repository 剛建立,目前應該還沒有任何 snapshot。

第一次完整備份

來源資料:

/data

執行:

time restic -r /backup/restic \
  --password-file /root/.config/restic/passwd \
  backup /data

第一次沒有 parent snapshot,因此 Restic 必須讀取全部檔案:

no parent snapshot found, will read all files

完成後會看到類似:

Files:       xxxx new
Dirs:         xxx new

Added to the repository: xxx GiB

processed xxxx files, xxx GiB
snapshot xxxxxxxx saved

第一次通常是最耗時間的一次,因為 Restic 必須進行:

讀取檔案
   ↓
Chunking
   ↓
Hash
   ↓
Compression
   ↓
Encryption
   ↓
寫入 Repository

測試增量備份

第一次完成後,如果資料沒有變更,立刻再執行完全相同的 command:

time restic -r /backup/restic \
  --password-file /root/.config/restic/passwd \
  backup /data

這次應該會看到:

using parent snapshot xxxxxxxx

Files:
    0 new
    0 changed
    xxxx unmodified

Added to the repository: 0 B

snapshot xxxxxxxx saved

這裡可以看到 Restic 增量備份的重要特性。

即使每次都建立一個新的完整 snapshot:

Day 1
Day 2
Day 3

沒有改變的資料並不會每次重新儲存一份。

設定 Snapshot Retention

如果每天備份一次,不可能永遠保留所有 snapshot。

本文使用:

Daily    7
Weekly   4
Monthly 12

也就是:

最近一週   → 每天一份
最近一個月 → 每週一份
過去一年   → 每月一份

Restic 使用 forget 管理 snapshot retention:

restic -r /backup/restic \
  --password-file /root/.config/restic/passwd \
  forget \
  --keep-daily 7 \
  --keep-weekly 4 \
  --keep-monthly 12

正式執行前,也可以先使用 --dry-run:

restic -r /backup/restic \
  --password-file /root/.config/restic/passwd \
  forget \
  --keep-daily 7 \
  --keep-weekly 4 \
  --keep-monthly 12 \
  --dry-run

先確認 Restic 打算保留與移除哪些 snapshots。

建立每日 Backup Script

建立:

mkdir -p /root/bin
vim /root/bin/restic-backup.sh

完整內容:

#!/bin/bash

set -euo pipefail

SOURCE="/data"
RESTIC_REPOSITORY="/backup/restic"
RESTIC_PASSWORD_FILE="/root/.config/restic/passwd"

echo "=== Backup started: $(date) ==="

echo "=== Starting Restic backup ==="

restic \
    -r "$RESTIC_REPOSITORY" \
    --password-file "$RESTIC_PASSWORD_FILE" \
    backup "$SOURCE"

echo "=== Restic backup completed ==="

echo "=== Applying Restic retention policy ==="

restic \
    -r "$RESTIC_REPOSITORY" \
    --password-file "$RESTIC_PASSWORD_FILE" \
    forget \
    --keep-daily 7 \
    --keep-weekly 4 \
    --keep-monthly 12

echo "=== Restic retention completed ==="

echo "=== Backup finished: $(date) ==="

設定權限:

chmod 700 /root/bin/restic-backup.sh

其中:

set -euo pipefail

很重要。

如果 restic backup 發生錯誤,script 就會停止,不應該假裝後面的流程仍然正常完成。

先手動測試:

/root/bin/restic-backup.sh

確認沒有問題後,再交給 systemd。

建立 systemd Service

建立:

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

內容:

[Unit]
Description=Daily Restic Backup
RequiresMountsFor=/data /backup

[Service]
Type=oneshot
ExecStart=/root/bin/restic-backup.sh

Type=oneshot 表示 service 執行指定工作,完成後就結束。

因此備份完成後看到:

Active: inactive (dead)

是正常現象。

真正應該確認的是:

status=0/SUCCESS

建立每日 systemd Timer

建立:

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

內容:

[Unit]
Description=Daily Restic Backup Timer

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
Unit=restic-backup.service

[Install]
WantedBy=timers.target

代表每天:

03:00

執行備份。

其中:

Persistent=true

很適合 NAS。

例如:

03:00  原本應該備份
       ↓
NAS 當時關機
       ↓
08:00 NAS 開機

timer 重新啟動後,可以補觸發停機期間錯過的 calendar 排程。

啟用 Backup Timer

重新載入 systemd:

systemctl daemon-reload

可以先檢查 unit:

systemd-analyze verify \
  /etc/systemd/system/restic-backup.service \
  /etc/systemd/system/restic-backup.timer

沒有問題後:

systemctl enable --now restic-backup.timer

查看 timer:

systemctl list-timers restic-backup.timer

例如:

NEXT                        LEFT    UNIT
Tue 2026-09-29 03:00:00     6h      restic-backup.timer

手動測試 systemd Backup

不需要等到凌晨 3 點,可以直接:

systemctl start restic-backup.service

查看:

systemctl status restic-backup.service

成功應該可以看到:

code=exited, status=0/SUCCESS

以及:

Finished restic-backup.service

查看完整 log:

journalctl -u restic-backup.service

查看最近 100 行:

journalctl -u restic-backup.service -n 100 --no-pager

即時追蹤:

journalctl -fu restic-backup.service

這也是 systemd timer 相較單純 cron 很方便的地方。

Forget 和 Prune 的差異

Restic 有兩個容易混淆的動作:

forget
prune

forget:

Snapshot
   ↓
依 retention policy
   ↓
移除不需要保留的 snapshot

prune:

Repository
   ↓
尋找已經沒有 snapshot 引用的資料
   ↓
整理並實際回收 repository 空間

因此不一定需要每天:

forget --prune

本文採用的方式是:

每天
restic backup
    ↓
restic forget

每月
restic check
    ↓
restic prune

把每日備份與較重的 repository maintenance 分開。

建立每月 Repository Maintenance

建立:

vim /etc/systemd/system/restic-maintenance.service

內容:

[Unit]
Description=Restic Repository Maintenance
RequiresMountsFor=/backup

[Service]
Type=oneshot
ExecStart=/usr/bin/restic -r /backup/restic --password-file /root/.config/restic/passwd check
ExecStart=/usr/bin/restic -r /backup/restic --password-file /root/.config/restic/passwd prune

執行順序:

restic check
      ↓
成功
      ↓
restic prune

如果第一個 command 失敗,service 會失敗,不應繼續執行後面的 prune。

建立 Maintenance Timer

建立:

vim /etc/systemd/system/restic-maintenance.timer

內容:

[Unit]
Description=Monthly Restic Repository Maintenance Timer

[Timer]
OnCalendar=*-*-01 04:00:00
Persistent=true
Unit=restic-maintenance.service

[Install]
WantedBy=timers.target

代表:

每月 1 日 04:00

執行 repository maintenance。

重新載入:

systemctl daemon-reload

檢查:

systemd-analyze verify \
  /etc/systemd/system/restic-maintenance.service \
  /etc/systemd/system/restic-maintenance.timer

啟用:

systemctl enable --now restic-maintenance.timer

現在可以一次查看兩個 timer:

systemctl list-timers \
  restic-backup.timer \
  restic-maintenance.timer

整體排程變成:

每天 03:00
restic-backup.timer
       ↓
restic-backup.service
       ↓
restic backup
       ↓
restic forget


每月 1 日 04:00
restic-maintenance.timer
       ↓
restic-maintenance.service
       ↓
restic check
       ↓
restic prune

驗證備份

備份 command 成功並不代表整個備份流程已經驗證完成。

真正重要的是:

能不能 Restore?

因此整套設定完成後,至少應該做一次實際還原測試。

查看 Snapshot

先查看目前有哪些 snapshot:

restic -r /backup/restic \
  --password-file /root/.config/restic/passwd \
  snapshots

查看最新 snapshot 裡的內容:

restic -r /backup/restic \
  --password-file /root/.config/restic/passwd \
  ls latest

假設選一個測試檔案:

/data/test.png

Restore 單一檔案

建立測試目錄:

mkdir -p /restore-test

從最新 snapshot 還原:

restic -r /backup/restic \
  --password-file /root/.config/restic/passwd \
  restore latest \
  --target /restore-test \
  --include '/data/test.png'

Restic 會保留 snapshot 中的路徑,因此還原後可能是:

/restore-test/data/test.png

使用 SHA-256 驗證

原始檔:

sha256sum /data/test.png

還原檔:

sha256sum /restore-test/data/test.png

例如:

abcdef123456...  /data/test.png
abcdef123456...  /restore-test/data/test.png

兩個 SHA-256 完全一致,就表示還原後的檔案內容與原始資料一致。

也可以直接使用 cmp:

cmp \
  /data/test.png \
  /restore-test/data/test.png \
  && echo "RESTORE VERIFIED: files are identical"

成功會顯示:

RESTORE VERIFIED: files are identical

測試完成後刪除測試目錄:

rm -rf /restore-test

最終架構

完成後整套系統如下:

             /data
               │
               │ restic backup
               ▼
         /backup/restic
               │
               ├── Daily   × 7
               ├── Weekly  × 4
               └── Monthly × 12


每天 03:00
restic-backup.timer
       │
       ▼
restic-backup.service
       │
       ├── restic backup
       └── restic forget


每月 1 日 04:00
restic-maintenance.timer
       │
       ▼
restic-maintenance.service
       │
       ├── restic check
       └── restic prune

日常查看排程:

systemctl list-timers \
  restic-backup.timer \
  restic-maintenance.timer

查看最近備份結果:

systemctl status restic-backup.service

查看備份 log:

journalctl -u restic-backup.service -n 100 --no-pager

查看 snapshots:

restic -r /backup/restic \
  --password-file /root/.config/restic/passwd \
  snapshots

備註

Restic 的優點不只是「增量備份」。

它把幾個 NAS 備份很需要的功能整合在一起:

Snapshot
Deduplication
Compression
Encryption
Retention
Integrity Check
Restore

再配合 systemd timer,可以得到一套不需要額外 GUI、相對簡單而且容易維護的 Linux 備份架構。

最重要的是,整套備份建立完成後,不應該停在:

snapshot saved

而應該至少完成一次:

Backup
   ↓
Snapshot
   ↓
Restore
   ↓
SHA-256 / cmp
   ↓
VERIFIED

只有真正還原過的備份,才算完成了最基本的復原驗證。

最後還有一個不能忽略的原則:

Restic repository 和 repository password 不應該成為同一個故障點。

密碼應另外安全保存。

如果 /backup/restic 與 /data 位於同一台機器,這套架構主要提供的是版本、誤刪與資料損壞保護;若還需要防範整台 NAS、磁碟陣列或實體設備故障,則應再把備份 repository 複製到另一台設備或異地儲存。

這也是從「有備份」走向真正備份策略的下一步。


Tags:Debian · Restic · systemd · Backup