はじめに
それは、何の変哲もないデプロイでした。
変更内容は、たった1行。
外部サービスの所有権確認用に、
public/verify.txt
を追加しただけです。
DB変更なし。
コード変更ほぼなし。
verify.txt を置いただけでした。
本当に、それだけのはずでした。
ところが、デプロイ直後、Strapi の本番 DB から Content-Type とデータが消えました。
当時の構成
- Strapi v4.12.7 (Community Edition)
- PostgreSQL 12
- GitHub Actions
- Dokku
- Docker 運用
デプロイは GitHub Actions から Dokku へ push する構成でした。
with:
git_push_flags: "--force"
この設定で、サーバー側は毎回 Git の状態へ強制的に同期されます。
事故の経緯
1. verify.txt を追加
外部 SaaS(CDN 系)の所有権確認のため、
public/verify.txt
を追加。
Strapi の public/ 配下に置いたファイルは、そのまま静的配信されるため、ただファイルを置くだけの作業でした。
2. デプロイ実行
GitHub Actions → Dokku 経由でデプロイ。
デプロイ自体は正常終了。
しかし、その直後。
- 一部 Content-Type が消失
- 管理画面からコレクションが消える
- データが参照不能になる
という異常が発生しました。
最初に疑ったこと
最初は PostgreSQL 側の事故を疑いました。
「DB が壊れた?」
「migration が誤実行された?」
「Dokku の volume が飛んだ?」
かなり焦りました。
しかし DB ログを確認すると、恐ろしい痕跡が残っていました。
ALTER TABLE ... DROP COLUMN
さらに、
DROP TABLE ...
まで実行されていました。
つまり、
DB を消していたのは Strapi 自身でした。
原因
ARG NODE_ENV=development
ENV NODE_ENV=${NODE_ENV}
CMD ["yarn", "develop"]
本番環境なのに、Strapi を develop モードで起動していました。
develop モードで何が起きていたのか
Strapi の Content-Type Builder は、管理画面でポチポチ編集しているように見えます。
しかし実際には、
src/api/*/content-types/*/schema.json
を書き換えています。
つまり、
「DB を編集している」のではなく、
「サーバー上のコードを書き換えている」
という状態でした。
そして --force push が噛み合った
ここから、一気に事故が加速します。
管理画面で編集された schema.json は、サーバー上にしか存在していませんでした。
Git にコミットしていなかったのです。
そこへ、
git_push_flags: "--force"
付きのデプロイが実行されました。
結果、サーバー上で変更されていた schema.json は、Git 上の古い状態で強制的に上書きされました。
この時点では、まだ何が起きているか理解できていませんでした。
Strapi の自動同期が発動
ここで Strapi が起動します。
Strapi は起動時に、
schema.json- 実際の DB 構造
を比較し、コード側へ DB を同期します。
この時点で状態はこうなっていました。
Git にある古い schema.json
{
"attributes": {
"title": {
"type": "string"
}
}
}
実際の DB
-
fieldAカラムが存在 - 管理画面で追加した Content-Type が存在
Strapi はこう判断します。
「schema.json に存在しないので不要」
そして静かに、
ALTER TABLE articles DROP COLUMN "fieldA";
を実行。
さらに、コード上に存在しなかった Content-Type は、
DROP TABLE ...
によって消滅しました。
なぜ起きたのか
今回の事故は、複数の地雷が綺麗に噛み合って発生しました。
① Strapi のスキーマは「コード」
管理画面の編集内容は DB ではなく schema.json に保存されます。
DB は二次的な投影に過ぎず、真実はコード側にあります。
② 本番なのに develop モード
CMD ["yarn", "develop"]
production モードでは、Content-Type Builder はロックされます。
つまり本来、
「本番管理画面からスキーマ変更する」
という運用自体ができません。
しかし今回は develop モードだったため、本番でも schema.json を編集し放題でした。
③ --force push
git_push_flags: "--force"
これにより、サーバー上のローカル変更はすべて破棄されます。
つまり、管理画面で変更した schema.json は、デプロイの瞬間に消えます。
④ Strapi の同期は「コード → DB」
Strapi はコードを正として DB を同期します。
逆方向(DB → コード)への吸い上げは存在しません。
つまり、
コードに無い = DB から削除対象
です。
再発防止策
1. スキーマ変更はローカルのみ
- ローカルの Strapi(develop モード)で変更
- schema.json を Git コミット
- 本番へデプロイ
本番管理画面は「コンテンツ入力専用」にするべきです。
2. 本番は必ず production モード
ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}
CMD ["yarn", "start"]
production モードでは Content-Type Builder がロックされます。
つまり、
「Content-Type はコード管理するもの」
という Strapi 本来の運用に強制されます。
3. --force push を見直す
便利ですが、かなり危険です。
少なくともデプロイ前に schema.json のスナップショットや DB backup は必要でした。
4. DBバックアップを自動化する
Dokku なら、
dokku postgres:export
を CI のデプロイ前へ入れるだけでも命綱になります。
幸い PostgreSQL のバックアップから一部復旧できましたが、最新データの一部は失われました。
おわりに
「1行の変更だから安全」
そんな保証はどこにもありませんでした。
Strapi の管理画面にある「保存」ボタンは、DB 保存ではありません。
あれは、
サーバー上のコードを書き換えるボタン
です。
そのコードは次のデプロイで消えます。
私の犠牲が、誰かの DB を守る糧になれば幸いです。
同じ運用をしている人、本当に気をつけてください。

