restic暗号化バックアップ検証:復元失敗とprune候補を先に見る

ネットワークを描いたアイキャッチ画像
目次

この記事が想定している読者

サービスが少しずつ増えてきて、Docker Composeの定義ファイルや各種設定ファイルが複数サービス分たまってきた。そろそろバックアップを手順化したい。けれど現状は、別ディレクトリへの手コピーか、一度も復元を試していないスクリプトで止まっている――そういう状態の自宅運用者を想定しています。

このとき本当に怖いのは「バックアップを取り忘れること」よりも、「取ったつもりで、いざというときに復元できないこと」「世代を整理するつもりが必要なsnapshotまで消すこと」「暗号化パスワードやリポジトリ指定のミスで、非常時に手が止まること」です。

そこで本記事では、resticを採用すると決める前に、どの失敗条件まで確認しておくべきかを、使い捨てVM上の実コマンドと実出力で確認します。backupが成功したログではなく、復元できない条件と削除候補の見え方からresticの実用性を判断します。

比較:手動コピー / tar+gpg / restic

読者が自然に比べているのは、有償製品ではなく「いまの手段」と「次の手段」です。

観点手動コピーtar + gpgrestic
暗号化なし(別途必要)gpgで可能リポジトリ単位で標準装備
世代管理自前で命名・管理自前で命名・管理snapshotとforgetポリシーで管理
復元確認のしやすさcpで戻すだけだが検証は手作業tar展開+復号、検証は手作業restoreコマンドと差分照合で確認しやすい
失敗ログの読みやすさOS依存、暗黙的に失敗しがちgpg/tarのエラーが分散Fatalメッセージと終了コードが揃う
重複排除なしなしブロック単位で重複排除

手動コピーとtar+gpgが悪いわけではありません。ただ、世代管理と暗号化を一つのツールでまとめ、しかも「失敗したときに何が起きたか」をコマンド出力で読みやすいのはresticの利点です。本記事はその利点が、実際に失敗させたときに成立するかを確認します。

検証環境

  • 環境: 使い捨ての一時VM(Docker利用可、apt導入が可能なレーン)
  • restic: 0.14.0(go1.19.8 / linux/amd64)
  • リポジトリ: VM内ローカルパスのみ(リモートrepoは未使用)
  • 対象データ: ダミーの設定ファイル3点(compose.yml / app.env / readme.txt)
  • 本番データ・本番リポジトリは一切使用していません。検証後にVM内データは削除しています。

resticは標準のDebian環境や軽量コンテナに最初から入っているとは限らないため、パッケージ導入から確認しています。以下のパスワードはすべて検証用のプレースホルダです。実運用では使い回さない、十分に強いパスフレーズを別管理してください。

実行ログ

1. 導入とバージョン確認

# apt-get update && apt-get install -y restic
# restic version
restic 0.14.0 compiled with go1.19.8 on linux/amd64

2. テスト用ダミーデータの準備

# mkdir -p /tmp/restic-lab/data /tmp/restic-lab/repo
# printf 'service: web\nimage: nginx:stable\n' > /tmp/restic-lab/data/compose.yml
# printf '設定項目=lab\nTOKEN=dummy\n' > /tmp/restic-lab/data/app.env
# printf 'home lab config note\n' > /tmp/restic-lab/data/readme.txt
# ls -l /tmp/restic-lab/data
total 12
-rw-r--r-- 1 root root 24 app.env
-rw-r--r-- 1 root root 33 compose.yml
-rw-r--r-- 1 root root 21 readme.txt

3. リポジトリの初期化(init)

# export 設定項目=/tmp/restic-lab/repo
# export 設定項目='example-passphrase'
# restic init
created restic repository 2602e69ea2 at /tmp/restic-lab/repo

Please note that knowledge of your password is required to access
the repository. Losing your password means that your data is
irrecoverably lost.

ここで表示される「パスワードを失うとデータは復旧できない」という警告は、暗号化バックアップの本質です。後述の誤パスワードの挙動と合わせて読んでください。

4. 正常なbackupとsnapshot確認

# restic backup /tmp/restic-lab/data
# restic snapshots
no parent snapshot found, will read all files

Files:           3 new,     0 changed,     0 unmodified
Dirs:            3 new,     0 changed,     0 unmodified
Added to the repository: 2.132 KiB (1.569 KiB stored)

processed 3 files, 78 B in 0:00
snapshot 6a78451a saved
ID        Time                 Host      Tags  Paths
-----------------------------------------------------------------
6a78451a  2026-06-28 03:18:35  labhost         /tmp/restic-lab/data
-----------------------------------------------------------------
1 snapshots

ここまでが「バックアップを取った」という状態です。多くの場合、ここで安心して終わります。本記事はここから先を見ます。

5. 誤パスワードでのアクセス失敗(終了コード1)

# 設定項目='wrong-passphrase' restic snapshots
Fatal: wrong password or no key found

終了コードは1でした。パスワードが違うと一覧すら取れず、Fatal: wrong password or no key found と明確に表示されます。非常時にこの表示を初めて見るより、平時に一度見ておくほうが落ち着いて対処できます。パスワードの管理が、復元可否そのものだと分かります。

6. 対象パス指定ミス時のbackup挙動(終了コード1)

# restic backup /tmp/restic-lab/does-not-exist
/tmp/restic-lab/does-not-exist does not exist, skipping
Fatal: all target directories/files do not exist

存在しないパスを指定すると、skipの行に続いて Fatal: all target directories/files do not exist で終了コード1になります。重要なのは、対象がすべて存在しないと止まる一方で、複数指定のうち一部だけ存在する場合はskipして進む可能性がある点です。「終了コードが0だったから全部取れている」と思い込まず、対象パスの綴りはbackup定義の時点で確認しておくべきです。

7. restoreと元ファイルの一致確認(終了コード0)

# mkdir -p /tmp/restic-lab/restore
# restic restore latest --target /tmp/restic-lab/restore
# diff -r /tmp/restic-lab/data /tmp/restic-lab/restore/tmp/restic-lab/data
restoring <Snapshot 6a78451a of [/tmp/restic-lab/data] at 2026-06-28 03:18:35 ...> to /tmp/restic-lab/restore

diff -r は差分を出力せず終了コード0でした。つまり復元したファイル群が元データと一致しています。snapshot一覧に行が見えることではなく、restore して diff が無言で返ることが「復元できる」の最小限の証拠です。restoreは元のパス構造を再現するため、--target 配下に元の絶対パスがぶら下がる点も確認しておくと、戻し先を間違えません。

8. 2世代目backupとforget –prune –dry-run

# printf 'second generation change\n' >> /tmp/restic-lab/data/readme.txt
# restic backup /tmp/restic-lab/data
# restic forget --keep-last 1 --prune --dry-run
using parent snapshot 6a78451a

Files:           0 new,     1 changed,     2 unmodified
Dirs:            0 new,     3 changed,     0 unmodified
Added to the repository: 2.098 KiB (1.393 KiB stored)

processed 3 files, 103 B in 0:00
snapshot 8f30adcf saved
Applying Policy: keep 1 latest snapshots
keep 1 snapshots:
ID        Time                 Host      Tags  Reasons        Paths
--------------------------------------------------------------------------
8f30adcf  2026-06-28 03:18:39  labhost         last snapshot  /tmp/restic-lab/data
--------------------------------------------------------------------------
1 snapshots

remove 1 snapshots:
ID        Time                 Host      Tags  Paths
-----------------------------------------------------------------
6a78451a  2026-06-28 03:18:35  labhost         /tmp/restic-lab/data
-----------------------------------------------------------------
1 snapshots

Would have removed the following snapshots:
{6a78451a}

to repack:             3 blobs / 360 B
this removes:          1 blobs / 62 B
to delete:             4 blobs / 1.093 KiB
total prune:           5 blobs / 1.153 KiB
remaining:             7 blobs / 1.328 KiB

--dry-run を付けているので、keep 1remove 1、そして Would have removed the following snapshots: {6a78451a} という形で「消える予定」が表示されるだけで、実際の削除は行われません。確認のため一覧を取り直すと、1世代目(6a78451a)も2世代目(8f30adcf)も残っていました。

forget --prune は世代を実際に消す操作です。最初から本番で実行せず、まず --dry-run で「何が消えて、何が残るか」を読んでから外す。これが世代削除で消しすぎないための最も簡単な歯止めです。

9. 整合性check(終了コード0)

# restic check
using temporary cache in /tmp/restic-check-cache-設定項目
create exclusive lock for repository
load indexes
check all packs
check snapshots, trees and blobs
[0:00] 100.00%  2 / 2 snapshots

no errors were found

restic check はリポジトリの構造的な整合性を確認し、no errors were found を返しました。ただしこれは「メタデータと構造に問題がない」ことの確認であり、各データブロックを実際に読み直す検証は別オプションです(後述)。

10. 検証データのクリーンアップ

# rm -rf /tmp/restic-lab
# ls -ld /tmp/restic-lab
ls: cannot access '/tmp/restic-lab': No such file or directory

使い捨てVM内の検証データは削除済みです。

結果のまとめ

  • init / backup / restore / check はいずれも終了コード0で成功し、restore後の差分照合も一致しました。
  • 誤パスワードと対象パスミスはどちらも終了コード1で停止し、Fatal: 行で原因が読み取れました。
  • forget --keep-last 1 --prune --dry-run は削除候補を提示するだけで、実際の世代は残りました。

つまり、resticは「失敗したときに何が起きたか」が読みやすく、復元確認と世代整理を安全に試せるツールだと、今回の範囲では確認できました。

失敗・制約・再現条件

  • 今回はVM内ローカルパスのリポジトリのみで、リモートリポジトリ(SFTPやオブジェクトストレージ等)の挙動は未検証です。
  • retentionポリシー(--keep-daily/--keep-weekly などの組み合わせ設計)は1世代の単純例にとどめており、長期運用向けの設計は別途必要です。
  • restic check は実行しましたが、データブロックを読み直す check --read-data 系の検証は今回行っていません。整合性の確認レベルとしては最小限です。
  • パスワードを失うと復元不能です(init時の警告どおり)。パスワード管理そのものがバックアップ戦略の一部になります。
  • 再現方法: apt導入が可能な使い捨てVM上で、ローカルパスのリポジトリと小さなダミーディレクトリを用意し、本記事のコマンドを同順で実行してください。

結論:採用前に試すべき失敗条件

読者の決定すべきことは「resticを採用する前に、どの失敗まで確認しておくか」でした。今回の検証から言えるのは次のとおりです。

採用に踏み出してよい条件:

  • 誤パスワード時に一覧すら取れないこと(Fatal: wrong password or no key found)を自分の目で確認できた。
  • restore して diff が無言で返る、つまり一致を確認できた。
  • forget --prune--dry-run 付きで先に試し、消える候補を読めるようになった。

もう少し様子を見たほうがよい条件:

  • パスワードを安全に保管・引き継ぐ方法がまだ決まっていない。
  • リモート保存や長期のretention設計が必要なのに、その検証をしていない。

backupが成功したログは、安全の証明にはなりません。復元できない条件を先に試し、削除候補をdry-runで読む――この順序を一度通しておくことが、resticを家庭ラボに迎える前の最低限の準備です。

この検証を回している環境

この検証は、自宅の常設ラボ(使い捨てVM/LXCを回す母艦+GPU+VLAN分離ネットワーク)で動かしています。使っている機材と選定理由、全体構成は1本にまとめています。

ラボ構成のまとめを見る →
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次