Vendure ステージング/本番マイグレーション実行手順(公式ドキュメント準拠)¶
目的と適用範囲¶
- Vendure をステージング環境で検証し、同一アーティファクトを本番へ昇格させるための実行手順をまとめる。
- データベースマイグレーションと初回データ投入、リリース時のチェックポイントを標準化する。
- 本書は開発・運用チームの内部向け。顧客向け資料には転記しない。
- 障害時の drift 初動・復旧は Vendure Drift 運用 Runbook を正とする。
公式リファレンス¶
- Production Configuration(本番向け推奨設定): https://docs.vendure.io/guides/deployment/production-configuration/
- Migrations(TypeORM マイグレーション運用): https://docs.vendure.io/guides/developer-guide/migrations/
- Getting Data into Production(初期データ投入と
synchronizeの扱い): https://docs.vendure.io/guides/deployment/getting-data-into-production - Using Docker(共通イメージを環境ごとに展開する手順): https://docs.vendure.io/guides/deployment/using-docker
- Horizontal Scaling(複数インスタンス運用時のセッション・ID 設定): https://docs.vendure.io/guides/deployment/horizontal-scaling
環境原則¶
- 同一 Docker イメージをステージング→本番へプロモートし、環境差分は環境変数とシークレットで管理する(Using Docker ガイド)。
VENDURE_SYNCHRONIZE=falseを基本とし、本番では常にマイグレーションで管理する。 空の DB を初期化するときだけ migration 実行時にALLOW_SCHEMA_SYNC=trueを明示し、通常運用では残さない(Getting Data into Production)。- Fly の
release_commandは 2026-04-15 以降、migration の直後に additive-only structural repair も実行し、missing relation / join table をアプリ起動前に補修する。 - ID 戦略と HardenPlugin を本番で有効化(UUID 推奨、GraphQL/REST の公開範囲を最小化)(Production Configuration)。
- 複数インスタンス時はセッション共有または stateless JWT を利用し、共通
COOKIE_SECRET/SESSION_SECRETを配布(Horizontal Scaling)。
データベースマイグレーション運用¶
- 生成(開発環境)
- スキーマ変更後、ローカルで
pnpm exec nx run ritsubi-vendure-server:migrate:generate -- --name=<name>を実行し、差分を確認する。 - 親 shell に古い
DATABASE_URLが残っていると、scripts/run-local-ts-source.shのローカル既定値 (postgresql://vendure:vendure_dev_password@127.0.0.1:5433/vendure_dev) が使われず、password authentication failed for user "vendure"で失敗することがある。 その場合はenv | grep DATABASE_URLで接続先を確認し、ローカル生成ではDATABASE_URL=postgresql://vendure:vendure_dev_password@127.0.0.1:5433/vendure_dev pnpm exec nx run ritsubi-vendure-server:migrate:generate -- --name=<name>のように明示して再実行する。 migrate:generateは生成後に 13 桁 epoch-ms timestamp へ正規化し、review / data guard まで実行する。 生の Vendure CLI を直接叩いて 14 桁YYYYMMDDHHmmssの migration file を作らない。- timestamp だけを確認したい場合は
pnpm run lint:vendure-migration-timestampsを使う。pre-commit / pre-push でも migration 変更時に同じ軽量検査が走る。 - 必要に応じて
pnpm exec nx run ritsubi-vendure-server:migrate:review:latestで最新の未コミット migration を再 review する。 - 生成結果をレビューし、不要な SQL を手動で修正する(公式も手動レビュー推奨)。
- Vendure core entity の custom field 追加は自動生成だけに依存しない。
特に
Orderなど既存テーブルでは、期待するcustomFields...カラムが migration に載らないことがあるため、実際の差分を確認し、必要ならALTER TABLE ... ADD COLUMN IF NOT EXISTSを明示した手書き migration を追加する。 VENDURE_SYNCHRONIZE=trueは development/test 用とし、共有環境では使わない。新規環境の空 DB 初期化が必要な場合だけALLOW_SCHEMA_SYNC=trueを明示する。- 直近の実例として、
Order.customFields.billingCustomerCode/shippingMode/deliveryDate/deliveryTimeSlot/specialInstructionsは不足カラムを補う migration を別途追加している。Order.customFields.directShippingSurchargeは per-linefinalUnitPrice内包設計へ 統一したため、専用の drop migration (*_drop_order_direct_shipping_surcharge.ts) で列を削除している。 - migration を追加・編集した PR では、少なくとも
pnpm exec nx run ritsubi-vendure-server:db:reset:dry-runとpnpm exec nx run ritsubi-vendure-server:db:upgrade:dry-runを通し、 空 DB bootstrap と既存 migration state からの upgrade path の両方を確認する。 - ステージング適用
- デプロイ前に DB バックアップ取得。
- 標準導線では
just deploy-fly stagingまたは GitHub Actions deploy を使い、 Flyrelease_commandが migration と additive-only structural repair まで自動実行する。 - 手元から shared env の DB に migration だけを当てる場合は
just vendure-drift-migrate stagingを正本にし、just vendure-migrate-run staging_vendureは使わない。 後者はDATABASE_URLの Fly private hostname (*.internal) をそのまま解決しようとするため、手元ネットワークからはgetaddrinfo ENOTFOUND ...internalで失敗する。 just vendure-drift-migrate <env>/just vendure-drift-sync <env>はscripts/ops/vendure-environment-drift.shの中でflyctl proxyを張り、prepare-fly-cli-env.shで既存の Fly 認証状態を再利用してから127.0.0.1:<port>経由で DB に接続する。shared env への手動 migration / drift repair はこの proxy 導線に統一する。just migrate-fly stagingはdeploy-fly-app staging/--skip-release-commandを使った復旧時に、release contract(migration + structural repair)を手動再実行する補助導線。skip-release は明示確認付きの 例外導線とし、通常の復旧はjust recover-fly-promoted ... env=stagingを正本にする。- 影響範囲のスモークテストを実施(カタログ参照・カート追加・チェックアウト)。
- 本番適用
- メンテナンスウィンドウを設定し、直前にバックアップ。
- ステージングと同じビルドのイメージをデプロイし、標準導線では
just deploy-fly productionまたは GitHub Actions deploy を使って Flyrelease_commandに migration と additive-only structural repair を自動適用させる。 - 手元から shared env の DB に migration だけを当てる場合は
just backup-postgres-productionを先に実行し、その後just vendure-drift-migrate productionを使う。release image を再 deploy しない一回限りの data/schema 補正でも、この順序を崩さない。 just migrate-fly productionはdeploy-fly-app production/--skip-release-commandを使った復旧時に、release contract(migration + structural repair)を手動再実行する補助導線。skip-release は明示確認付きの 例外導線とし、通常の復旧はjust recover-fly-promoted ... env=productionを正本にする。- エラー時はマイグレーションをロールバック(PostgreSQL: 同一トランザクション内のため自動巻き戻し。必要に応じて前バージョンのイメージ+バックアップ復元)。
No pending migrations でも終わりではないケース¶
2026-04-14 に production で確認した実例:
- 当時の
pnpm run migrate:production(migration-only 運用)はNo pending migrations - しかし React Dashboard の商品一覧は空表示
- 実原因は
featuredAsset解決時のrelation "asset_translation" does not exist
この知見から、migration history が最新でも core schema drift は起こりうる ことを前提に、次を短く確認します。
just vendure-diagnose-dashboard-products <env>を実行するjust vendure-drift-audit <env>を実行する- React Dashboard / Admin API で
productsquery を見る featuredAssetなしで成功し、ありで失敗するなら translation table 欠落を疑うinformation_schema.tables/information_schema.columnsで対象 table の有無を確認する- 欠落が限定的なら full sync より repair migration を優先する
2026-04-15 に確認した追加知見:
relation "product_option_groups_product_option_group" does not existは Vendure 3.6 の shared option group 用 core join table 欠落で起こりうる- この系統は migration history が最新でも発生する
- repo 側では repair migration に加えて
runtime driftの structural 監査を metadata 駆動にし、同じ join table 群を GraphQL 実行前に fail-fast させる
推奨コマンドの粒度¶
- migration だけ:
just vendure-drift-migrate <env> - structural repair だけ:
just vendure-drift-repair-structural <env> - baseline / policy / commercial rule sync だけ:
just vendure-drift-sync-baseline staging - 全部まとめて:
just vendure-drift-sync staging
shared env の vendure-drift-sync / vendure-drift-sync-baseline は
visibility policy・commercial rule・test customer の fixture seed repair を流しません。
drift:repair:policy / drift:repair:commercial は shared env 向け導線から外し、
policy / commercial の drift は対象を絞った audited repair か seed 定義の見直しで対応します。
shared env から踏みやすい罠¶
just vendure-migrate-run staging_vendure/production_vendureは local から shared DB へ直接つなぐ導線ではない。Fly private hostname を解決できずgetaddrinfo ENOTFOUND ritsubi-postgres-db-*.internalで失敗する。just vendure-drift-migrate <env>は Fly proxy 前提の導線であり、shared env の手動 migration はこれを使う。flyctl proxy did not become ready ... no access token availableが出たときは、prepare-fly-cli-env.shが既存ログイン状態を拾えていない。 現在のvendure-environment-drift.shはこの準備を内包しているため、同スクリプト経由で再実行する。
CI / local の dry-run 期待値¶
db:reset:dry-runは empty DB から current head だけで bootstrap したときに schema drift が残らないことを確認する。db:upgrade:dry-runはNX_BASE(またはVENDURE_MIGRATION_BASE_REF)時点の migration state から current head へ進め、schema issue / structural drift / migration history set drift が 0 件で終わることを確認する。- upgrade-path dry-run は runtime baseline seed の有無ではなく、 schema / migration contract の整合性に絞って判定する。
- staging / production の machine 起動時は Dashboard Admin API canary も走るため、
relation "...\" does not exist系の GraphQL 失敗は/healthが 503 のまま traffic へ乗らないことを前提にする。 drift:repair:structural-schemaは schema builder の差分から additive-only query だけを抽出して実行するため、drop / rename / type change は自動適用しない。
局所的な core schema 欠落では、vendure-drift-sync を最初に選ばず、上から順に必要最小限の粒度を選びます。
最短 SQL¶
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name IN (
'asset_translation',
'product_translation',
'collection_translation',
'facet_translation',
'facet_value_translation'
)
ORDER BY table_name;
今回の repair 指針¶
asset_translationは Vendure 3.6 系 core schema- table 作成後、
channel.code='__default_channel__'のdefaultLanguageCodeを使ってasset.nameから backfill する - repo には repair migration を追加し、runtime drift audit 側でも metadata ベースの structural drift として検知する
- shared option group 系では
product_option_groups_product_option_group,product_option_group_channels_channel,product_option_channels_channelをまとめて repair 対象にし、旧product_option_group.productIdが残る環境では Vendure 公式 utility による backfill を優先する
[!NOTE] React Dashboard の GraphQL は HTTP 200 でも
errorsを返しうるため、Sentry が静かでも network response を直接見る方が早いことがあります。
migration class 名の timestamp 規約¶
- migration file 名・class 名・
nameプロパティ末尾の timestamp は 13 桁 Unix milliseconds に統一する。 例:name = "AddFooBar1779184860000"; - TypeORM の
MigrationExecutor.getMigrationsは class 名末尾の 13 文字 をparseInt(name.substr(-13))で抽出し、その数値で sort する。 14 桁 (YYYYMMDDHHmmss) を使うと先頭の2が落ちて極端に小さい値になり、 新しい migration が古い migration より早く走って依存切れする。 実例:column "searchNormalizedKeywords" does not existで migrate が失敗した経験あり。 - filename も新規では必ず 13 桁 Unix ms にする。既存の 14 桁
YYYYMMDDHHmmss_*.tsは legacy grandfather のみで、新規追加は禁止。 既存環境のvendure_migrations.nameと一致しなくなるため、 途中で 14 桁→13 桁にリネームする場合はUPDATE vendure_migrations SET name = $1 WHERE name = $2パターンの rename migration を別途追加する(前例:1778257199000_rename_legacy_migration_registry_entries.ts、1779900000000_rename_14digit_migration_names_to_unix_ms.ts)。 - 14 桁 → 13 桁の変換は
Date.UTC(YYYY, MM-1, DD, HH, mm, SS)の UTC 解釈で行う。 既存 13 桁との衝突は Set 検査して +1000ms シフトする。
初回データ投入フロー(新規環境)¶
- 環境変数を設定し、空の DB に対して初回のみ
ALLOW_SCHEMA_SYNC=trueで migration を実行し、Vendure のベース schema を生成する。完了後は設定を外す。 populate()またはカスタムインポートスクリプトで初期データ(管理ユーザー、商品、顧客)を投入。 citeturn0search0- 以降はすべてマイグレーション+インポートスクリプトで管理し、
synchronizeは再度使用しない。
デプロイとプロモーション手順(Fly.io 想定)¶
- ビルド:
docker buildで単一イメージを作成し、タグritsubi-vendure:<git-sha>を付与。 - ステージングデプロイ: イメージをプッシュし、ステージングでデプロイ。標準導線では
just deploy-fly stagingまたは GitHub Actions deploy を使い、 release_command が migration まで自動実行する。復旧でjust deploy-fly-app stagingを使う場合だけ、その後にjust migrate-fly stagingを実行。 - 検証: E2E スモーク(商品検索、ログイン、注文)と監視のメトリクスを確認。
- プロモーション: 同一イメージを本番へタグ付け/デプロイ。構成差分は AWS Secrets Manager(必要に応じてデプロイ先 Secret へ同期)/ 環境変数のみ。
- 本番リリース: release_command による migration 自動適用 → アプリ起動 → 簡易動作確認(管理画面ログイン、GraphQL Health)。
運用チェックリスト¶
- マイグレーションファイルがリポジトリにコミット済みか
- 既存 core entity の custom field 追加時、必要な
customFields...カラムが migration に含まれているか -
VENDURE_SYNCHRONIZE=false(本番・ステージング)を確認 -
ALLOW_SCHEMA_SYNCが空 DB 初期化の場面以外で有効になっていないことを確認 - セッション鍵・JWT 秘密鍵が全インスタンスで一致
- バックアップ完了(直近取得時刻を記録)
- マイグレーション実行ログを保存し、失敗時の手順(リカバリ)を共有
- スモークテスト結果を記録(ステージング/本番)
ロールバック方針¶
- マイグレーション失敗時はトランザクションにより自動ロールバックされるが、 データ損失リスクを避けるためバックアップ復元手順を必ず用意 (MySQL/MariaDB はトランザクション非対応に注意)。
- アプリケーションは直前の安定イメージに切り戻し、再度マイグレーションを適用しない(DB をバックアップから戻す)。
付録: 推奨環境変数(抜粋)¶
VENDURE_SYNCHRONIZE=falseALLOW_SCHEMA_SYNC(空 DB 初期化時のみ)VENDURE_AUTH_OPTIONS_TOKEN_METHOD=bearerCOOKIE_SECRET,SESSION_SECRET(全環境で共有鍵を設定)VENDURE_API_OPTIONS_PLAYGROUND=false(本番では GraphQL Playground を無効化)PORT,DOMAIN,SHOP_API_PATH,ADMIN_API_PATH(環境ごとのエンドポイント)
参照先¶
- Vendure Guides: Production Configuration, Migrations, Getting Data into Production, Using Docker, Horizontal Scaling
- 当プロジェクトのホスティング固有手順:
docs/03-implementation/infrastructure/deployment-guide.md,docs/03-implementation/infrastructure/fly-io-postgres-docker.md