# Docker Volume を GitHub に自動バックアップする：1Panel 向けの低コスト復旧構成

> Canonical: https://vxi.me/ja/posts/docker-volume-backup-github-sync/
> Locale: ja-jp
> Published: 2026-03-25
> Updated: 2026-03-25
> Markdown: https://vxi.me/ja/posts/docker-volume-backup-github-sync.md

自分で Docker サービスを運用していると、「コンテナが普通に動いているからバックアップは後でいい」と思いがちです。

本当に困るのは、コンテナそのものではなく、Volume のデータ、1Panel の設定、自分で変更したファイルを復元できるかどうかです。

私は最終的に、あまり重いバックアップ基盤にはせず、次の形にしました。

- Docker Volume と 1Panel の設定を毎日圧縮
- ローカルには直近 7 日分だけ残す
- 圧縮ファイルを GitHub の Private リポジトリへ同期する

豪華な構成ではありませんが、「直近の状態へすぐ戻せること」と「現在のサーバー以外にもコピーがあること」の二つを先に満たせます。

## なぜ最初から大規模なバックアップ基盤にしなかったのか

バックアップというと、オブジェクトストレージ、多世代管理、複数リージョン、専用バックアップ製品まで考えたくなります。

ただ、個人サーバーや小規模環境では、最初の問題は「設計が高度か」ではなく、**毎日確実に実行される仕組みがあるか**です。

私が優先したのは次の三つです。

- 手動操作を忘れても自動で動く
- 数日前の状態へ短時間で戻せる
- バックアップを同じサーバーだけに置かない

そこで、最初の構成を次のようにしました。

```text
Docker Volume / 1Panel 設定
        ↓
  docker-sync スクリプト
        ↓
backup_<timestamp> をローカル生成
        ↓
7 日より古いバックアップを削除
        ↓
圧縮ファイルを Git リポジトリへコピー
        ↓
Git LFS で .tar.gz を管理
        ↓
GitHub Private リポジトリへ push
```

非常に大きなデータには向きませんし、企業向け DR と同じものでもありません。

ただ、1Panel で数個のサイト、DB、アプリを動かしている程度なら、「ローカル 7 日 + GitHub Private」という組み合わせはかなり実用的です。

## まず GitHub への SSH 接続を手動で通す

ここは最初に一度だけ手動確認しておくのがおすすめです。

後で Shell や 1Panel のスケジュールタスクから実行しても、最後は「このサーバーから GitHub へ push できるか」に依存するからです。

```bash
ssh-keygen -t ed25519 -C "your-email@example.com"
cat ~/.ssh/id_ed25519.pub
ssh -T git@github.com
```

1 行目で鍵を作り、2 行目の公開鍵を GitHub の SSH keys に登録し、3 行目で接続を確認します。

ここを曖昧にしたまま自動化を作ると、後でスクリプトではなく SSH 認証で止まっていることがよくあります。

バックアップ用リポジトリは、業務コードとは分けた方が扱いやすいです。

```text
git@github.com:kuangyongjie/my-docker-backup.git
```

もちろん `Private` にします。

## バックアップ処理を docker-sync にまとめる

長く使う処理は、実行入口を固定した方が後で迷いません。

私は次のパスにまとめています。

```text
/usr/local/bin/docker-sync
```

スクリプト本体は次のような形です。

```bash
cat << 'EOF' > /usr/local/bin/docker-sync
#!/bin/bash

GITHUB_REPO="git@github.com:kuangyongjie/my-docker-backup.git"
GIT_USER_NAME="kuangyongjie"
GIT_USER_EMAIL="admin@local.host"
LOCAL_SYNC_PATH="/root/github_sync_space"

if ! command -v git-lfs &> /dev/null; then
    apt-get update && apt-get install -y git git-lfs && git lfs install
fi

if [ ! -d "$LOCAL_SYNC_PATH/.git" ]; then
    echo "--- 第一次运行：初始化同步目录 ---"
    rm -rf "$LOCAL_SYNC_PATH"
    GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no" git clone "$GITHUB_REPO" "$LOCAL_SYNC_PATH"
    cd "$LOCAL_SYNC_PATH"
    git lfs track "*.tar.gz" && git add .gitattributes
    git config user.name "$GIT_USER_NAME" && git config user.email "$GIT_USER_EMAIL"
    git commit -m "Init LFS"
else
    cd "$LOCAL_SYNC_PATH" && git pull origin main --rebase
fi

TAG=$(date +%Y%m%d_%H%M%S)
DIR="/root/docker_backups/backup_$TAG"
mkdir -p "$DIR"

echo "--- 正在抓取本地数据 ---"
for vol in $(docker volume ls -q); do
    tar -zcf "$DIR/vol_$vol.tar.gz" -C $(docker volume inspect $vol --format '{{.Mountpoint}}') .
done

tar -zcf "$DIR/1panel_apps_config.tar.gz" /opt/1panel/apps

echo "--- 正在清理本地 7 天前的旧备份 ---"
find /root/docker_backups/ -maxdepth 1 -name "backup_*" -type d -mtime +7 -exec rm -rf {} +

echo "--- 正在同步到 GitHub ---"
cp "$DIR"/*.tar.gz "$LOCAL_SYNC_PATH/"
cd "$LOCAL_SYNC_PATH"
git add .
git commit -m "Auto Backup $TAG"
git push origin main
echo "--- 完美！所有任务已完成 ---"
EOF

chmod +x /usr/local/bin/docker-sync
```

コード内のメッセージは元の運用スクリプトのままですが、処理は大きく六つです。

1. `git-lfs` がなければインストール
2. 初回だけバックアップリポジトリを clone し、LFS を初期化
3. Docker Volume を列挙して一つずつ `tar.gz` にする
4. `/opt/1panel/apps` も圧縮
5. 7 日より古いローカルバックアップを削除
6. 新しい圧縮ファイルを Git リポジトリへコピーして push

Shell、Git、Git LFS、Docker という既存の道具だけで作っているため、どこで失敗したかを追いやすいのが利点です。

注意したいのは次の二点です。

- 変更がない場合、`git commit` は失敗する
- `docker volume ls -q` はすべての Volume を対象にする

本番運用するなら、この二つは自分の環境に合わせて調整した方が安全です。

## 1Panel のスケジュールタスクには長いスクリプトを置かない

私は 1Panel 側には処理本体を書かず、呼び出しだけを置きます。

1. SSH で `docker-sync` を手動実行して成功させる
2. 1Panel でスケジュールタスクを作成
3. 種類は Shell script
4. 例えば毎日 03:00 に実行
5. 内容は次だけにする

```bash
docker-sync
```

こうすると失敗したとき、SSH で同じ `docker-sync` を実行すればログをそのまま確認できます。

管理画面に長いスクリプトを貼り付けていると、修正やデバッグの場所が分散しやすくなります。

## バックアップより復旧手順の方が重要

バックアップファイルを作るだけでは十分ではありません。

実際に障害が起きたときに必要なのは「どう戻すか」です。

サーバーを失った場合は、おおよそ次の流れになります。

1. 新しいサーバーでバックアップリポジトリを clone
2. 必要な時点の圧縮ファイルを選ぶ
3. 対応する `vol_xxx.tar.gz` を展開
4. `docker volume create` で Volume を作る
5. データをその Volume の `Mountpoint` に戻す
6. コンテナを再作成して Volume をマウント

1Panel の設定も同じで、設定ディレクトリを戻してから新しい環境へ再配置します。

ここで本当に困るのは tar の展開方法より、「どの Volume がどのサービスに対応していたか分からない」ことです。

そのため、Volume 名とサービスの対応は普段から記録しておく方が、復旧時の時間を大きく減らせます。

## 普段よく使う確認コマンド

| 目的 | コマンド |
| --- | --- |
| 今すぐ手動バックアップ | `docker-sync` |
| ローカルバックアップ容量 | `du -sh /root/docker_backups` |
| バックアップ一覧 | `ls -lh /root/docker_backups` |
| 最近の同期履歴 | `cd /root/github_sync_space && git log -n 5` |
| 保持日数の変更 | `nano /usr/local/bin/docker-sync` |

7 日を 14 日に変えるなら、`find ... -mtime +7` の値を変えるだけです。

## 失敗しやすいポイント

実際には圧縮処理より、次の部分の方が失敗しやすいです。

- サーバーから GitHub への SSH 認証が未完成
- `git-lfs` が未導入、または未初期化
- 初回同期ディレクトリの権限が合っていない
- 新しいファイルがなく、`git commit` が非ゼロ終了する

また、GitHub Private リポジトリは、小さめの DB Volume、設定ファイル、一般的なアプリデータには使いやすい一方、バックアップ量が大きくなり続ける用途には向きません。

容量が増えた段階で、オブジェクトストレージや専用バックアップ先へ移行する方が自然です。

この構成の位置付けは「最終的な企業向け DR」ではなく、**まず 1Panel / Docker サーバーに、毎日動く復旧可能なバックアップを持たせること**です。
