SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor

ResticでVPSを別サーバーへバックアップする方法

ResticでVPSのデータを暗号化・重複排除し、別サーバーやS3互換ストレージへ保存します。Ubuntu 24.04での設定、毎晩のsystemdタイマー、保持ポリシー、復元テストまで解説します。

同じサーバー上のバックアップがバックアップにならない理由

Resticは、ファイルの暗号化および重複排除済みのスナップショットを別の場所にあるリポジトリへ送信する、無料のオープンソースバックアップツールです。送信先には、2台目のVPS、自宅のマシン、またはS3互換オブジェクトストレージを指定できます。このガイドでは、Ubuntu 24.04でのインストールから、SFTP経由のリポジトリ、最初のバックアップ、毎晩実行するsystemdタイマー、保持ポリシー、そして全体が機能することを確認するリストア手順まで設定します。コピーは同じサーバー上に置かず、別のマシンに保存する必要があります。同じサーバー上のコピーは、サーバーの障害とともに失われるためです。

バックアップ対象のサーバー上にbackup/ディレクトリを作成しても、防げるのは誤ってファイルを削除する事故だけです。そのディスク上にあるため、ディスク障害には耐えられません。root権限を持つ攻撃者が最初にコピーを削除するため、侵害にも耐えられません。VPS自体を削除してしまうアカウント操作のミスにも耐えられません。世界で最も非効率なデータセンターは、データと同じアレイ上にbackup_final_v2_REALという名前のtarballを置くことを冗談にしています。その冗談が通じるのは、多くの人が実際に同じことをしてきたからです。バックアップはサーバーの外部に置くのが原則です。Resticは、その原則を最も簡単に実践できる方法です。

Restic の4つの要点

Repository。 restic が書き込む場所です。restic 独自形式のディレクトリで、暗号化された blob が格納されており、読み取れるのは restic だけです。手動で編集せず、restic のコマンドと -r アドレスを通じて操作します。

Snapshot。 バックアップしたファイルを、ある時点で取得した状態です。バックアップを実行するたびに snapshot が作成され、各 snapshot は単独で復元できます。それぞれが、その時点のデータ全体のコピーとして動作します。

Deduplication。 Restic はファイルをコンテンツに基づく chunk に分割し、repository にまだ存在しない chunk だけをアップロードします。最初のバックアップではすべてをアップロードしますが、それ以降の実行では変更された部分だけをおおむねアップロードします。20 GB のデータで 50 MB だけ変更された場合、毎晩の snapshot に必要な容量は約 50 MB です。そのため、数十個の snapshot を保持しても安価です。

Encryption by default。 restic の repository は常に暗号化されます(AES-256)。--insecure-no-password を渡さない限り、空のパスワードで repository を作成または開くことを restic は拒否します(restic 0.17.0 以降)。したがって、このガイドの手順で設定した repository では、すべてのコマンドでパスワードが必要です。バックアップホストやストレージプロバイダーが確認できるのは、暗号化された blob だけです。重大な点は、パスワードを失うと、設計上、データを永久に失うことです。このサーバー以外の場所にもパスワードのコピーを保管してください。この点は重要なので、以下でもさらに2回取り上げます。

Ubuntu 24.04 に restic をインストールする

sudo apt update && sudo apt install -y restic
restic version

Ubuntu 24.04 では restic 0.16.4 がインストールされます。現在の upstream リリースは 0.19.1 です。この差は、LTS(長期サポート)リリースではパッケージのバージョンが固定されるために生じます。ただし、このガイドでは問題ありません。0.16.4 ですべての手順を実行できます。速度向上のために最新リリースを使用する場合は、restic プロジェクトの GitHub releases ページから公式の単一バイナリビルドをダウンロードし、bunzip2 で展開して /usr/local/bin/restic にインストールします。restic のインストールに必要な作業はこれだけです。

SFTP経由で別のサーバーにリポジトリを作成する

保存先のマシンが必要です。通常は2台目の小規模なVPSを使用します。SSHサーバーと空きディスクがあるマシンなら使用できます。ResticはSFTP(SSH経由のファイル転送)に対応しているため、バックアップホストにソフトウェアをインストールする必要はありません。このガイドでは、バックアップホストを10.0.0.12、ユーザー名をresticとします。このユーザー名にbackupを使用しないでください。UbuntuとDebianは、すべてのインストール環境にbackup(uid 34、ログインシェルなし)という予約済みのシステムアカウントを作成します。そのため、adduser backupは失敗し、ssh backup@...はnologinに配置されます。

夜間ジョブはバックアップ対象サーバー上でrootとして実行されるため、rootにはバックアップホストへの鍵認証によるログインが必要です。午前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

鍵認証に慣れていない場合は、SSH鍵管理の基本で仕組み、権限、後から鍵を失効させる方法を説明しています。

次に、リポジトリのパスワードを作成します。rootだけが読み取れるファイルに強力なパスワードを生成します。

openssl rand -base64 32 | sudo tee /root/.restic-password
sudo chmod 600 /root/.restic-password

先に進む前に、そのパスワードをパスワードマネージャーにコピーしてください。このVPSが停止しても、リポジトリとこのパスワードがあればすべて復元できます。パスワードがなければ、リポジトリから何も復元できません。

リポジトリを初期化します。

sudo restic -r sftp:restic@10.0.0.12:/srv/restic/web1 --password-file /root/.restic-password init
created restic repository 9f3c2a1b0d at sftp:restic@10.0.0.12:/srv/restic/web1

別の保存先として、S3互換オブジェクトストレージも使用できます。2台目のマシンを運用したくない場合に適しています。S3互換バケットなら同じ方法で使用できます。変更するのはアドレスと2つの認証情報変数だけです。

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 init

init以降は、どちらの保存先でも同じです。このガイドの残りではSFTPアドレスを示します。使用するアドレスに置き換えてください。

除外設定を行った最初のバックアップ

再インストールできないデータをバックアップし、ファイルシステム全体は対象にしません。オペレーティングシステムは再インストールで復元できますが、設定とデータは復元できません。一般的な VPS では、/etc、/home、およびアプリケーションが状態を保存する場所(/srv や /var/www など)が対象になります。キャッシュは除外します。キャッシュは容量が大きく、毎日更新され、自動的に再生成されるためです。

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 が数個あることが報告されます。取得済みの一覧を表示します。

sudo restic -r sftp:restic@10.0.0.12:/srv/restic/web1 --password-file /root/.restic-password snapshots

各スナップショットには ID、時刻、および含まれるパスが表示されます。復元にはこれらの ID を使用します。

systemd timerによる夜間実行

すべてのコマンドでリポジトリのアドレスを入力するのは手間がかかります。また、手動で実行するバックアップは1か月以内に実行されなくなります。どちらの問題も、1つのスクリプトと1つのタイマーで解決できます。スクリプトで、resticが読み取る2つの環境変数 RESTIC_REPOSITORY と RESTIC_PASSWORD_FILE を設定するため、スクリプト内の各コマンドを短くできます。

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 check
sudo chmod 700 /usr/local/bin/restic-backup.sh

forget と check の行については、次の2つのセクションで説明します。次にスケジュールを設定します。スクリプトを実行する oneshot serviceと、毎晩03:00に起動するtimerを作成します。ここではcronの行よりtimerが適しています。実行ログを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を手動で1回実行し、動作を監視します。

sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer
sudo systemctl start restic-backup.service
sudo journalctl -u restic-backup.service -f

systemctl list-timers には、次回の実行時刻が表示されます。unit fileのペアは、手動で入力する代わりに生成することもできます。

ToolGenerate the backup service and timer

これら2つのファイルの基礎となる完全なパターンについては、カレンダー構文やserviceに設定できるhardeningディレクティブを含めて、VPSでプログラムをsystemd serviceとして実行する方法で説明しています。

復元するまでは、バックアップは噂にすぎない

この文を原則として扱ってください。毎晩バックアップジョブが正常終了していても、ジョブが実行されたことしか証明できません。データを復元できることは証明できません。この差を埋めるには、2つの確認が必要です。

1つ目は restic check です。これはスクリプトですでに毎晩実行しています。repository の構造と index を検証するため、バックアップホスト上のサイレントな破損を、復元当日ではなく翌晩に検出できます。月に1回、実データのランダムな10分の1をダウンロードして暗号学的に検証する、より詳細な形式を実行します。

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%

毎回ランダムに対象が選ばれるため、restic のドキュメントには、何度実行してもすべての pack file を網羅できる保証はないと明記されています。完全なダウンロードの費用をかけずに、毎月の実行で repository 全体を確認したい場合は、固定グループ形式を使います。ある月は --read-data-subset=1/10、翌月は 2/10、その次は 10/10 まで続けます。Restic は pack file をほぼ同じサイズの10グループに分け、n 番目のグループだけを確認します。そのため、10回で1サイクルとなり、各グループを1回ずつ読み取れます。

2つ目は、復元訓練です。上記と同じ root shell で、最新の snapshot から実際のディレクトリを1つ 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 を削除します。この訓練を毎月実施し、年に1回または2回は完全版も行います。最新の snapshot 全体を scratch VPS に復元し、アプリケーションがそこから実際に起動することを確認します。緊急時に動作させる必要が生じた日に備え、すでに何度も実施した手順にしておくことが重要です。

保持期間: forget と prune

ポリシーがないと、スナップショットは無期限に蓄積し、リポジトリだけが増え続けます。スクリプトの forget 行は、毎晩ポリシーを適用します。--keep-daily 7 は過去7日間について1日あたり1つのスナップショットを保持し、--keep-weekly 4 は4週間について週あたり1つ、--keep-monthly 6 は6か月について月あたり1つを保持します。ルールで保護されないものはすべて忘れられます。

forget だけではスナップショットの記録を削除するだけです。データチャンクは、何らかの処理で削除されるまでリポジトリに残ります。それを行うのが --prune です。残っているスナップショットから参照されていないチャンクを探して削除します。この時点で、実際にディスク容量が解放されます。Prune はリポジトリに対して実際の処理を行うため、大規模なリポジトリでは forget を毎晩、--prune を毎週実行する方法もあります。一般的な VPS のサイズであれば、毎晩の実行で問題ありません。

データベース: まずダンプし、その後ダンプをバックアップする

Restic はファイルを読み取りながらコピーします。一方、データベースは継続的にファイルへ書き込みます。書き込み途中のライブデータベースファイルを取得すると、書き込み前後のページが混在するため、復元時にデータベースが破損します。標準的な対策は、データベースエンジンに整合性のあるエクスポートをファイルへ作成させ、そのファイルを restic でバックアップすることです。

PostgreSQL の場合は、restic-backup.sh の先頭に、restic backup コマンドより前にダンプの行を追加します。また、ダンプディレクトリをバックアップ対象のパスに含めます。

mkdir -p /var/backups/db
sudo -u postgres pg_dump myapp | gzip > /var/backups/db/myapp.sql.gz

MariaDB と MySQL では、mysqldump が同じ役割を果たします。手順全体の例については、Nextcloud のバックアップセクションでメンテナンスモードを有効にし、Postgres をダンプして、ファイルを一貫したセットとしてコピーします。これは、restic が毎晩サーバーから持ち出すべき対象そのものです。SQLite も同じ考え方ですが、より簡単な方法を使います。Vaultwarden ガイドでは、数秒間コンテナを停止して db.sqlite3 のコールドコピーを作成し、そのアーカイブを restic でサーバー外へ転送します。

FAQ

restic のバックアップは暗号化されますか?

はい。常に暗号化されます。すべての restic repository は AES-256 で暗号化され、暗号化しないモードはありません。--insecure-no-password を渡さない限り、空のパスワードも拒否されます(restic 0.17.0 以降)。そのため、このガイドの手順で設定した repository では、すべてのコマンドでパスワードが必要です。repository を保存するマシンやプロバイダーが保持するのは暗号化された blob だけなので、バックアップホストが侵害されてもファイルは公開されません。代償は絶対的です。パスワードがなければ、誰もデータを復元できません。サーバーから離れた場所にもコピーを保管してください。

restic は増分バックアップを実行しますか?

restic の各 snapshot は完全バックアップとして動作しますが、必要なストレージ容量は増分方式と同程度です。Restic はファイルを chunk に分割し、repository に保存されていない chunk だけをアップロードします。そのため、毎晩の実行で転送されるのは、その日に変更された部分のおおよその量だけです。従来の増分方式と異なり、再生する chain はありません。どの snapshot からでも直接復元でき、古い snapshot を削除しても新しい snapshot が壊れることはありません。

restic のバックアップからファイルを復元するにはどうすればよいですか?

restic snapshots を実行して snapshot ID を確認し、restic restore <id> --target /some/empty/dir で復元します。一部だけを復元する場合は --include /path を追加します。ID の代わりに latest も使えます。Restic は元のディレクトリ構造を復元先の下に再作成するため、/etc/ssh を復元すると /some/empty/dir/etc/ssh に配置されます。必要になる前に練習してください。テストしていないバックアップは噂にすぎないためです。

restic backup はどのくらいの頻度で実行すべきですか?

サーバーでは毎晩が妥当な最低ラインです。deduplication により、各実行で前回から変更された chunk だけがアップロードされるため、低コストで実行できます。変更が速いデータや、1日分でも失うと大きな影響があるデータは、同じ timer パターンで数時間おきに実行できます。頻度は対策の一部にすぎません。restic check も定期的に実行し、月に1回は復元訓練も行ってください。検証のないスケジュールは、誤った安心感にすぎません。

restic repository のパスワードを失うとどうなりますか?

バックアップは復元できなくなります。Restic の暗号化には back door も reset 機能もありません。したがって、パスワードはバックアップそのものと同じくらい重要です。password manager と、バックアップ対象のサーバー以外の耐久性のある場所にコピーを保管してください。まだアクセスできる間であれば、restic key add により同じ repository に2つ目のパスワードを登録できます。これで予備のパスワードを用意できます。