Restic 備份教學:如何將 VPS 資料傳送到遠端儲存
本指南教學於 Ubuntu 24.04 使用 Restic 進行備份,包含建立 SFTP repository、設定 nightly systemd timer 以及執行還原演練。透過 deduplication 技術節省空間,並利用 AES-256 加密確保資料安全,避免備份檔與原始資料存放在同一台機器。
為什麼將備份存放在同一台伺服器並非真正的備份
Restic 是一款免費且開源的備份工具。它會將加密且經過重複資料刪除(deduplicated)的檔案快照,傳送到遠端的儲存庫(repository),例如第二台 VPS、家用電腦或 S3 相容的物件儲存空間。本指南將於 Ubuntu 24.04 上進行設定,內容涵蓋從安裝、建立 SFTP 儲存庫、執行首次備份、設定 nightly systemd timer、設定保留策略(retention policy),以及驗證流程是否正確的還原演練。目的地必須是另一台機器,因為存放在同一台伺服器上的副本,會隨伺服器毀損而消失。
在被備份的機器上建立 backup/ 目錄,僅能防止一種情況:誤刪檔案。它無法應對硬碟故障,因為備份檔就在該硬碟上。它無法應對擁有 root 權限的攻擊者,因為攻擊者會優先刪除副本。它也無法應對因帳戶錯誤而導致 VPS 本身被刪除的情況。全球效率最低的資料中心 曾開玩笑說,將名為 backup_final_v2_REAL 的 tarball 存放在與原始資料相同的磁碟陣列中,這個笑話之所以引起共鳴,是因為我們許多人都曾犯過同樣的錯誤。原則是必須將備份移至機器之外,而使用 restic 是遵循此原則最簡單的方式。
Restic 的四個核心概念
Repository。 Restic 寫入資料的地方。這是一個採用 restic 專有格式的目錄,包含大量加密後的 blobs,且僅能由 restic 讀取。請勿手動編輯此目錄;請透過 restic 指令與 -r 位址進行操作。
Snapshot。 被備份檔案在特定時間點的快照。每次執行備份都會建立一個 snapshot,每個 snapshot 都可以獨立還原,且其行為如同該時刻資料的完整副本。
Deduplication。 Restic 會將檔案拆分為以內容定義的 chunks,並僅上傳 repository 中尚未存在的 chunks。第一次備份會上傳所有內容;之後每次執行僅上傳大致變動的部分。若一個 20 GB 的每日 snapshot 中僅有 50 MB 變動,則僅需花費約 50 MB 的空間,因此保留數十個 snapshot 的成本非常低。
Encryption by default。 Restic repository 預設皆為加密狀態 (AES-256),且所有指令都需要 repository 密碼。備份主機或儲存供應商僅會看到加密後的 blobs。這會導致一個嚴重的後果:若遺失密碼,資料將永久消失,這是系統設計使然。請將密碼副本存放於此伺服器以外的地方。此點至關重要,下文將再次提及。
在 Ubuntu 24.04 上安裝 restic
sudo apt update && sudo apt install -y restic
restic version在 Ubuntu 24.04 上,此指令會安裝 restic 0.16.4,而目前的官方版本為 0.19.1。版本差異是因為 LTS (長期支援) 版本會固定套件版本,但這不影響使用:0.16.4 已足以完成本指南的所有操作。若您需要最新的版本以獲得效能提升,請從 restic 專案的 GitHub releases 頁面下載官方單一執行檔,使用 bunzip2 解壓縮,並安裝至 /usr/local/bin/restic;restic 的安裝僅需這些步驟。
在另一台伺服器上透過 SFTP 建立 repository
您需要一台目標機器:通常會選擇第二台小型 VPS,任何具備 SSH server 與剩餘磁碟空間的裝置皆可。Restic 使用 SFTP (透過 SSH 進行檔案傳輸),因此備份主機不需要安裝任何軟體。在本指南中,備份主機為 10.0.0.12,使用者名稱為 restic。請勿將該使用者命名為 backup:Ubuntu 與 Debian 在安裝後都會預設包含一個名為 backup 的保留系統帳號 (uid 34,無 login shell),這會導致 adduser backup 失敗,且 ssh backup@... 會被存放在 nologin。
夜間排程會在被備份的伺服器上以 root 權限執行,因此 root 必須具備登入備份主機的 key 權限。請建立一個不含 passphrase 的專用 key,因為凌晨 3 點不會有人在場輸入密碼,接著將其複製過去:
sudo ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N "" -C "web1-restic"
sudo ssh-copy-id -i /root/.ssh/id_ed25519.pub restic@10.0.0.12
sudo ssh restic@10.0.0.12 true && echo key login works如果您不熟悉 key 的操作,請參閱 SSH key 管理基礎,其中解釋了其模型、權限以及日後如何撤銷 key。
接下來是 repository password。請將強密碼產生至僅限 root 讀取的檔案中:
openssl rand -base64 32 | sudo tee /root/.restic-password
sudo chmod 600 /root/.restic-password在繼續後續步驟前,請先將該密碼存入您的密碼管理員。如果這台 VPS 損毀,只要擁有 repository 與此密碼即可還原所有資料;若只有 repository 而沒有密碼,則無法還原任何內容。
初始化 repository:
sudo restic -r sftp:restic@10.0.0.12:/srv/restic/web1 --password-file /root/.restic-password initcreated restic repository 9f3c2a1b0d at sftp:restic@10.0.0.12:/srv/restic/web1另一種目標是 S3-compatible object storage,如果您不想運行第二台機器,這是正確的選擇。任何 S3-compatible bucket 的運作方式皆相同,僅地址與兩個憑證變數會有所不同:
export AWS_ACCESS_KEY_ID=your-key-id
export AWS_SECRET_ACCESS_KEY=your-secret-key
sudo -E restic -r s3:https://s3.example.com/web1-backups --password-file /root/.restic-password initinit 之後的所有步驟對於這兩種目標都是相同的。本指南後續內容皆以 SFTP 地址為範例,請自行替換為您的地址。
第一次備份(包含排除項目)
僅備份無法透過重新安裝來恢復的資料,而非整個檔案系統。重新安裝即可還原作業系統,但設定與資料無法還原。對於典型的 VPS,這代表需要備份 /etc、/home,以及應用程式存放狀態的位置,例如 /srv 或 /var/www。請排除快取(cache)目錄,因為它們體積龐大、每日變動,且會自動重建:
sudo restic -r sftp:restic@10.0.0.12:/srv/restic/web1 --password-file /root/.restic-password backup /etc /home /srv --exclude '/home/*/.cache'Files: 4181 new, 0 changed, 0 unmodified
Added to the repository: 731.204 MiB (312.418 MiB stored)
snapshot 5b8a3f2c saved首次執行時會上傳所有內容,因此需要較長時間。再次執行相同的指令,只需數秒即可完成,並顯示少數檔案變更與少數 MiB 新增內容,因為重複資料刪除(deduplication)技術僅會上傳新的資料塊(chunks)。列出目前的備份內容:
sudo restic -r sftp:restic@10.0.0.12:/srv/restic/web1 --password-file /root/.restic-password snapshots每個快照(snapshot)會顯示一個 ID、時間以及所包含的路徑。還原時請使用這些 ID。
使用 systemd timer 進行每晚執行
每次指令都要輸入 repository 位址非常繁瑣,且手動執行的備份通常在一個月內就會停止。這兩個問題都可以透過一個 script 與一個 timer 來解決。該 script 會設定 restic 所需的兩個環境變數 RESTIC_REPOSITORY 與 RESTIC_PASSWORD_FILE,使 script 內的指令保持簡短:
sudo nano /usr/local/bin/restic-backup.sh#!/usr/bin/env bash
set -euo pipefail
export RESTIC_REPOSITORY='sftp:restic@10.0.0.12:/srv/restic/web1'
export RESTIC_PASSWORD_FILE=/root/.restic-password
restic backup /etc /home /srv --exclude '/home/*/.cache'
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
restic checksudo chmod 700 /usr/local/bin/restic-backup.shforget 與 check 兩行會在接下來的兩個章節中說明。接著是排程:一個負責執行該 script 的 oneshot service,以及一個會在每天 03:00 觸發的 timer。在此情境下,使用 timer 優於 cron,因為執行紀錄會寫入 journal,且當伺服器從停機狀態恢復後,Persistent=true 會立即執行錯過的備份。
# /etc/systemd/system/restic-backup.service
[Unit]
Description=Nightly restic backup
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/restic-backup.sh# /etc/systemd/system/restic-backup.timer
[Unit]
Description=Run the nightly restic backup
[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=15m
Persistent=true
[Install]
WantedBy=timers.target啟用 timer,接著手動執行一次 service 以觀察運作狀況:
sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer
sudo systemctl start restic-backup.service
sudo journalctl -u restic-backup.service -fsystemctl list-timers 會顯示下次執行的時間。您也可以直接產生這對 unit files,而不必手動輸入:
關於這兩個檔案的完整模式,包含日曆語法以及 service 可使用的強化指令(hardening directives),請參閱 在 VPS 上將程式作為 systemd service 執行。
備份在成功還原前僅是傳聞
請將此句視為準則。備份作業每晚顯示綠燈,僅代表作業已執行,並不代表資料可以成功還原。必須透過兩項檢查來彌補落差。
第一項是 restic check,腳本已在每晚自動執行。它會驗證 repository 結構與 index,藉此在備份主機發生靜默毀損時,於隔日即發現問題,而非等到還原時才發現。每月請執行一次深度版本,該版本會下載並進行加密驗證,驗證實際資料中隨機的 10%:
sudo -i
export RESTIC_REPOSITORY='sftp:restic@10.0.0.12:/srv/restic/web1'
export RESTIC_PASSWORD_FILE=/root/.restic-password
restic check --read-data-subset=10%由於每次選取的子集都是隨機的,每月執行即可逐步遍及整個 repository,且無需支付完整下載的成本。
第二項是還原演練。請在上述的 root shell 中,將最新 snapshot 中的一個實際目錄還原至 scratch 位置,並與現有的檔案進行比對:
restic restore latest --target /srv/restore-drill --include /etc/ssh
diff -r /etc/ssh /srv/restore-drill/etc/ssh若 diff 沒有輸出任何內容,代表每個 byte 都完全一致,這才是唯一有效的證據。完成後請刪除 /srv/restore-drill。每月執行此演練一次,且每年執行一至兩次完整版本:將整個最新 snapshot 還原至 scratch VPS,並確認應用程式能從中正常啟動。當你在壓力下需要此功能運作時,你希望這是一項你已經執行過的常規流程。
Retention: forget 與 prune
若未設定策略,快照會不斷累積,導致 repository 持續膨脹。該指令碼的 forget 行會在每晚執行策略:--keep-daily 7 會保留過去 7 天內每天一個快照,--keep-weekly 4 會保留過去 4 週內每週一個快照,而 --keep-monthly 6 會保留過去 6 個月內每月一個快照。所有未受規則保護的內容都會被 forget。
單獨執行 forget 只會移除快照紀錄;data chunks 仍會保留在 repository 中,直到被刪除為止。這就是 --prune 的作用:它會尋找不再被任何快照引用的 chunks 並將其刪除,屆時才能真正釋放磁碟空間。由於 prune 會執行實際的 repository 維護工作,在大型 repository 中,有些人會選擇每晚執行 forget 並每週執行一次 --prune;對於一般的 VPS 規模,每晚執行即可。
Databases: 先進行 dump,再備份 dump 檔案
Restic 在讀取檔案時即進行複製,而資料庫會持續寫入檔案。若在寫入過程中擷取運作中的資料庫檔案,會導致還原後資料庫損毀,因為複製的內容混雜了寫入前後的頁面。標準解決方案是:先讓資料庫引擎產生一個一致的匯出檔案,再由 restic 備份該檔案。
針對 PostgreSQL,請在 restic-backup.sh 的最上方、restic backup 指令之前加入 dump 指令,並將 dump 目錄包含在備份路徑中:
mkdir -p /var/backups/db
sudo -u postgres pg_dump myapp | gzip > /var/backups/db/myapp.sql.gzmysqldump 對於 MariaDB 與 MySQL 具有相同作用。若要查看完整流程的實作範例,請參閱 Nextcloud 備份章節,該章節會開啟維護模式、匯出 Postgres,並將檔案作為一組一致的集合進行複製,這正是 restic 每晚應從主機移走的完整集合。SQLite 的處理邏輯相同,但操作較簡單:Vaultwarden 指南 會暫時停止容器幾秒鐘,以取得 db.sqlite3 的冷備份,接著由 restic 將該封存檔傳送至伺服器外。
FAQ
restic 備份是否經過加密?
是的,始終如此。每個 restic repository 都使用 AES-256 加密,沒有不加密的模式,且每個指令都需要 repository password。儲存 repository 的機器或供應商僅持有加密後的 blobs,因此即使備份主機遭入侵,您的檔案也不會外洩。這是一項絕對的權衡:沒有密碼,任何人皆無法復原資料,因此請將密碼副本儲存在伺服器之外。
restic 是否支援增量備份?
每個 restic snapshot 的行為皆如同完整備份,但僅消耗增量儲存空間。Restic 會將檔案拆分為 chunks,且僅上傳 repository 尚未儲存的 chunks,因此每日執行時,傳輸量僅約等於當天變動的部分。與傳統的增量方案不同,這裡不需要重放鏈結 (chain):任何 snapshot 都能直接進行復原,且刪除舊的 snapshot 絕不會導致較新的 snapshot 失效。
如何從 restic 備份中復原檔案?
執行 restic snapshots 以尋找 snapshot ID,接著執行 restic restore <id> --target /some/empty/dir 進行復原,並加上 --include /path 僅復原部分內容。latest 可用來取代 ID。Restic 會在目標路徑下重建原始目錄結構,因此復原 /etc/ssh 會存放於 /some/empty/dir/etc/ssh。請在實際需要前先進行練習,因為未經測試的備份僅是傳聞。
我應該多頻繁執行 restic backup?
對於伺服器而言,每日執行一次是合理的底線,且重複資料刪除 (deduplication) 降低了成本:每次執行僅上傳自上次執行以來變動的 chunks。對於變動快速或即便損失一天資料也會造成嚴重影響的資料,可以依照相同的定時模式每幾小時執行一次。頻率是簡單的部分;請務必定期執行 restic check 並每月進行一次復原演練,因為沒有驗證的排程只是虛假的安全感。
如果我遺失了 restic repository password 會怎樣?
備份將無法復原。Restic 的加密技術沒有後門,也沒有重設機制,因此密碼與備份本身同樣重要。請將副本儲存在您的密碼管理員或其他非備份伺服器的持久位置。在您仍保有存取權限時,可以使用 restic key add 為同一個 repository 註冊第二個密碼,作為備援使用。