の自動バックアップが動いているから安心」と思っていましたが、設定画面を見て気づきました。週 1 回・1 世代のみ。3 日前の誤削除には対応できません。

本記事では、当方が Hostinger + + + Traefik + 各種 DB)に対して、 + 毎日を組んだ実装記録を、コマンド付きで公開します。月額 22 円、初回 16.7GB を 2 分 59 秒でアップロード、7 世代保管の構成です。

構成全体像

restic + Cloudflare R2 によるバックアップ構成図
restic + Cloudflare R2 によるバックアップ構成図
要素採用理由
バックアップ元Hostinger VPSNextcloud / n8n / Postiz / 各 DB が稼働
バックアップツールrestic 0.16.4単一バイナリ、暗号化、対応
保存先 R2egress 無料、$0.015/GB/月、S3 互換
スケジュール 04:00 UTC(13:00 JST)サーバー負荷の低い時間帯
日次 7 + 週次 4 + 月次 6restic forget で自動運用
暗号化restic 標準パスフレーズで全リポジトリ暗号化

Step 1: Cloudflare R2 のセットアップ

1.1 R2 を有効化

Cloudflare ダッシュボード → R2 で「Purchase R2」をクリック。クレジットカードを登録(無料枠内なら課金 0 円)。

1.2 バケット作成

項目設定
Bucket name<任意>(例: vps-backup
LocationAutomatic(Asia Pacific が自動選択)
Default Storage ClassStandard(restic は週 1 回 check で読みに行くため)

Infrequent Access クラスは月 1 回未満アクセス前提で、restic の整合性チェックで取り出し料金が発生するため不向きです。

1.3 Account API Token を発行

「Manage R2 API Tokens」→「Create Account API token」。

項目設定
Token namevps-backup-token
PermissionsObject Read & Write(Admin Read & Write は権限が広すぎるので NG)
Specify bucket(s)作成したバケットのみ
TTLForever

発行後に表示される Access Key IDSecret Access Key を必ずメモ。この画面を閉じると Secret は二度と表示されません

💡 KEY TAKEAWAYS
Account API Token は必ずバケットスコープで発行。「All buckets」+「Admin Read & Write」は事故時の影響範囲が広すぎます。

バックアップ「あるはず」じゃ済まない時代、御社の耐久力を診断しませんか?
細マッチョ企業診断 / 3 分 8 問
診断する

Step 2: VPS 側のセットアップ

2.1 restic のインストール

``bash
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq restic
restic version

→ restic 0.16.4 compiled with go1.22.2 on linux/amd64


`

apt-get の対話プロンプトが表示されると SSH 越しにハングするので、DEBIAN_FRONTEND=noninteractive を必ず設定します。

2.2 認証情報を保存

/root/.config/restic/r2-credentials.env を chmod 600 で作成:

`bash
export AWS_ACCESS_KEY_ID=""
export AWS_SECRET_ACCESS_KEY=""
export RESTIC_REPOSITORY="s3:https://.r2.cloudflarestorage.com/"
export RESTIC_PASSWORD=""
`

RESTIC_PASSWORD3 重保管してください(VPS / 手元 PC / オフライン媒体)。これを失うとバックアップは復元できなくなります

2.3 リポジトリ初期化

`bash
. /root/.config/restic/r2-credentials.env
restic init

→ created restic repository at s3:https://...


`

これで R2 バケット内に restic のリポジトリ構造が作られます。

Step 3: バックアップスクリプト

/usr/local/bin/vps-backup.sh を作成(chmod 700)。流れは「DB ダンプ → restic backup → 保持ポリシー適用 → 軽い整合性チェック」。

`bash
#!/usr/bin/env bash
set -euo pipefail

LOG_FILE="/var/log/vps-backup.log"
DUMP_DIR="/tmp/restic-dumps-$$"
log() { printf '[%s] %s\n' "$(date -u +%FT%TZ)" "$*" | tee -a "$LOG_FILE"; }
trap 'rm -rf "$DUMP_DIR"' EXIT

. /root/.config/restic/r2-credentials.env
. /root/.config/restic/db-credentials.env

mkdir -p "$DUMP_DIR" && chmod 700 "$DUMP_DIR"

log "= Backup START ="

DB ダンプ(並列で MariaDB + Postgres x N)

docker exec -e MYSQL_PWD="$NEXTCLOUD_DB_PASSWORD" nextcloud-db \ mariadb-dump --single-transaction --quick --skip-lock-tables \ -uroot nextcloud | gzip > "$DUMP_DIR/nextcloud.sql.gz"

docker exec -e PGPASSWORD="$POSTIZ_DB_PASSWORD" postiz-postgres \
pg_dumpall -U postiz | gzip > "$DUMP_DIR/postiz.sql.gz"

restic backup

restic backup \ --tag scheduled \ --exclude-caches \ --exclude '/var/log' --exclude '/tmp' \ --exclude '/root/.npm' --exclude '/root/.cache' \ /var/lib/docker/volumes/nextcloud_nc_data \ /var/lib/docker/volumes/n8n_data \ /var/lib/docker/volumes/postiz_postiz-uploads \ /etc /root/.ssh /root/.config \ "$DUMP_DIR"

保持ポリシー(日次 7 + 週次 4 + 月次 6)+ 自動 prune

restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

軽い整合性チェック(5% サブセット)

restic check --read-data-subset=5%

log "= Backup END ="
`

mariadb-dump--single-transactionロックなしダンプ。本番稼働中の DB に影響しません。

Step 3.1: cron に登録

`bash
( crontab -l 2>/dev/null; echo '0 4 * * * /usr/local/bin/vps-backup.sh >> /var/log/vps-backup.log 2>&1' ) | crontab -
`

毎日 04:00 UTC(13:00 JST)に自動実行。サーバー負荷の低い時間帯です。

Step 3.2: ログローテート設定

`bash
cat > /etc/logrotate.d/vps-backup </var/log/vps-backup.log {
weekly
rotate 8
compress
missingok
notifempty
create 600 root root
}
EOF
`

これでログが肥大化することを防げます。

Step 4: 初回フルバックアップ

`bash
nohup /usr/local/bin/vps-backup.sh >> /var/log/vps-backup.log 2>&1 &
tail -f /var/log/vps-backup.log
`

当方の実測値:

項目
ファイル数332,446
元サイズ16.730 GiB
R2 上の実サイズ5.048 GiB(圧縮率 2.56x、60.93% 削減)
所要時間2 分 59 秒
アップロード速度約 95 MB/s

R2 月額試算: 5GB × $0.015 = $0.075(約 11 円)。7 世代分の差分も含めて月 $0.15(約 22 円)程度に収まります。

Step 5: — 必ずやる

「バックアップは取れている」と思い込んで実は復元できないケースが多すぎます。月 1 回必ず復元テストを行います。

`bash
. /root/.config/restic/r2-credentials.env

任意のファイルを R2 から取り出す

restic restore latest \ --target /tmp/restore-test \ --include /etc/hostname

オリジナルと比較

diff /etc/hostname /tmp/restore-test/etc/hostname && echo "✅ 一致"

後片付け

rm -rf /tmp/restore-test
`

当方の実測:単一ファイル復元は1 秒以内、フル復元は 5〜15 分。

💡 KEY TAKEAWAYS
復元テストをしていないバックアップは、バックアップではありません。月 1 回必ず実行する習慣をつけてください。

ハマりポイント 3 つ

restic + R2 構築で踏んだ 3 つの罠
restic + R2 構築で踏んだ 3 つの罠

落とし穴 1: SSH 鍵パスのタイプミス

~/.ssh/_ed25519~/.ssh/_ed25739 と打ち間違えて、SSH コマンドが「Permission denied (publickey)」で延々と弾かれました。Tab 補完を使わずに手打ちで ed25519 を入れる場合、数字部分は要注意です。

突破: SSH 鍵パスはエイリアスを ~/.ssh/config に登録して、ssh で接続するスタイルが安全です。

落とし穴 2: apt-get install がプロンプトでハング

apt-get install restic が SSH 越しに実行中、Configuration file modified の対話プロンプトが出てハングしました。SSH は「待機中」と見えるだけで、何も進みません。

突破: export DEBIAN_FRONTEND=noninteractive` をスクリプト先頭で必ず宣言。これだけで対話プロンプトを全て自動回答に切り替えられます。

落とし穴 3: restic の保持ポリシー試算ミス

「日次 7 + 週次 4 + 月次 6 = 17 世代分の容量が必要」と思いがちですが、restic は重複排除しているため、実容量は初回 + 変更分のみ。当方の場合 16.7GB の元データに対し、7 世代維持しても R2 上は 7〜10GB 程度で済みます。

突破: 試算は「初回 × 1.5」程度を上限の目安に。月額計算は実測でみて初月で確定させます。

次のアクション

「バックアップは大事」と頭で分かっていても、実際に組まないと災害復旧はできません。本記事の構成は VPS 1 台あたり月 22 円から始められ、構築 30〜60 分で運用に乗ります。

本シリーズ第 1 回「Nextcloud のロック詰まりは Redis 化で解決する」、第 3 回「Dropbox 代替を自社で組む判断軸」と合わせて読むと、自社のクラウドストレージを / サブスクリプションから卒業する全体像が掴めます。

「自社で組むのは技術的に厳しい」「組んだが運用が続かない」という方は、まず 細マッチョ企業診断 で耐久力(事業継続性)の現在地を把握ください。EXBANK ではバックアップ構築 + 6 ヶ月伴走運用パッケージも提供しています。