- Rust 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Level 19 runs on a single core at a couple of MB/s: the 1.4 GB Pleroma dump took 15 minutes and the 1 GB relay dump 8. Level 3 does the relay in about 30 seconds for roughly 50% larger files, a few tens of MB. Co-Authored-By: Claude Opus 5.5 <[email protected]> |
||
| src | ||
| systemd | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| config.example.toml | ||
| README.md | ||
backup-manager-san
PostgreSQL・ファイル・docker volume を Cloudflare R2 にバックアップするワンショット CLI。
ホスト上、LXC コンテナ内、docker コンテナ内(LXC の中の docker も可)で pg_dump /
tar などを実行し、zstd 圧縮 → age 暗号化(.zst.enc)してから R2 に
(大きいファイルはマルチパートで)アップロードします。完了時に Discord へ通知し、
結果を状態ファイル(JSON)に残すので infra-status などから監視できます。
LXC 上の Pleroma と ActivityRelay にはプリセット(kind = "pleroma" / "relay")が
あり、それ以外は kind = "generic" で中身を1つずつ書きます。
lxd / docker グループに所属したユーザー(root 不要)で動かす前提で、
systemd --user タイマーで定期実行します。
各アーティファクトは <元名>.zst.enc(例 pleroma-db-<ts>.dump.zst.enc)として保存され、
中身は age 形式で暗号化されます。復号は backup-manager-san decrypt(下記)で行います。
バックアップ対象
| ターゲット | 内容 |
|---|---|
| generic | [[targets.artifacts]] に書いたもの(下記) |
| pleroma | pg_dump -Fc(DB pleroma)/ /etc/pleroma・/opt/pleroma/config/ /var/lib/pleroma/uploads(任意) |
| relay | docker の activity-relay-postgres を pg_dump -Fc/ docker volume relay_keys・relay_redis_data(redis は事前に SAVE)/ /root/relay-server(compose・.env) |
R2 上のキー: <prefix>/<target>/<YYYY>/<MM>/<target>-<artifact>-<timestamp>.<ext>
generic ターゲット
各アーティファクトは「どこで実行するか(exec)」と「何を取るか(type)」の組み合わせ。
exec |
実行場所 |
|---|---|
省略 / {} |
このホスト(sh -c) |
{ lxc = "c" } |
lxc exec c -- sh -c |
{ docker = "d" } |
docker exec d sh -c |
{ lxc = "c", docker = "d" } |
LXC c の中の docker d |
type |
中身 | 拡張子 |
|---|---|---|
postgres |
pg_dump -Fc -Z 0 -d <db>(user で -U、run_as で su - して peer 認証) |
.dump |
paths |
root(既定 /)からの相対パスで paths を tar(excludes 可) |
.tar |
docker_volume |
/var/lib/docker/volumes/<volume>/_data を tar |
.tar |
command |
command の標準出力 |
ext で指定 |
before に書いたコマンド(例: redis-cli SAVE)はアーティファクトの前に実行され、
失敗しても警告だけで続行します。例は config.example.toml の Mitra を参照。
状態ファイル
各ターゲットの最後の実行結果(成否・エラー・開始/終了時刻・アップロードしたキーとサイズ)を
[general].status_file(既定 ~/.local/state/backup-manager/status.json)に書きます。
--target で一部だけ実行したときは、そのターゲットの分だけ置き換えます。
ビルド
cargo build --release
install -m 0755 -D target/release/backup-manager-san ~/.local/bin/backup-manager-san
どのディストリでも動く静的バイナリ(musl)は docker で作れます:
docker run --rm -v "$PWD":/src -w /src rust:alpine \
sh -c 'apk add -q musl-dev && cargo build --release --target-dir target/alpine'
# → target/alpine/release/backup-manager-san
設定
設定ファイルは(--config 未指定時)~/.config/backup-manager/config.toml →
/etc/backup-manager/config.toml の順に探索します。ユーザー運用ではホーム側に置きます。
mkdir -p ~/.config/backup-manager
cp config.example.toml ~/.config/backup-manager/config.toml
chmod 600 ~/.config/backup-manager/config.toml # R2 の秘密鍵が入るので必ず
$EDITOR ~/.config/backup-manager/config.toml
R2 の API トークンは Cloudflare ダッシュボードの R2 → "Manage API Tokens" で
Object Read & Write 権限のものを発行し、access_key_id / secret_access_key /
account_id(または endpoint)を設定します。
暗号鍵([encryption])
推奨は**非対称(age recipient)**方式。鍵ペアを作り、公開鍵だけをこのホストの config に置き、秘密鍵はホスト外(手元の PC など)に保管します:
# 秘密鍵を手元に生成(このホストには置かない想定)
backup-manager-san keygen -o ~/backup-identity.txt
# → 標準エラーに age1... の公開 recipient が出るので config.toml に貼る:
# [encryption]
# recipients = ["age1..."]
簡易にやるなら対称パスフレーズでも可(recipients を空にして passphrase = "...")。
Discord 通知([notify.discord])
Discord のチャンネル設定 → 連携サービス → Webhook で URL を発行し、
webhook_url に設定します。on = "failure" にすると失敗時のみ通知します。
通知には各オブジェクト名と圧縮後サイズ、合計サイズが載ります。
実行
# 設定の検証だけ(dump/upload はしない)
backup-manager-san --dry-run
# 全ターゲット実行
backup-manager-san
# 特定ターゲットのみ
backup-manager-san --target pleroma
# 保持期間による削除をスキップ
backup-manager-san --no-prune
ログレベルは RUST_LOG=debug などで調整。
定期実行(systemd --user timer)
mkdir -p ~/.config/systemd/user
cp systemd/backup-manager-san.service ~/.config/systemd/user/
cp systemd/backup-manager-san.timer ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now backup-manager-san.timer
# ログアウト中/未ログインでもタイマーを動かす(初回のみ)
loginctl enable-linger "$USER"
# 手動トリガ / 状態確認
systemctl --user start backup-manager-san.service
systemctl --user list-timers backup-manager-san.timer
# ログ(--user -u が空になる環境があるのでこちらが確実)
journalctl _SYSTEMD_USER_UNIT=backup-manager-san.service -n 50
復元の概要
R2 からオブジェクト(*.zst.enc)を取得し、まず復号します。復号すると元の
.dump(Postgres custom フォーマット)や .tar に戻ります。
# 非対称方式なら秘密鍵ファイルを指定
backup-manager-san decrypt --identity ~/backup-identity.txt \
-i pleroma-db-<ts>.dump.zst.enc -o pleroma-db.dump
# パスフレーズ方式なら config の passphrase を使う(--identity 不要)
backup-manager-san decrypt -i relay-db-<ts>.dump.zst.enc -o relay-db.dump
Pleroma DB(custom フォーマット):
lxc file push pleroma-db.dump pleroma/tmp/restore.dump
lxc exec pleroma -- su - postgres -c \
'pg_restore --clean --if-exists -d pleroma /tmp/restore.dump'
Pleroma 設定 / uploads(/ 起点の相対パスで格納された tar):
backup-manager-san decrypt --identity ~/backup-identity.txt \
-i pleroma-config-<ts>.tar.zst.enc -o pleroma-config.tar
lxc exec pleroma -- tar xf - -C / < pleroma-config.tar
Relay DB:
cat relay-db.dump | lxc exec relay -- \
docker exec -i activity-relay-postgres pg_restore --clean --if-exists -U relay -d relay
Relay volume: 対象コンテナを停止してから volume 内へ展開:
backup-manager-san decrypt --identity ~/backup-identity.txt \
-i relay-volume-relay_keys-<ts>.tar.zst.enc -o relay_keys.tar
lxc exec relay -- docker compose -f /root/relay-server/compose.yaml down
lxc exec relay -- tar xf - -C / < relay_keys.tar
lxc exec relay -- docker compose -f /root/relay-server/compose.yaml up -d
tar アーカイブは先頭
/を除いた相対パスで格納しているため、tar x -C /で 元の位置に戻ります。
注意
staging_dirには「単一アーティファクトの最大サイズ」ぶんの空き容量が必要 (Pleroma の uploads を含める場合は特に)。アップロード成功後に逐次削除されます。- 保持期間 (
retention_days) の削除はバックアップ成功後に走るため、常に最新が残ります。 - postgres は稼働中でも
pg_dumpは一貫したスナップショットを取れます。redis volume は アーカイブ前にSAVEを発行しています。