Vaultwardenのバックアップ&復元スクリプト

Vaultwardenにはインポート/エクスポート機能があるので基本的に移行時には標準機能を利用し、念の為のバックアップとします。ただデータの取り扱いには十分注意を。

スクリプトの主な機能

バックアップ (backup)

  • コンテナを一時停止してからアーカイブを作成(データ整合性確保)
  • data/docker-compose.yml を一緒にバックアップ
  • SHA256チェックサムファイルも自動生成
  • バックアップ後にコンテナを自動再起動
  • 7日以上前のバックアップは削除

復元 (restore)

  • ファイル指定省略時は最新バックアップを自動選択
  • SHA256チェックサムで破損検証
  • 復元前に現在の data/data_before_restore_日時/ に退避(ロールバック可能)
  • 確認プロンプトあり(誤操作防止)

一覧 (list) / クリーンアップ (clean)

  • バックアップファイルのサイズ・日時を一覧表示
  • 指定日数より古いファイルを対話的に削除
スクリプトファイル:/opt/lxd-data/script/vaultwarden
バックアップ保存先:/opt/lxd-data/vaultwarden
コンテナ内で実行

バックアップスクリプト

sudo mkdir -p /opt/lxd-data/script/vaultwarden
cd /opt/lxd-data/script/vaultwarden
sudo nano vaultwarden-backup.sh
# 下記スクリプトを貼り付け
# 実行権限を付与
chmod +x vaultwarden-backup.sh
#!/bin/bash
set -euo pipefail
# =============================================================
#  Vaultwarden バックアップ / 復元スクリプト
#
#  使い方:
#    バックアップ : sudo bash vaultwarden-backup.sh backup
#    復元        : sudo bash vaultwarden-backup.sh restore [バックアップファイルパス]
#    一覧表示    : sudo bash vaultwarden-backup.sh list
#
#  バックアップ保存先: /opt/lxd-data/vaultwarden/
#  バックアップ対象  : /opt/docker/vaultwarden/data/
# =============================================================

VAULTWARDEN_DIR="/opt/docker/vaultwarden"
BACKUP_DIR="/opt/lxd-data/vaultwarden"
CONTAINER_NAME="vaultwarden"
KEEP_DAYS=7   # バックアップの保持日数

# ── カラー出力 ────────────────────────────────────
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'

# ── ヘルパー関数 ──────────────────────────────────
info()    { echo -e "${GREEN}[INFO]${NC}  $*"; }
warn()    { echo -e "${YELLOW}[WARN]${NC}  $*"; }
error()   { echo -e "${RED}[ERROR]${NC} $*" >&2; }
section() { echo -e "\n${CYAN}==> $*${NC}"; }

print_banner() {
    echo ""
    echo "════════════════════════════════════════"
    echo "  Vaultwarden バックアップ管理スクリプト"
    echo "════════════════════════════════════════"
    echo ""
}

usage() {
    print_banner
    echo "使い方:"
    echo "  $0 backup              # バックアップを作成"
    echo "  $0 restore [ファイル]  # バックアップから復元"
    echo "                           ファイル省略時は最新を使用"
    echo "  $0 list                # バックアップ一覧を表示"
    echo "  $0 clean               # 古いバックアップを削除 (${KEEP_DAYS}日以上)"
    echo ""
    echo "保存先: ${BACKUP_DIR}/"
    echo ""
}

check_root() {
    if [ "$(id -u)" -ne 0 ]; then
        error "このスクリプトはrootまたはsudoで実行してください"
        exit 1
    fi
}

check_vaultwarden_dir() {
    if [ ! -d "${VAULTWARDEN_DIR}/data" ]; then
        error "Vaultwardenデータディレクトリが見つかりません: ${VAULTWARDEN_DIR}/data"
        exit 1
    fi
}

# ── バックアップ関数 ──────────────────────────────
do_backup() {
    print_banner
    check_root
    check_vaultwarden_dir

    local TIMESTAMP
    TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
    local BACKUP_FILE="${BACKUP_DIR}/vaultwarden_${TIMESTAMP}.tar.gz"

    section "[1/4] バックアップディレクトリを準備..."
    mkdir -p "${BACKUP_DIR}"
    info "保存先: ${BACKUP_DIR}"

    section "[2/4] Vaultwardenコンテナを一時停止..."
    if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
        docker stop "${CONTAINER_NAME}"
        info "コンテナを停止しました"
        local CONTAINER_WAS_RUNNING=true
    else
        warn "コンテナは既に停止しています"
        local CONTAINER_WAS_RUNNING=false
    fi

    section "[3/4] データをアーカイブ..."
    # docker-compose.yml も含めてバックアップ
    tar -czf "${BACKUP_FILE}" \
        -C "${VAULTWARDEN_DIR}" \
        data \
        docker-compose.yml 2>/dev/null || {
            # docker-compose.yml が無い場合は data のみ
            tar -czf "${BACKUP_FILE}" \
                -C "${VAULTWARDEN_DIR}" \
                data
        }

    local BACKUP_SIZE
    BACKUP_SIZE=$(du -sh "${BACKUP_FILE}" | cut -f1)
    info "アーカイブ作成完了: ${BACKUP_FILE} (${BACKUP_SIZE})"

    # チェックサム生成
    sha256sum "${BACKUP_FILE}" > "${BACKUP_FILE}.sha256"
    info "チェックサム: ${BACKUP_FILE}.sha256"

    section "[4/4] Vaultwardenコンテナを再起動..."
    if [ "${CONTAINER_WAS_RUNNING}" = true ]; then
        cd "${VAULTWARDEN_DIR}"
        docker compose up -d
        info "コンテナを再起動しました"
    else
        warn "コンテナは停止したままです (元々停止していたため)"
    fi

    # 古いバックアップを自動削除 (cronでの自動実行対応)
    local OLD_FILES
    OLD_FILES=$(find "${BACKUP_DIR}" -name "vaultwarden_*.tar.gz" \
        -mtime +${KEEP_DAYS} 2>/dev/null || true)
    if [ -n "${OLD_FILES}" ]; then
        local OLD_COUNT
        OLD_COUNT=$(echo "${OLD_FILES}" | wc -l)
        echo "${OLD_FILES}" | while read -r F; do
            rm -f "${F}" "${F}.sha256"
            info "古いバックアップを削除: $(basename "${F}")"
        done
        info "${OLD_COUNT} 件の古いバックアップを削除しました (${KEEP_DAYS}日以上前)"
    fi

    echo ""
    echo "════════════════════════════════════════"
    echo -e "  ${GREEN}✅  バックアップ完了!${NC}"
    echo "════════════════════════════════════════"
    echo ""
    echo "  📦 ファイル : ${BACKUP_FILE}"
    echo "  📏 サイズ   : ${BACKUP_SIZE}"
    echo "  📅 日時     : $(date '+%Y-%m-%d %H:%M:%S')"
    echo ""
}

# ── 復元関数 ──────────────────────────────────────
do_restore() {
    print_banner
    check_root

    local BACKUP_FILE="${1:-}"

    # ファイル指定がなければ最新を使用
    if [ -z "${BACKUP_FILE}" ]; then
        section "最新バックアップを検索..."
        BACKUP_FILE=$(find "${BACKUP_DIR}" -name "vaultwarden_*.tar.gz" \
            -printf '%T@ %p\n' 2>/dev/null | sort -n | tail -1 | cut -d' ' -f2-)
        if [ -z "${BACKUP_FILE}" ]; then
            error "バックアップファイルが見つかりません: ${BACKUP_DIR}"
            exit 1
        fi
        info "最新バックアップ: ${BACKUP_FILE}"
    fi

    # ファイル存在確認
    if [ ! -f "${BACKUP_FILE}" ]; then
        error "バックアップファイルが見つかりません: ${BACKUP_FILE}"
        exit 1
    fi

    # チェックサム検証
    local SHA256_FILE="${BACKUP_FILE}.sha256"
    if [ -f "${SHA256_FILE}" ]; then
        section "チェックサムを検証..."
        if sha256sum -c "${SHA256_FILE}" --quiet 2>/dev/null; then
            info "チェックサム OK"
        else
            error "チェックサム検証に失敗しました!ファイルが破損している可能性があります"
            exit 1
        fi
    else
        warn "チェックサムファイルが見つかりません。スキップします"
    fi

    # アーカイブ内容確認
    section "バックアップ内容を確認..."
    echo ""
    tar -tzf "${BACKUP_FILE}" | head -20
    echo ""

    # 確認プロンプト
    local BACKUP_SIZE
    BACKUP_SIZE=$(du -sh "${BACKUP_FILE}" | cut -f1)
    warn "以下の内容で復元します:"
    echo "  バックアップファイル : ${BACKUP_FILE} (${BACKUP_SIZE})"
    echo "  復元先               : ${VAULTWARDEN_DIR}/data/"
    echo ""
    read -rp "  現在のデータが上書きされます。続行しますか? [y/N]: " CONFIRM
    if [[ ! "${CONFIRM}" =~ ^[Yy]$ ]]; then
        warn "復元をキャンセルしました"
        exit 0
    fi

    section "[1/4] Vaultwardenコンテナを停止..."
    if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
        docker stop "${CONTAINER_NAME}"
        info "コンテナを停止しました"
    else
        warn "コンテナは既に停止しています"
    fi

    section "[2/4] 現在のデータをバックアップ (退避)..."
    local ESCAPE_TIMESTAMP
    ESCAPE_TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
    local ESCAPE_DIR="${VAULTWARDEN_DIR}/data_before_restore_${ESCAPE_TIMESTAMP}"
    if [ -d "${VAULTWARDEN_DIR}/data" ]; then
        mv "${VAULTWARDEN_DIR}/data" "${ESCAPE_DIR}"
        info "退避先: ${ESCAPE_DIR}"
    fi

    section "[3/4] バックアップを展開..."
    mkdir -p "${VAULTWARDEN_DIR}"
    tar -xzf "${BACKUP_FILE}" -C "${VAULTWARDEN_DIR}"
    info "展開完了"

    section "[4/4] Vaultwardenコンテナを起動..."
    cd "${VAULTWARDEN_DIR}"
    docker compose up -d
    info "コンテナを起動しました"

    echo ""
    echo "════════════════════════════════════════"
    echo -e "  ${GREEN}✅  復元完了!${NC}"
    echo "════════════════════════════════════════"
    echo ""
    echo "  📦 復元元  : ${BACKUP_FILE}"
    echo "  🗂️  退避先  : ${ESCAPE_DIR}"
    echo "  📅 日時    : $(date '+%Y-%m-%d %H:%M:%S')"
    echo ""
    warn "復元後の動作確認が取れたら、退避データを削除してください:"
    echo "    rm -rf ${ESCAPE_DIR}"
    echo ""
}

# ── 一覧表示関数 ──────────────────────────────────
do_list() {
    print_banner

    if [ ! -d "${BACKUP_DIR}" ]; then
        warn "バックアップディレクトリが存在しません: ${BACKUP_DIR}"
        exit 0
    fi

    local FILES
    FILES=$(find "${BACKUP_DIR}" -name "vaultwarden_*.tar.gz" \
        -printf '%T@ %p\n' 2>/dev/null | sort -rn | cut -d' ' -f2-)

    if [ -z "${FILES}" ]; then
        warn "バックアップファイルが見つかりません"
        exit 0
    fi

    echo "バックアップ一覧: ${BACKUP_DIR}"
    echo ""
    printf "  %-50s  %8s  %s\n" "ファイル名" "サイズ" "作成日時"
    echo "  $(printf '─%.0s' {1..75})"

    while IFS= read -r FILE; do
        local BASENAME SIZE MTIME
        BASENAME=$(basename "${FILE}")
        SIZE=$(du -sh "${FILE}" | cut -f1)
        MTIME=$(stat -c '%y' "${FILE}" | cut -d'.' -f1)
        printf "  %-50s  %8s  %s\n" "${BASENAME}" "${SIZE}" "${MTIME}"
    done <<< "${FILES}"

    echo ""
    local TOTAL_COUNT TOTAL_SIZE
    TOTAL_COUNT=$(echo "${FILES}" | wc -l)
    TOTAL_SIZE=$(du -sh "${BACKUP_DIR}" | cut -f1)
    echo "  合計: ${TOTAL_COUNT} 件 / ${TOTAL_SIZE}"
    echo ""
}

# ── 古いバックアップ削除関数 ──────────────────────
do_clean() {
    print_banner
    check_root

    local OLD_FILES
    OLD_FILES=$(find "${BACKUP_DIR}" -name "vaultwarden_*.tar.gz" \
        -mtime +${KEEP_DAYS} 2>/dev/null || true)

    if [ -z "${OLD_FILES}" ]; then
        info "${KEEP_DAYS}日以上前のバックアップはありません"
        exit 0
    fi

    warn "以下のファイルを削除します (${KEEP_DAYS}日以上前):"
    echo "${OLD_FILES}" | while read -r F; do
        echo "  - $(basename "${F}") ($(du -sh "${F}" | cut -f1))"
    done
    echo ""
    read -rp "  削除しますか? [y/N]: " CONFIRM
    if [[ ! "${CONFIRM}" =~ ^[Yy]$ ]]; then
        warn "キャンセルしました"
        exit 0
    fi

    echo "${OLD_FILES}" | while read -r F; do
        rm -f "${F}" "${F}.sha256"
        info "削除: $(basename "${F}")"
    done

    info "クリーンアップ完了"
    echo ""
}

# ── メイン ────────────────────────────────────────
COMMAND="${1:-}"

case "${COMMAND}" in
    backup)
        do_backup
        ;;
    restore)
        do_restore "${2:-}"
        ;;
    list)
        do_list
        ;;
    clean)
        do_clean
        ;;
    *)
        usage
        exit 1
        ;;
esac

バックアップ作成

sudo bash vaultwarden-backup.sh backup

バックアップ一覧表示

sudo bash vaultwarden-backup.sh list

最新バックアップから復元

sudo bash vaultwarden-backup.sh restore

特定のバックアップから復元

sudo bash vaultwarden-backup.sh restore /opt/lxd-data/vaultwarden/vaultwarden_20250101_120000.tar.gz

古いバックアップを削除(自動で7日以上前を削除しています)

sudo bash vaultwarden-backup.sh clean

cronで自動バックアップ

crontab -e

crontab -e で追加(毎日午前2時に実行)

# Vaultwarden
0 2 * * * bash /opt/lxd-data/script/vaultwarden/vaultwarden-backup.sh backup >> /var/log/vaultwarden-backup.log 2>&1

タイトルとURLをコピーしました