コンテンツにスキップ

運用ガイド

設置後の運用(更新・バックアップ・復元・点検)の手順です。設置手順は同梱の インストールガイド、利用者向けの使い方は ユーザーガイド を 参照してください。

このガイドのコマンドは、すべてインストール先(.env のあるディレクトリ)で実行します。

php artisan … は Web サーバーと同じユーザーで実行してください(このガイドでは www-data を例にしています)。root や個人アカウントで実行すると、作られたキャッシュ (bootstrap/cache/*.php)とログがそのユーザーの所有になり、以後 Web サーバーから 書き換えられなくなります。更新の「キャッシュの作り直し」が失敗する原因の第一位です。 詳しくは インストールガイド の §3-7 を参照してください。

内容
§1 日々の動き cron が自動で回すこと
§2 設定を変更したとき キャッシュの作り直しとワーカー再起動
§3 点検 システム状態画面と connection:doctor
§4 更新 管理画面 / コマンド / 手動。失敗したときは §4-4
§5 バックアップ 何が取られるか・取り扱い・外部への二重化・復元テスト
§6 復元 バックアップから戻す手順
§7 マイグレーション 手動で操作する場合の参考
§8 利用者・セッションの運用 無効化・メールアドレス変更・2FA 解除
§9 ログ 置き場所と LOG_LEVEL
§10 困ったときは 症状ごとの参照先

cron(php artisan schedule:run)が動いていれば、通知・整理・バックアップ・更新確認はすべて 自動で回ります。何がいつ動くかの一覧は インストールガイド の §6-1 に あります(時刻はすべて APP_TIMEZONE に従います)。

運用の側から見て、押さえておくとよいのは次の3点です。

  • 01:30 のバックアップが、1日1本の生命線です。 保持期間を過ぎたものの整理(01:00)より 後に走るので、その夜の控えが整理に巻き込まれることはありません。
  • バックアップの異常を人に知らせるのは 04:00 の点検だけです(古すぎる・容量超過があれば 管理者へメール)。裏を返すと、この通知が来なくなったときは cron ごと止まっています。
  • 毎分の稼働記録が、止まったことを検出できる唯一の痕跡です。 更新されなくなった時点で、 自己診断とシステム状態画面が「スケジューラが停止」と報告します。

管理者がやることは、月に一度ほど管理画面の「システム状態」を開くことだけです。 異常はメールで届きますが、cron 自体が止まるとメールも飛ばなくなります(止まったことを 知らせる仕組みも cron の上で動いているため)。この画面は開くたびに「最後のバックアップからの 経過日数」を評価するので、静かな停止に気づく最後の砦になります。


.envconfig/ を変更したら、必ず次の2つを行ってください。

Terminal window
sudo -u www-data php artisan optimize # 設定キャッシュを作り直す
sudo supervisorctl restart connection-worker:* # キューワーカーを入れ替える
# systemd の場合: sudo systemctl restart connection-worker

キューワーカーは起動時に読み込んだ設定を持ち続けます。 再起動を忘れると、たとえば メールの送信設定を直したのに、ワーカーが送るメールだけ古い設定のまま失敗し続ける、 という状態になります。画面から送られるメールとキュー経由のメールで挙動が食い違うため、 原因が分かりにくい種類の不具合です。

opcache.validate_timestamps=0 で運用している場合は、あわせて PHP-FPM も再読み込みします。


3. 点検(システム状態と自己診断)

Section titled “3. 点検(システム状態と自己診断)”

同じ検査結果を2つの入口から見られます。

  • 管理画面 → システム状態(管理者のみ)。インストール要件の検査に加えて、バージョン・ 更新状況・ライセンス・最新バックアップを1画面にまとめています。
  • コマンド。サポートへの連携や監視からの利用に向きます。
Terminal window
sudo -u www-data php artisan connection:doctor # 人が読む形式
sudo -u www-data php artisan connection:doctor --json # 機械可読(監視・サポート用)

[問題] があると終了コードが 1 になります。[警告] は 0 のままです(閉域運用など、 正常な運用でも出る指摘があるためです)。検査は読み取りのみで、環境を変更しません。

変更のあとは必ず実行してください。 .env の編集・PHP のバージョン更新・サーバー移設の あとで、設定の噛み合わせが崩れていないかを確かめられます。


キューワーカーをいったん止めることを強く推奨します。

更新はファイルを置き換えたあとにデータベースを更新します。この間に走っているジョブは、 新しいスキーマの上で古いコードを実行しうる状態になります。更新前に止めておけば、 この窓そのものが無くなります。

Terminal window
sudo supervisorctl stop connection-worker:* # supervisor の場合
sudo systemctl stop connection-worker # systemd の場合

止めている間、通知メールは送られずキューに溜まります(失われません)。更新が終わったら 忘れずに起動し直してください。

あわせて、直近のバックアップがあることを確認してください(システム状態の「バックアップ」)。 更新自体もデータベースのバックアップを自動で取りますが、それはデータベースだけです。

4-1-1. ライセンスと更新を受け取れる期限

Section titled “4-1-1. ライセンスと更新を受け取れる期限”

更新を適用するにはライセンスキーの登録と確認が必要です。 アプリの機能そのものには 一切影響しません(未登録でも期限切れでも、これまでどおり全ての機能が使えます)。

管理画面 → インスタンス設定 → 「ライセンス」の表示は、画面の行に対応して次のとおりです。

画面の行 意味
ライセンス状態 「有効」=サーバーで確認済み(下の期限まで適用可)/「未確認」=キーは保存済みだが未確認
ライセンス先 購入時の名義(サーバーが返した値)
更新を受け取れる期限 この日までに公開されたバージョンを適用できます
ライセンスサーバーで最後に確認した日時 「ライセンスを再確認」が最後に成功した日時
  • キーを登録した直後や、セットアップウィザードで入力した場合は「未確認」から始まります。 「ライセンスを再確認」を押してください。
  • 更新料を支払って期間を延長したときも、「ライセンスを再確認」を押すと新しい期限が 反映されます(自動では反映されません)。
  • 期限を過ぎた後に公開されたバージョンは適用できません。「バージョン情報」には新しい バージョンがあることは表示されますが、適用ボタンの代わりに理由が出ます。期限内に公開 されたバージョンであれば、期限を過ぎた後でも適用できます。
  • ライセンスサーバーへ接続できなかった場合、保存済みの期限はそのまま残ります(一時的な 障害で更新できなくなることはありません)。

このとき外部へ送るのはライセンスキーだけです。URL・バージョン・利用者数などは送りません。 また、通信が発生するのは**「ライセンスを有効化」と「ライセンスを再確認」の2つのボタン**を 押したときだけで、日々の更新チェックではキーを送りません(§4-6)。

4-1-2. 更新期間の延長・お問い合わせ

Section titled “4-1-2. 更新期間の延長・お問い合わせ”

更新を受け取れる期限が切れても、アプリはこれまでどおり使えます。期限より後に公開された バージョンを適用したくなったときに、期間を延長してください。

  1. 販売元(bit-part / https://bit-part.net/)へ、ライセンス先の名義を添えてお問い合わせ ください。更新料のお支払い後、こちらで期限を延長します。
  2. 延長の連絡が届いたら、インスタンス設定で**「ライセンスを再確認」**を押してください。 新しい期限が画面に反映され、期限内に公開されたバージョンを適用できるようになります。

キーを紛失した場合・「このライセンスキーは無効になっています」と表示される場合も、 同じ窓口へお問い合わせください。

4-2. 管理画面から適用する(ワンクリック)

Section titled “4-2. 管理画面から適用する(ワンクリック)”
  1. 管理画面 → インスタンス設定 → 「バージョン情報」を開きます。
  2. 新しいバージョンがある場合、案内と「更新を適用」ボタンが表示されます。 すぐ確認したい場合は「今すぐ確認」を押します(10分あたり5回まで)。
  3. 「更新を適用」を押し、確認ダイアログで進みます。
  4. 進み具合が画面に表示されます。この画面を閉じても更新は続きます(別のプロセスで 動いています)。止めてはいけないのはサーバーのほうです。

作業中、利用者にはメンテナンス画面(503)が表示されます。更新を始めた管理者だけは そのまま操作を続けられます。

更新がやること(14 工程):

準備の確認 → 配信情報の検証 → ダウンロード → 配布物の検証 → 展開 → 適用前バックアップ → メンテナンスモード → ファイルの置き換え → データベースの更新 → キャッシュの作り直し → キューワーカーの再起動 → 応答の確認 → メンテナンス解除 → 後片付け

.env とアップロード実体(storage/app)には一切書き込みません。 storage/ の下には 更新の記録(storage/updater/<ジョブID>/updater.log)と適用前バックアップ (storage/backups/pre-update/)を書き、新しいバージョンが必要とするディレクトリがあれば 作ります(既存のファイルを上書きすることも、削除することもありません)。

配布物は電子署名で検証され、署名が合わないもの・現在より古いバージョンは適用されません。

更新では README.mddocs/ も置き換わります。 手順書はコードと同じ版であって初めて 正しいため、コードと一緒に入れ替えています。自社の運用メモをこの2か所に置かないでください (更新のたびに消えます)。.envstorage/ の外に、別のディレクトリを作って保管することを 推奨します。config/filesystems.php をはじめとする config/ 以下も同様に置き換わります。

Terminal window
sudo -u www-data php artisan connection:self-update

同じ処理を前面で実行し、進み具合が標準出力に出ます。.envSELF_UPDATE_ENABLED=false にして管理画面からの適用を塞いでいる場合でも使えます (サーバーに入れる人だけが更新できる状態にしたいときの構成です)。

失敗した時点によって、後始末が変わります。

失敗した時点 ファイル メンテナンス画面 管理者がやること
データベース更新より前 更新前に戻ります 自動で解除されます なし(更新前と同じ状態です)
データベース更新の途中 更新前に戻ります 維持されます 下の復元後に sudo -u www-data php artisan up

後者でメンテナンス画面を維持するのは、古いコードを新しいスキーマの上で公開しないためです。 このとき画面とコマンドが、戻すべきバックアップの位置(storage/backups/pre-update/…)を 表示します。§6 の復元手順でデータベースを戻してから、メンテナンスを解除してください。

詳しい記録は storage/updater/<ジョブID>/updater.log に残ります。

4-5. 手動での更新(フォールバック)

Section titled “4-5. 手動での更新(フォールバック)”

配信元へ到達できない閉域運用や、上の経路が使えない場合は、配布 zip を直接展開して 入れ替えます。この手順は自動化された安全策(署名検証・自動ロールバック)を伴わないため、 バックアップの取得を省略しないでください。

Terminal window
cd /var/www/connection
# 1. キューワーカーを止める
sudo supervisorctl stop connection-worker:*
# 2. バックアップを取る(データベース + アップロード実体)
sudo -u www-data php artisan backup:run
# 3. メンテナンスモードに入る
sudo -u www-data php artisan down
# 4. 現在の配置を丸ごと退避する(戻すときはこれを使う)
cd /var/www
sudo cp -a connection "connection.bak.$(date +%Y%m%d%H%M%S)"
# 5. 新しい配布物を展開し、置き換えて構わない要素だけを差し替える
unzip connection-1.1.0.zip
cd connection-1.1.0
for item in app artisan bootstrap composer.json composer.lock config database docs lang \
resources routes updater vendor CHANGELOG.md README.md .env.example; do
sudo rm -rf "/var/www/connection/$item"
sudo cp -a "$item" "/var/www/connection/$item"
done
# 5-2. public は「storage を除いて」差し替える(rm -rf public だと public/storage を巻き込む)
sudo rsync -a --delete --exclude=/storage/ public/ /var/www/connection/public/
# ★ .env / storage / public/storage には触れないこと(利用者のデータと設定が入っています)
# 6. 権限を戻す
cd /var/www/connection
sudo chown -R www-data:www-data .
sudo chmod -R 775 storage bootstrap/cache
sudo chmod 600 .env
# 7. データベースとキャッシュを更新して復帰する
sudo -u www-data php artisan migrate --force
sudo -u www-data php artisan optimize:clear
sudo -u www-data php artisan optimize
sudo -u www-data php artisan storage:link
sudo -u www-data php artisan up
# 8. ワーカーを起動し直し、点検する
sudo supervisorctl start connection-worker:*
sudo -u www-data php artisan connection:doctor

手順 5 の一覧は、自動更新が置き換える対象(updater/lib/Layout.phpREPLACEABLE)と 同じものです。 手動と自動で結果が食い違わないよう、どちらかを増やすときは両方を合わせて ください。README.mddocs/ が入っているのは、手順書がコードと同じ版であって初めて 正しいためです(§4-2 の注意書きも参照)。

public を丸ごと消さないのが手順 5-2 の要点です。 public/storage はアップロード実体への 入口で、多くの環境ではシンボリックリンク、環境によっては実体のディレクトリです。消してしまうと 手順 7 の storage:link では元の配信状態に戻らないことがあります(自動更新も、この1つだけは symlink であれ実体であれ関与しません)。--delete を付けているのは、旧版にしか無かったファイルを public/ に残さないためです。それでも storage:link は省略しないでください(リンクが未作成の 環境ではここで作られます)。

opcache.validate_timestamps=0 で運用している場合は、最後に PHP-FPM を再読み込みして ください(古いコードが実行され続けます)。これは自動の更新でも同じで、アプリ側からは Web サーバーの opcache を落とせません。

外部への通信を許さない環境では、.env で止められます。

UPDATE_CHECK_ENABLED=false # 新しいバージョンの確認をしない(外部へ一切接続しない)
SELF_UPDATE_ENABLED=false # 管理画面からの適用を塞ぐ(コマンドからの適用は残る)

確認が送るのは静的な GET だけで、バージョンもライセンスキーもインスタンスを識別する情報も 送りません。ただし通信の性質上、接続元の IP と時刻、および「PHP の HTTP クライアントから 来た」ことは配信元に残ります。

UPDATE_CHECK_ENABLED=false にすると、ライセンスの照会(有効化・再確認)も行われません。 「この環境からは外部へ出さない」という設定なので、管理者の操作でも破りません。この状態では 更新の適用もできませんが、そもそも新しいバージョンを取りに行けないため実害はありません (更新は §4-5 の手動での置き換えで行ってください)。


5-1. 何が取られ、何が取られないか

Section titled “5-1. 何が取られ、何が取られないか”
対象 備考
データベース全体 MySQL は mysqldump、SQLite は sqlite3 が PATH に必要です
アップロード実体(storage/app 共有ファイル・ブランディング画像・アバター
.envAPP_KEY・認証情報) 導入時に別途控えてください。 APP_KEY を失うと、復元しても暗号化された列(2要素認証の秘密鍵など)を復号できません
アプリのコード 同じバージョンの配布 zip から取り直せます
アップロード中の一時ファイル 戻しても意味がないため

保存先は既定で storage/backups/connection/2026-07-29-01-30-00.zip の形です。 保持は直近7日ぶんの7本 + そこから週次で3〜4本(週の区切りとの重なり方で前後します)。 保存先の合計が 5GB を超えると古いものから削除されます。

システム状態画面には設定値そのままの「日次7本+週次4本」と表示されます。上の「3〜4本」は 実際に残る本数で、どちらも同じ設定を指しています。

更新の適用前に取られるバックアップは storage/backups/pre-update/ に分かれており、 上の保持ポリシーの対象外です(直近3本だけが残ります)。

5-2. ★バックアップ zip の取り扱い

Section titled “5-2. ★バックアップ zip の取り扱い”

バックアップ zip は、認証情報と同じ重さで扱ってください。

中身はデータベース全体とアップロードされたファイルです。全データが平文で入っています。 利用者のメールアドレス・パスワードハッシュ・ログインセッション・社内限定の課題や ファイルが、1つの zip にまとまった状態です。

  • storage/backups/ を Web から辿れる場所へ置かないでください(既定の配置は安全です)。
  • 社外のストレージへ持ち出す運用にするなら、.envBACKUP_ARCHIVE_PASSWORD で 暗号化してください。 パスワードは .env とは別の場所に必ず控えます(失うと バックアップは二度と開けません)。
  • 既定で暗号化していないのは、同じサーバーにバックアップと鍵が並ぶだけでは守りにならず、 「パスワードを失って復元できない」事故だけが増えるためです。持ち出すときに設定する、 という分担にしています。
Terminal window
sudo -u www-data php artisan backup:run # データベース + アップロード実体
sudo -u www-data php artisan backup:run --only-db # データベースのみ(速い)

5-4. バックアップを外部にも持つ

Section titled “5-4. バックアップを外部にも持つ”

同じサーバーの中だけに置いたバックアップは、そのサーバーごと失う障害では役に立ちません。 運用に入ったら、外部にも控えを持つことを推奨します。

推奨する形は「ローカル+外部」の二重化です。 ローカルの控え(既定の storage/backups/)は そのまま維持し、そこから外部へ複製します。

  • バックアップが確実になります。 夜間バックアップが外部ストレージの状態に依存しません。 外部側が落ちていた晩も、控えはローカルに必ず残ります。
  • 復元が確実になります。 実際の復元の多くはサーバー喪失ではなく、操作ミスや不良データ からの巻き戻しです。ローカルに控えがあれば §6 の手順を最短で実行でき、サーバーごと失った ときだけ外部の複製から戻します。どちらの zip も中身は同一です。

複製の持ち方は2つあります。守りの強さで選ぶなら引き取り方式です。

引き取り方式(推奨)— バックアップサーバー側から取りに来る

Section titled “引き取り方式(推奨)— バックアップサーバー側から取りに来る”

別のサーバーが自分のスケジュールでこのサーバーへ接続し、控えを取得します。このサーバー側には 何も足しません。

Terminal window
# バックアップサーバー側で実行します。控え(600・Web サーバーのユーザー所有)を
# 読めるアカウントで接続してください。www-data でログインできない構成では、
# 読み取り専用の鍵を用意するなどの工夫が要ります。取得した控えの保持数は取得側で管理します。
rsync -a -e ssh www-data@app-host:/var/www/connection/storage/backups/connection/ /backups/connection/

この方式の価値は権限の向きにあります。このサーバーは外部ストレージの資格情報を一切 持たないため、このサーバーが侵害されても(ランサムウェアを含む)、攻撃者は外部の控えを 消すことも書き換えることもできません。マウントも後送り同期も「このサーバーから書ける= 侵害されたら消せる」という構造を共有しており、データを守り切る観点ではこの方式が最も 強くなります。2台目のサーバーの管理が前提です。

後送り同期 — cron で外部へコピーする

Section titled “後送り同期 — cron で外部へコピーする”

2台目のサーバーを用意できない場合の方法です。夜間バックアップ(01:30)はローカルへ置いた まま、終わったあとに外部ストレージへコピーします。rclone なら S3 / R2 / Google Drive / SFTP など主要なサービスへそのまま送れます。

Terminal window
# www-data の crontab(sudo crontab -u www-data -e)に追加します。
# 01:30 のバックアップが終わってから動くよう、時刻は 03:00 にしています。
0 3 * * * rclone copy /var/www/connection/storage/backups/connection remote:connection-backups

バックアップ本体と転送が分かれているため、外部側の障害が夜間バックアップ自体を失敗させません。 ただしアプリはローカル側しか見ていません。転送の失敗は 04:00 の点検にもシステム状態画面にも 出ないため、cron のメール通知などで rclone の終了コードを必ず拾ってください。転送先に バージョニング(世代保持)のあるストレージを選ぶと、消される・上書きされる事故への備えが 一段強くなります。

どちらの方式でも、外部へ持ち出す運用では BACKUP_ARCHIVE_PASSWORD(§5-2)の設定を強く 推奨します。

保存先そのものを外部へ切り替える(マウント)

Section titled “保存先そのものを外部へ切り替える(マウント)”

二重化ではなく、保存先自体を外部ストレージへ置き換える方法もあります。自己診断が保存先の 安全性まで検査できるのが利点ですが、ローカルに控えが残らなくなり、外部ストレージが使えない 晩はバックアップ自体が失敗します。上の二重化で足りる場合はそちらを使ってください。

外部ストレージを OS でマウントし、そこを指すディスクを使います。NFS・SMB/CIFS・sshfsrclone mount(S3 / Google Drive / OneDrive などをファイルシステムとして見せられます)の いずれでも構いません。アプリから見れば普通のディレクトリなので、追加の依存も資格情報の 受け渡しも要りません。

  1. 外部ストレージを、storage/app と公開ディレクトリのへマウントします。

    Terminal window
    sudo mkdir -p /mnt/connection-backups
    # 例: 別サーバーの NFS 共有をマウントする(/etc/fstab に書いて永続化してください)
    sudo mount -t nfs backup-host:/export/connection /mnt/connection-backups
    sudo chown www-data:www-data /mnt/connection-backups
    sudo chmod 700 /mnt/connection-backups

    マウントが外れると保存先がローカルの空ディレクトリに化けます。バックアップは成功した ように見えて実サーバーには何も残らないため、/etc/fstab に登録して起動時に必ず戻るように してください。異常は 04:00 の点検(§1)とシステム状態画面の「最新のバックアップ」で 気づけます。

  2. config/filesystems.phpdisks に、そのパスを指す local ディスクを追加します。

    'offsite' => [
    'driver' => 'local',
    'root' => '/mnt/connection-backups',
    'serve' => false, // ★必須(Web から配信させない)
    'visibility' => 'private', // ★必須
    'directory_visibility' => 'private', // ★必須
    'backup_options' => ['visibility' => 'private'],
    'throw' => true,
    ],
  3. .envBACKUP_DISK を、そのディスク名に変えます。

    BACKUP_DISK=offsite
  4. 反映して、実際に置かれることを確認します。

    Terminal window
    sudo -u www-data php artisan optimize
    sudo -u www-data php artisan connection:doctor # 「バックアップ保存先の安全性」を確認
    sudo -u www-data php artisan backup:run

localpublic は指定しないでください。 local はバックアップ対象 (storage/app)の中を指すためバックアップがバックアップを取り込む形になり、public は Web から URL 一本で全データを持ち出せる場所になります。この2つを含む危険な指定は sudo -u www-data php artisan connection:doctor が検出します。

config/filesystems.php は更新で置き換わります(§4-2)。ディスクを追加した場合は、更新の あとに追加が残っているかを確認してください(.envBACKUP_DISK は残ります)。確認は 自己診断の「バックアップ保存先の安全性」で行えます — 定義が消えていれば、その項目が 「ディスクが定義されていません」と報告します。

S3 / S3 互換ストレージを直接指定する場合

Section titled “S3 / S3 互換ストレージを直接指定する場合”

config/filesystems.php には s3 ディスクの定義が同梱済みです(.envAWS_* を 埋めるだけで設定は整います)。ただし、この配布物には S3 のドライバ(league/flysystem-aws-s3-v3)が 同梱されていません。 導入先に Composer を必要としない構成にするため、配布 zip には本番依存を 同梱済みの形で入れており、後から依存を追加することもできません。

そのため BACKUP_DISK=s3 にすると、バックアップは保存先を組み立てる時点で失敗します。 この状態は sudo -u www-data php artisan connection:doctor の「バックアップ保存先の安全性」が [問題] として報告します(毎晩静かに失敗し続ける状態にはなりません)。

S3 系のストレージを使いたい場合は、後送り同期の rclone copy(前述)で同じサービスへ 送るのが最も簡単です。保存先自体を S3 に置き換えたい場合は、rclone mount でバケットを ファイルシステムとしてマウントし、上の local ディスクで指してください。この方式なら 保存先が実パスを持つため、バックアップ対象の中や公開ディレクトリの下を指していないかまで 自己診断が検査できますs3 ディスクにはこの検査を行うための情報がありません)。

復元を一度も試していないバックアップは、復元できるかどうか分からないバックアップです。 運用開始のあと一度、以降は四半期に一度を目安に、検証用の環境(使い捨てのデータベースと ディレクトリで十分です)で §6 の手順を通してください。確認する点は3つです。

  • 外部に置いた複製を取得するところから始めて、§6 が最後まで通ること。
  • BACKUP_ARCHIVE_PASSWORD を設定している場合、控えておいたパスワードで実際に開けること (§5-2 のとおり、これを失うと復元そのものが不可能になります)。
  • 復元後の画面にログインでき、直近のデータが見えること。

保存先や転送方法を変えたときは、次の定期確認を待たずにその場で一度通してください。 バックアップは「取れていること」ではなく「戻せること」が目的です。


先に読んでください。 復元は「戻す」のではなく「今のデータを捨てて置き換える」 作業です。手順どおり進めても、バックアップ以降に入力されたデータは失われます。 下の手順0は、その捨てるデータの控えを取るためにあります。

Terminal window
# --- 手順0: 今の状態を先に退避する(省略しない)------------------------------
# 「復元してみたら戻す zip を間違えていた」から回復できる唯一の手段です。
sudo -u www-data php artisan backup:run --only-db --destination-path=before-restore
# → storage/backups/before-restore/ にできます。
# ここは日次の整理(01:00)の対象外で、**誰も自動では消しません**。復元が済んで
# 不要になったら手で削除してください(全データが平文で入っています)。
# --- 手順1: 利用者を締め出す(必須)-------------------------------------------
# 任意ではありません。復元中に書き込みが入ると、復元後のデータベースと storage/app の
# 中身が食い違い(DB に無いファイル/ファイルの無い DB 行)、どこまで戻ったのかが
# 分からなくなります。
sudo -u www-data php artisan down
# --- 手順2: 接続情報を取り出す -------------------------------------------------
# 以下の $DB_* はすべて .env に書かれている値です。手で写すと打ち間違えるので取り出します。
#
# ★ `source .env` は使わないでください。.env はシェルスクリプトではありません。パスワードに
# バッククォートや $(...) が含まれていると、**その場でコマンドとして実行されます**
# (記号を含む強いパスワードはむしろ推奨しているので、現実に起こります)。空白・引用符・
# `!` でも壊れます。必要な行だけを値として取り出します。
envval() { sed -n "s/^$1=//p" .env | head -n 1 | sed -e 's/^"\(.*\)"$/\1/' -e "s/^'\(.*\)'$/\1/"; }
DB_HOST=$(envval DB_HOST)
DB_PORT=$(envval DB_PORT)
DB_DATABASE=$(envval DB_DATABASE)
DB_USERNAME=$(envval DB_USERNAME)
DB_PASSWORD=$(envval DB_PASSWORD)
# --- 手順3: zip を作業ディレクトリへ展開する -----------------------------------
# 中身は全データが平文で並びます。`/tmp/connection-restore` のような固定パスは使わないで
# ください(誰でも書ける場所に予測できる名前で作ると、先回りして作っておいた第三者の
# ディレクトリへ全データを展開してしまいます)。mktemp なら 700・名前は予測できません。
WORK=$(mktemp -d)
unzip "storage/backups/connection/2026-07-29-01-30-00.zip" -d "$WORK"
# → db-dumps/ と storage/ の2つが出てきます
# BACKUP_ARCHIVE_PASSWORD を設定している場合、unzip が対話でパスワードを尋ねます。
# `-P` で渡さないでください(`ps` で同じサーバーの他の利用者に見えます)。
# パスワードを失っていると、ここで開けず復元そのものが不可能になります。
# --- 手順4: アップロード実体を戻す(インストール先のルートで実行)--------------
# rsync は**マージ**です。バックアップ以降に追加されたファイルは消えずに残ります
# (DB から参照されない孤児ファイルになるだけで、実害は容量のみ)。
rsync -a "$WORK"/storage/app/ storage/app/
# バックアップ時点と完全に一致させたい場合のみ --delete を足します。**バックアップに
# 入っていないファイルを消す**ので、zip の中身を確認してから使ってください。
# rsync -a --delete "$WORK"/storage/app/ storage/app/
# 戻したファイルの所有者を Web サーバーのユーザーへ揃えます(省略しない)。
# 展開したユーザーの所有のままだと、以後 www-data がファイルを削除・置換できず、
# 画面からのファイル削除やアバターの差し替えが失敗します。
sudo chown -R www-data:www-data storage/app

手順5: データベースを戻す — 使っているデータベースのブロックだけを実行します。

MySQL / MariaDB

パスワードは -p... でコマンドラインに書かず、MYSQL_PWD で渡します(コマンドラインの引数は ps で同じサーバーの他の利用者から見え、シェルの履歴にも残ります)。

A. データベースごと作り直す場合(任意)。 ダンプに含まれないテーブル——バックアップ後に 追加されたもの——まで消して、バックアップ時点と完全に一致させたいときだけ実行します。 バージョンをまたいで戻すときが主な用途です。

Terminal window
MYSQL_PWD="$DB_PASSWORD" mysql -h "$DB_HOST" -P "$DB_PORT" -u "$DB_USERNAME" \
-e "DROP DATABASE \`$DB_DATABASE\`; CREATE DATABASE \`$DB_DATABASE\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"

ここで終えないでください。 このブロックだけを実行すると、空のデータベースが残ります。 続けて次の流し込みを必ず実行してください。

B. ダンプを流し込む(必須)。 ダンプは全テーブルの DROP TABLE IF EXISTS + CREATE TABLE を 含む自己完結したスクリプトです。ただし CREATE DATABASE / USE は含まないので、接続先を指定します。

Terminal window
MYSQL_PWD="$DB_PASSWORD" mysql -h "$DB_HOST" -P "$DB_PORT" -u "$DB_USERNAME" "$DB_DATABASE" \
< "$WORK"/db-dumps/mysql-*.sql

A を省いた場合、バックアップ後に追加されたテーブルは DROP されずに残ります (ダンプに存在しないため)。通常の復元ではそれで問題ありません。

SQLite
Terminal window
# **空のファイルへ流し込んでください。** SQLite のダンプには DROP TABLE が無いため、
# 中身のあるファイルへそのまま流すと「置き換え」ではなく**マージ**になります:
# ・CREATE TABLE は「already exists」で失敗し、テーブル定義は今のまま
# ・主キーが衝突する行は復元されず、**今の値が勝つ**
# ・バックアップ後に追加された行はそのまま残る
# 結果はバックアップでも現状でもない第三の状態になり、しかも一見「戻った」ように見えます。
#
# 終了コードも成否の判断に使えません。エラーが出て 1 が返っても一部の行は入っており、
# 逆に「入ったつもりで入っていない」ことも起きます。**確認は手順7の件数照合で行います。**
# 今のファイルは上書きせず、タイムスタンプ付きで残します(cp -n は既存ファイルを壊しません)
cp -n database/database.sqlite "database/database.sqlite.$(date +%Y%m%d%H%M%S).bak"
rm -f database/database.sqlite
sqlite3 database/database.sqlite < "$WORK"/db-dumps/sqlite-*.sql
sudo chown www-data:www-data database/database.sqlite
Terminal window
# --- 手順6: キャッシュを作り直して復帰する -------------------------------------
sudo -u www-data php artisan optimize:clear
sudo -u www-data php artisan optimize
sudo -u www-data php artisan up
# --- 手順7: 戻ったことを確かめる -----------------------------------------------
# 終了コードではなく**中身**で確認します。件数がバックアップ時点と合っているか、
# マイグレーション記録とスキーマが揃っているかを見ます。
sudo -u www-data php artisan migrate:status
sudo -u www-data php artisan tinker --execute='
foreach (["users","projects","issues","migrations"] as $t) {
echo $t.": ".\Illuminate\Support\Facades\DB::table($t)->count()."\n";
}'
# --- 手順8: 作業ディレクトリを消す(省略しない)--------------------------------
# 全データが平文で置かれたままになります。
rm -rf "$WORK"
  • .env は zip に入っていません。 サーバーごと作り直した場合は、バックアップ時と同じ APP_KEY を持つ .env を先に戻してください(違う鍵だと、復元したデータベースの 2要素認証の秘密鍵などが復号できません)。
  • ログインセッションもバックアップ時点に戻ります。 復元前にログインしていた端末は 無効なセッションを持つことになるので、全員にログインし直してもらってください。
  • 復元したあとは sudo -u www-data php artisan connection:doctor で点検してください。
  • 手順0で取った storage/backups/before-restore/ の控えは、自動では消えません。 復元の 結果に納得できたら手で削除してください(全データが平文で入っています)。

7. データベースのマイグレーション

Section titled “7. データベースのマイグレーション”

更新はデータベースの更新(マイグレーション)を自動で行うため、通常は意識する必要が ありません。以下は手動で操作する場合の参考です。

Terminal window
sudo -u www-data php artisan migrate:status # 適用済み / 未適用の一覧
sudo -u www-data php artisan migrate --force # 未適用のものを適用(本番では --force が必要)
  • 本番で migrate:fresh / migrate:refresh / migrate:reset を実行しないでください。 全データが失われます。
  • ロールバック(migrate:rollback)はデータを失う可能性があります。 列を削除する マイグレーションを戻すと、その列のデータは戻りません。バージョンを戻したい場合は、 ロールバックではなくバックアップからの復元(§6)を使ってください。
  • 大きなテーブルの構造変更は、その間の書き込みをブロックすることがあります。利用者の 少ない時間帯に更新してください。

サーバーの移設後、セッションの漏洩が疑われるとき、大きなアップデートの直後などに使います。

Terminal window
sudo -u www-data php artisan tinker --execute='\Illuminate\Support\Facades\DB::table("sessions")->delete();'

全員が次のリクエストでログイン画面へ戻ります(データは失われません)。

パスワード変更時の他端末失効は、その端末がログイン後に一度でもリクエストを送っていれば 効きます。導入直後は基準となる記録が無い端末が残るため、バージョンアップの直後に一度 上のコマンドで全セッションを流しておくことを推奨します。これが実行できるのは SESSION_DRIVER=database のときだけです(推奨設定です)。

利用者は自分では変更できず、画面には「管理者にお問い合わせください」と表示されます。 管理画面にユーザー一覧が無いため、依頼を受けた管理者はコマンドで変更します。

Terminal window
sudo -u www-data php artisan tinker --execute='\App\Models\User::where("email","old@example.com")->update(["email" => "new@example.com"]);'
  • 変更後のアドレスが他の利用者と重複していないかを先に確認してください(users.email は 一意です。重複していると上のコマンドはエラーで止まります)。
  • 保留中の招待は招待時のアドレスに固定されているため、この変更では追随しません。必要なら 招待を取り消して出し直してください。
  • ログイン中の端末はそのままです。本人にはログイン ID が変わったことを伝えてください。

退職者などのアカウントを止めるには、users テーブルの is_active を 0 にします。

Terminal window
sudo -u www-data php artisan tinker --execute='\App\Models\User::where("email","taro@example.com")->update(["is_active" => false]);'

無効化すると、ログインできなくなるだけでなく、すでにログイン中の端末も次のリクエストで ログアウトされます。データ(投稿・ファイル)は残ります。

現在のバージョンには、管理画面のユーザー一覧がありません。 ユーザーの無効化・削除・ 権限変更は上のようにコマンドから行う必要があります。他の利用者の2要素認証を解除する 導線も同様にありません。解除するときは、users テーブルの3つの列をまとめて NULL に してください。

Terminal window
sudo -u www-data php artisan tinker --execute='\App\Models\User::where("email","taro@example.com")->update([
"two_factor_secret" => null,
"two_factor_recovery_codes" => null,
"two_factor_confirmed_at" => null,
]);'

two_factor_recovery_codes を落とさないでください。 アプリ自身の解除処理はこの3列を すべて NULL にします。ここだけ残すと、解除したはずの利用者のリカバリコードが(暗号化された 状態のまま)データベースに残り続けます。

セッション行が増えることについて

Section titled “セッション行が増えることについて”

存在しない URL へのアクセスでもセッションの行が1件作られます。通常は自動的に整理されますが、 外部からのスキャンを継続的に受けているサイトでは sessions テーブルが増えます。 気になる場合は、Web サーバー側でスキャンを遮断してください。


Terminal window
ls -t storage/logs/ # 日付ごとのファイルを新しい順に見る
tail -f storage/logs/laravel-$(date +%F).log # アプリケーション(当日ぶん)
tail -f storage/logs/worker.log # キューワーカー(設定した場合)
ls storage/updater/ # 更新の記録(ジョブごとのディレクトリ)

アプリのログは日付ごとのファイルですlaravel-2026-07-29.log)。laravel.log という ファイルは作られないため、その名前を指定すると中身のない結果が返ります。既定で14日ぶん 残ります。.envLOG_LEVELinfo のままにして ください。debug にすると、利用者の氏名や課題名がログに残ります(障害調査で一時的に 下げるのは構いませんが、常用しないでください)。


  1. sudo -u www-data php artisan connection:doctor を実行します。設定の不整合はほぼここに出ます。

  2. 症状によって参照先が分かれます。

    • 更新に失敗した → このガイドの §4-4
    • バックアップから戻したい → このガイドの §6
    • バックアップができていない / 保存先を変えたい → このガイドの §5
    • それ以外(画面が真っ白・419・ログインに戻される・ロゴが出ない・メールが届かない・ アップロードが失敗する・通知やリマインダーが来ない・日本語が英語になる) → インストールガイド の §11 トラブルシュート
  3. 解決しない場合は、次の2つを添えてサポートへ連絡してください。 .env そのものは送らないでください(認証情報が含まれます)。

    Terminal window
    sudo -u www-data php artisan connection:doctor --json > doctor.json
    tail -n 200 storage/logs/laravel-*.log > app.log