メインコンテンツへスキップ

🪣 BookStackバックアップをS3へ自動退避する手順書(cron・検証・通知つき)

🧭 はじめに

本手順書は、BookStack(Linux上)で作成したバックアップ(DB / uploads / .env)を 日次で生成し、S3へ自動転送し、成功/失敗を検証して通知するまでを、運用向けにまとめたもの。 S3は容量課金+リクエスト課金の従量制で、バックアップ用途ではコストが小さくなりやすい。 (Amazon Web Services, Inc.)


🧱 全体構成

✅ 目的の状態

  • Lightsail(BookStack)側で毎日バックアップを作る
  • その成果物をS3へ同期(アップロード)
  • 「今日のバックアップがS3に存在する」ことを確認して成否判定
  • 失敗したら通知(最小はログ、発展でWebhook/メール)

Windowsのサスペンド/デュアルブート問題を避けるため、サーバ側(常時稼働)を主系にする。


📌 前提

  • BookStackのパス: /var/www/BookStack
  • バックアップ保存先(ローカル): /var/backups/bookstack
  • DB名: bookstackdb
  • DBユーザー: bookstackuser

🧰 0. LightsailにAWS CLIを入れる

0-1. AWS CLIの存在確認

  • aws --version

無ければ導入(Ubuntu想定):

sudo apt update`
sudo apt install -y awscli`

aws s3 sync の挙動は公式ドキュメントに準拠。 :contentReference[oaicite:3]{index=3}


🔐 1. S3側の準備(バケット作成・安全設定)

🪣 1-1. バケットを作成

S3コンソールで、Lightsailと同じリージョン(例:ap-northeast-1=東京)にバケットを作成(例: my-bookstack-backup-<unique>)。

バケット名はグローバルで固有。 被ると作れない。

参考記事: # Lightsail(BookStack)バックアップ用 S3 + IAM 設定手順

🧩 1-2. バージョニングを有効化(強く推奨)

S3のバージョニングを有効にすると、上書きや削除に対して復元余地が増える。 (AWS ドキュメント)

  • コンソール: バケット → プロパティ → バージョニング → 有効化

“同期で上書きした” を戻せる可能性が上がる。

参考記事:🧩 S3バケットのバージョニング有効化手順(BookStackバックアップ向け)

🧊 1-3. ライフサイクル(任意・後でOK)

バックアップはアクセス頻度が低いので、一定日数後にGlacier系へ移行するとコストを下げやすい(ただし復元に時間がかかる場合がある)。 (AWS ドキュメント)

最初はライフサイクル無しで運用を安定させ、後から追加するのが安全。


🗃 2. BookStackバックアップを作る(ローカル)

手順記事:BookStack データのバックアップ方法

2-1. ローカルに生成される成果物(3点セット)

  • DB: bookstackdb_YYYY-MM-DD.sql.gz
  • uploads: bookstack_uploads_YYYY-MM-DD.tar.gz
  • env: bookstack.env.YYYY-MM-DD

.env が無いと復旧できない。 3点セット必須。


☁️ 3. S3へ同期アップロード(aws s3 sync)

3-1. S3の配置ルール(推奨)

S3側のプレフィックス例:

  • s3://<bucket>/bookstack/

3-2. まずは手動で同期(初回)

以下のように同期する(ローカル→S3)。aws s3 sync は差分同期が基本。 ([AWS ドキュメント][4])

コマンド例(実際はバケット名に置換):

  • aws s3 sync /var/backups/bookstack s3://<bucket>/bookstack/

✅ 正常確認(S3にあるか)

  • aws s3 ls s3://<bucket>/bookstack/

初回は “lsで見える” を確認してから自動化に進む。

3-3. 同期時の削除(慎重に)

--delete を付けると、ローカルに無いファイルがS3から消える(ミラー運用)。 ([AWS ドキュメント][5])

初期は --delete を付けない。 運用が固まってから。


方針:5-2に「そのまま置ける」スクリプト例を入れて、(生成→sync→検証→ログ) が機械的に回る形にする。生成部分は既存スクリプト呼び出し前提にして、必要なら差し替えできるようにしておく。


✅ 4. “正しく取れた” を機械的に検証する(最重要)

4-1. 今日の日付のバックアップがローカルにあるか

  • ls -lh /var/backups/bookstack | grep "$(date +%F)"

期待:

  • bookstackdb_YYYY-MM-DD.sql.gz
  • bookstack_uploads_YYYY-MM-DD.tar.gz
  • bookstack.env.YYYY-MM-DD

4-2. 今日の日付のバックアップがS3にあるか

  • aws s3 ls s3://<bucket>/bookstack/ | grep "$(date +%F)"

“コマンドが成功した” ではなく “成果物が存在する” を合否にする。


⏰ 5. cronで自動化(生成→S3同期→検証→通知)

5-1. 認証情報の置き場所(安全寄り)

方式A(簡単・妥当): rootのみ読めるファイルに環境変数として保存

例: /root/.aws/bookstack_env

  • 所有者root、権限600

中身(例):

AWS_ACCESS_KEY_ID=書き換える
AWS_SECRET_ACCESS_KEY=書き換える
AWS_DEFAULT_REGION=ap-northeast-1

権限:

sudo chmod 600 /root/.aws/bookstack_env

アクセスキーを一般ユーザーが読める場所に置かない。


5-2. 実行スクリプト作成(サンプル付き)

例(root実行): /usr/local/bin/bookstack_backup_to_s3.sh

#!/usr/bin/env bash
set -euo pipefail
set -a
. /var/www/BookStack/.env
set +a

set -a
. /root/.aws/bookstack_env
set +a


# ===== 設定(ここだけ見直せばOK)=====
BACKUP_DIR='/var/backups/bookstack'
BUCKET='bookstack-backup-xxxx'          # ←自分のバケット名に置換
S3_PREFIX='bookstack'                   # ←S3上のprefix。未作成でもOK(自動で生える)
S3_URI="s3://${BUCKET}/${S3_PREFIX}/"

# ローカル生成(既存スクリプトを呼ぶ想定)
# 例: /usr/local/bin/bookstack_local_backup.sh が
#   - bookstackdb_YYYY-MM-DD.sql.gz
#   - bookstack_uploads_YYYY-MM-DD.tar.gz
#   - bookstack.env.YYYY-MM-DD
# を /var/backups/bookstack に作る、という前提
LOCAL_BACKUP_SCRIPT='/usr/local/bin/bookstack_local_backup.sh'

# ログ(loggerに流すだけでもよいが、ファイルにも残すと便利)
LOG_FILE='/var/log/bookstack_backup_to_s3.log'

# ===== ここから処理 =====
today="$(date +%F)"

log() {
  # cronから見ても追えるように、logger + ファイル両方へ
  logger -t bookstack-backup "$*"
  printf '%s %s\n' "$(date '+%F %T')" "$*" >> "$LOG_FILE"
}

fail() {
  log "ERROR: $*"
  exit 1
}

log "START: today=${today}"

# 0) 事前条件
[ -d "$BACKUP_DIR" ] || fail "backup dir not found: ${BACKUP_DIR}"
[ -x "$LOCAL_BACKUP_SCRIPT" ] || fail "local backup script not executable: ${LOCAL_BACKUP_SCRIPT}"

# 1) ローカルバックアップ生成
log "RUN: local backup script: ${LOCAL_BACKUP_SCRIPT}"
"$LOCAL_BACKUP_SCRIPT"

# 2) “今日の成果物がローカルにあるか”を検証
need_local=(
  "${BACKUP_DIR}/bookstackdb_${today}.sql.gz"
  "${BACKUP_DIR}/bookstack_uploads_${today}.tar.gz"
  "${BACKUP_DIR}/bookstack.env.${today}"
)

for f in "${need_local[@]}"; do
  [ -s "$f" ] || fail "missing or empty local file: $f"
done
log "OK: local artifacts exist"

# 3) S3へ同期(差分同期)
log "RUN: aws s3 sync -> ${S3_URI}"
aws s3 sync "$BACKUP_DIR" "$S3_URI" --region "${AWS_DEFAULT_REGION:-ap-northeast-1}"

# 4) “今日の成果物がS3にあるか”を検証(存在を合否にする)
# aws s3 ls の出力は末尾がファイル名になるので、それで一致確認する
need_s3=(
  "bookstackdb_${today}.sql.gz"
  "bookstack_uploads_${today}.tar.gz"
  "bookstack.env.${today}"
)

s3_list="$(aws s3 ls "$S3_URI" --region "${AWS_DEFAULT_REGION:-ap-northeast-1}")"

for name in "${need_s3[@]}"; do
  echo "$s3_list" | grep -Fq "$name" || fail "missing on S3: ${S3_URI}${name}"
done
log "OK: S3 artifacts exist"

log "SUCCESS"
exit 0

編集して保存したら、権限をつけておく

sudo chmod 755 /usr/local/bin/bookstack_backup_to_s3.sh

ローカル生成を「既存スクリプト呼び出し」にしている。
手元の実装に合わせて LOCAL_BACKUP_SCRIPT だけ差し替えれば動く。

空ファイルも失敗扱いにしている。
[ -s file ] で「存在する」だけでなく「サイズ>0」も検査。


5-3. cron登録

sudo crontab -e

例(毎日03:30のローカル生成も今回のスクリプトで処理する:

30 3 * * * . /root/.aws/bookstack_env && /usr/local/bin/bookstack_backup_to_s3.sh

cronの時刻はOSのタイムゾーン依存。 date でJST/UTCを固定してから設定する。

---

🔔 6. 通知(最小→発展)

6-1. 最小(まずこれ)

スクリプト内で失敗時に:

  • logger -t bookstack-backup "FAILED: ..."

確認:

  • sudo journalctl -t bookstack-backup -n 50

6-2. 発展(Webhook)

Slack/Discord等へ投げる(curl)方式が扱いやすい。 詳細は下記リンク先記事に記載

記事:🔔 BookStackバックアップ失敗時にSlackへ通知する設定手順(Incoming Webhook)

通知は “失敗時のみ” にするのが運用ノイズを減らす。


🧪 7. 復旧リハーサル(最短の動作確認)

本番を壊さずに確認したい場合は「別DB名」でリストアテストをする。

本番DBに上書きリストアしない。 必ず別DBで試す。


📎 8. コストの要点

S3は保存容量とリクエスト等の従量課金。バックアップ程度の容量では月コストが小さくなりやすい。 (Amazon Web Services, Inc.)

この3点が決まると、スクリプトとcronを“そのままコピペで動く形”に確定できる。