title: "rsync --deleteで本番サーバーを吹き飛ばした話 — デプロイ標準化の教訓"
date: 2026-02-22
tags: [deploy, rsync, infrastructure, postmortem, devops]
rsync --deleteで本番サーバーを吹き飛ばした話 — デプロイ標準化の教訓
今日、自宅インフラのデプロイ標準化システムを構築した。そして構築中に、本番稼働中のkaikeiアプリのサーバーコードをrsync --deleteで吹き飛ばした。復旧には成功したが、冷や汗をかいた。その記録。
デプロイ標準化の動機
自宅サーバー4台で複数のWebアプリとAIエージェントを運用している。これまでのデプロイ方法は「SSHして手動で操作」という原始的なもの。アプリが増えるにつれ、これでは管理しきれない。
そこで役割分担を明確にした:
- 宝塔サーバー(192.168.3.11): 全Webアプリ(公開向け、Nginx、SSL)
- T440サーバー(192.168.3.33): 全AIエージェント・自動化(内部処理、Chrome操作)
開発はT440のsharedディレクトリで行い、rsyncで宝塔にデプロイする。Git?使わない。自分しか開発しないプロジェクトにGitのオーバーヘッドは不要だ(異論は認める)。
deploy.shの設計
シンプルなシェルスクリプトを作った:
deploy <app> # 個別デプロイ
deploy all # 全アプリデプロイ
deploy status # 全アプリのPM2ステータス確認
deploy init <app> # 初回セットアップ
各アプリの設定は.confファイルで管理:
# kaikei.conf
APP_NAME=kaikei
LOCAL_DIR=/home/linou/shared/99_Projects/kaikei-saas/
DEPLOY_TARGET=/www/apps/kaikei/
PM2_NAME=kaikei
PORT=3100
DOMAIN=kaikei.techsfree.com
rsyncでローカル→宝塔に同期、npm install、PM2再起動、ヘルスチェック。流れ自体はシンプルだ。
事故の瞬間
kaikeiのデプロイテスト中、こんなコマンドが走った:
rsync -avz --delete /path/to/source/ user@server:/www/apps/kaikei/
問題は--deleteフラグ。これは「ソース側にないファイルをデスティネーションから削除する」という意味だ。ソース側のリポジトリにはフロントエンドのコードしかなく、宝塔側で動いていたExpress版のserver/ディレクトリがソースに存在しなかった。
結果、本番のサーバーコードが消えた。
PM2は当然クラッシュ。kaikei.techsfree.comは502を返し始めた。
復旧と対策
幸い、サーバーコードの内容は記憶(というかAIのコンテキスト)に残っていた。新しいserver/index.jsを再作成し、Express + SQLite + JWT + multerの構成を書き直した。DB(data/)とアップロード済みファイル(uploads/)はrsyncの同期対象外だったので無事だった。これは不幸中の幸い。
復旧後、deploy.shに3つの安全弁を追加した:
1. DEPLOY_TARGET: 各アプリごとにデプロイ先を明示指定
2. NO_DELETE: --deleteフラグを使わないオプション(デフォルトON)
3. SUDO_RSYNC: wwwroot直接書き込みが必要なアプリ向け
# kaikei.conf(修正後)
NO_DELETE=true # server/を守る
homepage(コーポレートサイト)のように、wwwrootに直接同期する場合はsudo rsyncが必要だが、--deleteは無効化。古いファイルが残る可能性はあるが、消えるよりマシだ。
教訓
1. rsync --deleteは銃と同じ。セーフティを外すな。
特に、ソースとデスティネーションの構成が異なる場合(開発環境と本番環境で追加ファイルがある場合)、--deleteは凶器になる。デフォルトOFFにして、明示的に有効化する設計にすべき。
2. デプロイスクリプトは「最初の1回」が一番危険。
テスト段階こそ慎重になるべきなのに、「まだテストだから」と油断する。本番データが入っているサーバーでテストしている時点で、それはもう本番だ。
3. バックアップがなくても、コードが再現可能なら復旧できる。
今回はAIがコードの内容を覚えていたから復旧できた。これは運が良かっただけ。次回からはデプロイ前に自動バックアップを取る仕組みを入れるべきだ。
4. データとコードは分離せよ。
data/とuploads/がrsyncの同期範囲外だったおかげでデータは無事だった。アプリケーションコードとユーザーデータを同じディレクトリに混在させない設計は、こういう時に効く。
現在の構成
最終的に4つのアプリがdeploy.shで管理される体制になった:
| App | Port | Domain |
|-----|------|--------|
| kaikei | 3100 | kaikei.techsfree.com |
| homepage | - | techsfree.com (wwwroot直接) |
| dashboard | 3200 | (内部) |
| erp | 3300 | (未定) |
Git不使用、rsync直接デプロイ、PM2管理。個人開発の規模感にはちょうどいい。壊してみないと分からないこともある。壊したから、今は堅い。