Nextcloudのアップデートは、それほど頻繁に行う作業ではありません。しかし、だからこそ毎回「あれ、このコマンド実行したっけ?」となりがちです。

筆者自身も、メンテナンスモードの切り替えやデータベースのバックアップなど、手順を一つひとつ確認しながら作業していました。そこで「毎回やることはスクリプトに任せよう」と考え、更新前のバックアップから updater.phar の実行までを一通り補助する Bash スクリプトを作成しました。

今回は、そのスクリプトの仕組みを紹介します。

動作環境

動作を想定している環境は以下の通りです。

  • Ubuntu 20.04 LTS / 22.04 LTS / 24.04 LTS
  • MySQL または MariaDB
  • PHP / Bash
  • root 権限(sudo)
  • Apache実行ユーザ: www-data

Ubuntu系で一般的な構成であれば、そのまま利用できるようになっています。

スクリプト本体

最初に環境に合わせて、スクリプト冒頭の変数だけ変更します。

NC_DIR="/var/www/html/nextcloud"
NC_USER="www-data"

DB_NAME="nextcloud"
DB_USER="nextcloud"

BACKUP_DIR="/var/backups/nextcloud"

続いて実行権限を付与します。

chmod +x nextcloud_update.sh

最後に sudo で起動します。

sudo ./nextcloud_update.sh

途中では

  • データベースパスワード
  • 各ステップへ進むかどうか

を対話形式で確認します。

確認しながら進められるので、完全自動化というより「操作ミスを減らすための半自動化」という位置付けです。

Bashで自動化というと「全部ワンコマンドで終わらせる」方向へ行きがちですが、個人的にはサーバーの更新作業はそこまで割り切らない方が安心だと考えています。

特に Nextcloud のアップデートは、途中で状況を確認したくなる場面も少なくありません。

そのため、このスクリプトでは各工程ごとに確認を挟み、「人が判断するところ」と「機械に任せるところ」を分ける構成にしました。

毎回同じ手順を思い出しながら実行するより、確認すべきポイントだけに集中できるので、更新作業はかなり気楽になります。

以下がスクリプト全文です。

#!/bin/bash
# ==============================================================================
# Nextcloud Update & Database Backup Script
# ==============================================================================

set -euo pipefail

# --- 変数定義(環境に合わせて変更してください) ---
NC_DIR="/var/www/html/nextcloud"
NC_USER="www-data"
PHP_BIN="php"

# DB設定
DB_NAME="nextcloud"
DB_USER="nextcloud"
DB_HOST="localhost"
BACKUP_DIR="/var/backups/nextcloud"

# --- ユーティリティ関数 ---
log_info() {
    echo -e "\n\e[34m[INFO]\e[0m $(date '+%Y-%m-%d %H:%M:%S') - $1"
}

log_warn() {
    echo -e "\e[33m[WARN]\e[0m $(date '+%Y-%m-%d %H:%M:%S') - $1"
}

log_error() {
    echo -e "\e[31m[ERROR]\e[0m $(date '+%Y-%m-%d %H:%M:%S') - $1" >&2
}

ask_proceed() {
    local prompt_msg="${1:-次のステップに進みますか?}"
    while true; do
        read -rp "👉 ${prompt_msg} [y/N]: " answer
        case "${answer}" in
            [yY]|[yY][eE][sS])
                return 0
                ;;
            [nN]|[nN][oS]|"")
                log_warn "ユーザーにより処理が中断されました。"
                exit 1
                ;;
            *)
                echo "'y' または 'n' を入力してください。"
                ;;
        esac
    done
}

# --- 前提チェック ---
# root 権限チェック(sudo 経由を含む root 以外は即時終了)
if [ "${EUID}" -ne 0 ]; then
    log_error "このスクリプトは root 権限で実行する必要があります。(例: sudo $0)"
    exit 1
fi

log_info "前提条件と実行権限を確認しています..."

if [ ! -d "${NC_DIR}" ]; then
    log_error "Nextcloudディレクトリが見つかりません: ${NC_DIR}"
    exit 1
fi

if ! command -v mysqldump &> /dev/null && ! command -v mariadb-dump &> /dev/null; then
    log_error "mysqldump または mariadb-dump コマンドが見つかりません。"
    exit 1
fi

mkdir -p "${BACKUP_DIR}"

# ==============================================================================
# Step 1: データベースパスワードの取得と認証確認
# ==============================================================================
log_info "【Step 1】データベース情報の入力"
read -rsp "🔑 DBユーザー (${DB_USER}) のパスワードを入力してください: " DB_PASS
echo

# パスワードの疎通確認
if ! MYSQL_PWD="${DB_PASS}" mysqladmin -h "${DB_HOST}" -u "${DB_USER}" ping &>/dev/null; then
    log_error "データベースへの接続に失敗しました。パスワードまたはホスト設定を確認してください。"
    exit 1
fi
log_info "データベースへの接続を確認しました。"

# ==============================================================================
# Step 2: メンテナンスモードの有効化
# ==============================================================================
log_info "【Step 2】メンテナンスモードの有効化"
ask_proceed "メンテナンスモードをONにしますか?"

sudo -u "${NC_USER}" "${PHP_BIN}" "${NC_DIR}/occ" maintenance:mode --on
log_info "メンテナンスモードを有効化しました。"

# スクリプト異常終了時にメンテナンスモード解除を促すトラップを設定
cleanup() {
    if [ $? -ne 0 ]; then
        log_warn "エラーが発生したため処理を停止しました。"
        log_warn "必要に応じてメンテナンスモードを解除してください:"
        log_warn "sudo -u ${NC_USER} ${PHP_BIN} ${NC_DIR}/occ maintenance:mode --off"
    fi
}
trap cleanup EXIT

# ==============================================================================
# Step 3: DBバックアップの実行(インラインパスワード処理)
# ==============================================================================
log_info "【Step 3】DBバックアップの実行"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/${DB_NAME}_backup_${TIMESTAMP}.sql.gz"

ask_proceed "DBバックアップを開始しますか? (保存先: ${BACKUP_FILE})"

# dumpコマンドの判定(MariaDB/MySQL互換)
DUMP_CMD="mysqldump"
command -v mariadb-dump &> /dev/null && DUMP_CMD="mariadb-dump"

# MYSQL_PWD環境変数を使用することで、psコマンド等へのパスワード露出を防止
if MYSQL_PWD="${DB_PASS}" ${DUMP_CMD} -h "${DB_HOST}" -u "${DB_USER}" --single-transaction --quick "${DB_NAME}" | gzip > "${BACKUP_FILE}"; then
    log_info "バックアップが正常に完了しました: ${BACKUP_FILE}"
else
    log_error "バックアップ処理中にエラーが発生しました。"
    exit 1
fi

# ==============================================================================
# Step 4: updater.phar によるアップデート実行
# ==============================================================================
log_info "【Step 4】updater.phar の実行"
log_warn "updater自体の対話プロンプトが表示されます。画面の指示に従って操作してください。"
ask_proceed "updater.phar を起動しますか?"

cd "${NC_DIR}"
sudo -u "${NC_USER}" "${PHP_BIN}" "${NC_DIR}/updater/updater.phar"

# ==============================================================================
# Step 5: 状態の確認とメンテナンスモードの調整
# ==============================================================================
log_info "【Step 5】最終確認とメンテナンスモードの解除"

current_mode=$(sudo -u "${NC_USER}" "${PHP_BIN}" "${NC_DIR}/occ" maintenance:mode)
echo "現在の状態: ${current_mode}"

if echo "${current_mode}" | grep -q "enabled"; then
    read -rp "👉 メンテナンスモードをOFF(解除)にしますか? [y/N]: " unfreeze
    if [[ "${unfreeze}" =~ ^[yY]$ ]]; then
        sudo -u "${NC_USER}" "${PHP_BIN}" "${NC_DIR}/occ" maintenance:mode --off
        log_info "メンテナンスモードを解除しました。"
    else
        log_warn "メンテナンスモードを維持したまま終了します。"
    fi
fi

# ==============================================================================
# Step 6: バージョン確認
# ==============================================================================
log_info "【Step 6】バージョンの確認"
sudo -u "${NC_USER}" "${PHP_BIN}" "${NC_DIR}/occ" status

log_info "すべての更新手順が完了しました。"
trap - EXIT

このスクリプトで重視したこと

単にコマンドを並べるだけではなく、「途中で失敗しても致命傷にならないこと」を意識しています。

set -euo pipefail

Bashスクリプトでは定番ですが、非常に重要です。

  • コマンドが失敗した
  • 未定義変数を参照した
  • パイプラインの途中でエラーが発生した

このような場合は、その時点で処理を停止します。

「バックアップに失敗したのに、そのままアップデートが続く」といった事故を防ぐためです。

root権限の確認

EUID を利用して、root 権限以外では実行できないようにしています。

sudo ./nextcloud_update.sh

以外の実行方法では即座に終了します。

パスワードをコマンドラインへ残さない

昔ながらの

mysqldump -pPASSWORD

という書き方は、ps コマンドなどからパスワードが見えてしまいます。

そこで本スクリプトでは

MYSQL_PWD

環境変数を利用しています。

賛否のある方法ではありますが、少なくともコマンドライン引数へ平文パスワードを残すより安全です。(なお筆者は別スクリプトを利用していますが、それはまた別のお話)

trapによる復旧案内

一番怖いのは

  • メンテナンスモードON
  • エラー発生
  • そのまま終了

というパターンです。

そこで trap cleanup EXIT を利用し、異常終了した場合は

occ maintenance:mode --off

を案内するようにしています。

「解除方法を忘れた」という事態を防ぐための保険です。

実行の流れ

処理は次の順番で進みます。

1. 前提条件の確認

まずは

  • root権限
  • Nextcloudディレクトリ
  • mysqldump または mariadb-dump

の存在を確認します。

ここで問題があれば、それ以上処理は進みません。

2. データベース接続の確認

パスワードを入力したあと、

mysqladmin ping

で接続テストを行います。

アップデート直前になって認証エラーが発覚するより、最初に確認してしまった方が安心です。

3. メンテナンスモードを有効化

occ maintenance:mode --on

を実行します。この操作の前にも確認プロンプトを表示するため、誤操作を防げます。

4. データベースをバックアップ

バックアップには

  • mysqldump
  • mariadb-dump

のどちらか利用可能な方を自動で選択します。

取得したSQLは、そのまま gzip 圧縮して保存します。

nextcloud_backup_20260821_153000.sql.gz

のように日時付きファイルになるため、履歴も管理しやすくしています。

5. updater.phar を起動

Nextcloud標準の

updater.phar

を実行します。ここから先はNextcloud側の対話画面になりますので、画面の案内に従って更新を進めます。

6. 最終確認

更新後は

  • メンテナンスモードの状態確認
  • 必要ならOFFへ変更
  • occ status

まで実行します。最後に現在のバージョンが表示されれば、一通りの更新は完了です。

さっくりとしたまとめ

サーバー運用では「バックアップを取ってから更新する」は当たり前ですが、人間は当たり前ほど慣れてしまうものです。だから私は、忘れない仕組みを作るようにしています。

「運用者は呼吸するかのようにできる。だが、第三者に任せられるか? すっごく焦ってるときに緊急アップデートをした時は?」のためにも、確実に動く仕組みは大切というお話でした。