Docker ボリュームの中身は root 所有なので、普通にやるとバックアップに sudo が要る。 cron に sudo を書きたくない。restic 自体をコンテナで動かせば全部解決する。
restic をホストにインストールせず、公式イメージを都度起動する。 コンテナの中は root なので、マウントしたボリュームの中身をそのまま読める。 ホスト側で必要な権限は docker グループだけ。
R() {
docker run --rm \
-v "$REPO":/repo \
-v "$PWFILE":/pw:ro \
-v ~/homelab:/src/homelab:ro \
-v monitoring_grafana_data:/src/vol/grafana:ro \
-v portainer_data:/src/vol/portainer:ro \
restic/restic:latest -r /repo --password-file /pw "$@"
}
R backup --host myhost --tag homelab --exclude-caches /src/homelab /src/vol
R forget --host myhost --tag homelab --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
ソースは全部 :ro で渡す。バックアップ処理が元データを壊す事故が原理的に起きない。
ダッシュボードや設定が SQLite に入っているサービスは多い。
稼働中の .sqlite をそのままコピーすると、書き込み途中の状態を掴んで
壊れたファイルがバックアップされることがある。WAL がある場合はさらに厄介。
先に整合コピーを作り、生ファイルのほうは除外する。
# 整合コピーを staging に作る
docker run --rm -v ~/app/appdata:/a:ro -v "$STAGING":/out \
alpine:latest sh -c '
apk add --no-cache sqlite >/dev/null 2>&1
sqlite3 /a/db/db.sqlite ".backup /out/app-db.sqlite"'
# 生ファイルは除外し、staging のほうを取り込む
R backup --exclude '/src/app/appdata/db/db.sqlite*' ...
復元テストでは PRAGMA integrity_check が ok を返すことまで確認する。
ファイルが戻ってきただけでは検証にならない。
容量の大きいものほど「本当に必要か」を考える価値がある。うちの場合、
| 対象 | 容量 | 判断 |
|---|---|---|
| スタック定義(compose ファイル群) | 数百KB | 入れる。消えると構成を失う |
| VPN のノード鍵 | 数KB | 入れる。再登録が面倒 |
| 画像生成のワークフロー・出力 | 65MB | 入れる。手で作ったので再現できない |
| 画像生成のモデル | 29GB | 入れない。再ダウンロードできる |
| Python の仮想環境・パッケージキャッシュ | 19GB | 入れない。再構築できる |
| LLM のモデル | 13.6GB | 入れない。再取得できる |
49GB のディレクトリのうち、本当に失うと困るのは 65MB だった。 「再取得できるか」で切ると、バックアップは驚くほど小さくなる。
最初は内蔵 NVMe にリポジトリを置いていた。誤削除からは守れるが、ディスク故障では両方消える。
外付け SSD を挿して移すとき、REPO= を1行書き換えるだけでは危ない。
なので、リポジトリを探す処理を挟んでから動かす。
find_repo() {
local d
for d in /mnt/backup /media/$USER/PortableSSD; do
# マウント済み かつ restic の config が実在すること
if mountpoint -q "$d" && [ -f "$d/backups/restic/config" ]; then
echo "$d/backups/restic"; return 0
fi
done
echo "エラー: バックアップ用 SSD が見つかりません" >&2
return 1
}
REPO=$(find_repo) # set -e なので、見つからなければここで止まる
ディレクトリの存在ではなく mountpoint -q と中身のファイルの両方を見るのが要点。
ディレクトリは Docker が勝手に作ってしまうので、存在確認だけでは検出できない。
持ち運ぶ外付けは exfat のことが多い。restic のリポジトリは平坦なファイルの集まりなので
exfat でも問題なく動くが、クラスタサイズが 1MB あるとディレクトリだけで
約 290MB のオーバーヘッドが出る(restic は data/00〜ff の
256 ディレクトリを作るため)。TB 級のディスクなら誤差。
それと、コンテナで動かした restic が書いたファイルは root 所有の 0600 になる。
リポジトリを別の場所にコピーするときは、ホストの cp ではなく
コンテナ経由でやらないと「許可がありません」で一部だけコピーされる。
黙って欠けるので気づきにくい。ファイル数を突き合わせて確認したほうがいい。
取れているつもりで取れていない、を避けるために。
restic check --read-data # 全パックを実読み出しして破損を検出
./restore.sh homarr /tmp/t # 空ディレクトリにだけ復元させる(上書き事故の防止)
復元先を空ディレクトリに限定しておくと、うっかり本番に展開する事故が防げる。