---
title: "Supabase CLIとローカル開発環境：db diffで進めるマイグレーションとCI"
url: "https://www.issoh.co.jp/tech/details/17063/"
published: 2026-08-27
updated: 2026-08-28
categories: ["インフラ・クラウド"]
publisher: "株式会社一創"
---

# Supabase CLIとローカル開発環境：db diffで進めるマイグレーションとCI

Supabase CLI は、クラウド上のプロジェクトを遠隔操作する道具ではありません。本番と同じ構成のPostgreSQLとAPIを、手元のコンテナに丸ごと立てるための道具です。`supabase start` を一度叩けば、データベース、REST API、認証、ストレージ、Studio、メール受信箱までが起動します。厄介なのはその先です。手元で変えたスキーマをどう本番へ運ぶのか、CIに何をシークレットとして渡すのか。この記事では CLI 2系（2026年8月時点で v2.116.0）を前提に、ローカルスタックの構成、`db diff` と `db push` の運用、宣言的スキーマの検出漏れ、GitHub Actions への組み込み、履歴修復までを実装順に並べます。

## まとめ：ローカルスタックとマイグレーション運用で先に決める3点

Supabase CLI のローカル環境は「本番と同じPostgreSQLをコンテナで再現し、スキーマ変更をSQLファイルとしてGitに載せる」ための仕組みです。手元のデータベースは正ではありません。正は `supabase/migrations` 配下のSQLファイルであり、ローカルはそれを再生した結果に過ぎない。この向きを取り違えると運用が崩れます。

着手前に決めるのは3点です。第一に、スキーマ変更の入り口です。手書きの `supabase migration new` か、Studio で触ってからの `db diff` か、宣言的スキーマのファイルを正にするかで、レビューの形が変わります。第二に、本番反映を人が叩くのかCIに任せるのか。第三が行レベルセキュリティのポリシーの扱いです。`db diff` はポリシー文を拾わないため、ここだけは手書きのマイグレーションを維持してください。

## supabase startで立ち上がるローカルスタックの構成と4つのポート

まず環境を立てます。CLI はグローバルインストールを前提としないため、プロジェクトの開発依存として入れる形が扱いやすい構成です。

同じ構成を検証用ではなく本番として自前のサーバーで持つ場合、更新とバックアップの責任は自分側へ移ります。現行のDocker Compose構成と運用の負荷は[Supabaseセルフホストの構成と運用の記事](https://www.issoh.co.jp/tech/details/17074/)で整理しました。

### コンテナランタイムの選択肢とsupabase initが作る設定ファイル

インストール手段は4系統あります。Node系なら `npm install supabase --save-dev`、macOS は `brew install supabase/tap/supabase`、Windows は `scoop install supabase`、Linux は Homebrew か `.deb`、`.rpm`、`.apk` のパッケージから選びます。

前提として、コンテナランタイムが必要です。公式ドキュメントが挙げているのは Docker Desktop、Rancher Desktop、Podman、OrbStack、colima の5つです。Docker Desktop の常駐が重い開発機なら、[OrbStackのような軽量なMac用コンテナ環境](https://www.issoh.co.jp/tech/details/4058/)に差し替えても `supabase start` は同じように動きます。

次に `supabase init` を実行すると、リポジトリ直下に `supabase` ディレクトリと `config.toml` が作られます。この1ファイルがポート番号、認証設定、シードの挙動までを持つため、Gitに載せてチームで共有します。

### 54321から54324に割り当てられるAPI・DB・Studioの接続先

`supabase start` が完了すると、エンドポイントが一覧で表示されます。番号の割り当ては固定です。

| ポート   | 提供されるもの    | 主な用途            |
| ----- | ---------- | --------------- |
| 54321 | API ゲートウェイ | REST・GraphQL・関数 |
| 54322 | PostgreSQL | psql・ORMの直接接続   |
| 54323 | Studio     | テーブル編集・SQL実行    |
| 54324 | Mailpit    | 確認メールの受信        |

54321 の下には `rest/v1`、`graphql/v1`、`functions/v1` が並び、MCP 用のエンドポイントも同じポートの `mcp` パスに載ります。このローカル向けMCPサーバーで使えるツールの範囲とホスト型との違いは[Supabase MCPサーバーの接続手順とスコープ設計](https://www.issoh.co.jp/tech/details/17076/)で扱っています。アプリ側の環境変数は、このホストとローカル用の anon キーを指すだけで切り替わる構成です。Next.js 側でこの値を読んでクライアントを生成する手順は[Supabase Next.js連携の実装とproxyでのセッション更新](https://www.issoh.co.jp/tech/details/17065/)にまとめています。[Edge Functionsの実行環境とデプロイ手順](https://www.issoh.co.jp/tech/details/17061/)も同じスタックで起動するため、関数も手元で叩けます。

サインアップ確認やパスワード再設定のメールは外部へ飛ばず、54324 の Mailpit に溜まります。認証フローのテストで届かないと感じたら、まずここを開いてください。

### supabase stopでの停止とデータを残すか捨てるかの判断

停止は `supabase stop` です。既定ではデータベースの内容がバックアップとして保持され、次回の `supabase start` で復帰します。この保持が事故のもとにもなります。マイグレーションを書き換えたのに古いデータが残り、手元だけ通って本番で落ちる経路が生まれるためです。スキーマをいじった直後は `supabase stop --no-backup` で捨てるか、起動したまま `supabase db reset` で作り直してください。

## db diffとdb pushで組むマイグレーションの生成と本番反映の手順

ここが運用の中心です。Supabase のマイグレーションはタイムスタンプ付きSQLを積み上げる素朴な仕組みで、フレームワーク固有のDSLは介在しません。

### supabase migration newで書くSQLとdb resetでの再適用

`supabase migration new create_employees_table` を実行すると、`supabase/migrations` 配下に「タイムスタンプ\_create\_employees\_table.sql」という空ファイルが生成されます。中身は素のDDLを自分で書く形です。

```
create table employees (
  id bigint primary key generated always as identity,
  name text not null,
  email text unique,
  created_at timestamptz default now()
);
```

書いたら `supabase db reset` をかけます。ローカルのデータベースを作り直し、マイグレーションを古い順に全部再適用するコマンドで、途中のファイルを書き換えても頭から通るかを毎回検証できます。命名や正規化そのものは、[三層スキーマの考え方を扱った記事](https://www.issoh.co.jp/tech/details/4124/)の観点を先に通してからファイルへ落としてください。

### Studioでの変更をdb diffで拾ってファイル化する手順

SQLを手書きせず、54323 の Studio でテーブルを作る進め方もあります。その場合は変更後に `supabase db diff -f add_employees` を叩いてください。CLI がローカルデータベースの現状とマイグレーション適用後の状態を比較し、差分をSQLとして書き出します。

1. Studio でテーブルやカラムを追加する
2. `supabase db diff -f 変更名` で差分をファイル化する
3. 生成されたSQLを目で読み、不要な差分を削る
4. `supabase db reset` で頭から通るか確かめる

3番目を飛ばさないでください。`db diff` の出力には意図していない権限付与や既定値の変更が混ざるため、そのままコミットするとレビューで読めないSQLが積まれていきます。

### supabase linkとdb pushで本番へ反映する前の確認項目

リモートへは `supabase link --project-ref` にプロジェクト参照文字列を渡してリンクし、`supabase db push` でローカルのマイグレーションを適用します。

反映前に見るのは `supabase migration list` です。ローカルとリモートのどちらに何が適用済みかが並ぶため、想定外のファイルが未適用で残っていないかを確認できます。カラム削除や型変更、NOT NULL 追加のような破壊的な変更が含まれていても `db push` は止まらず、既存行が制約に違反した時点で本番側がエラーになります。データ移行のSQLを同じマイグレーションに同梱するか、段階を分けてください。

### db pullで既存プロジェクトを後からCLI管理に載せる流れ

ダッシュボードだけで運用してきたプロジェクトをCLI管理へ移す場合は、`supabase db pull` でリモートの現状を1本のマイグレーションファイルとして取り込みます。これが初期スキーマになり、以降の変更を差分で積む形へ移れます。直後に `supabase db reset` が通るかが分岐点です。通らないなら、拡張機能の有効化順序かダッシュボード由来の内部オブジェクトが再現できていません。手作業の補正を1回だけ入れ、以降は差分運用へ統一してください。

## 宣言的スキーマでのスキーマ管理とdb diffが検出しない変更の扱い

差分を積み上げる方式の弱点は、テーブルの最終形を知るために全マイグレーションを読む必要が出てくることです。これに対する公式の答えが宣言的スキーマです。

### supabase/schemasのSQLを唯一の正とする宣言的な運用

宣言的スキーマでは、`supabase/schemas` 配下のSQLファイルに「あるべき状態」を書く方式です。テーブル定義を1ファイルにまとめ、変更したいときはそのファイルを直接編集します。`supabase db diff -f 変更名` を実行すれば、現在の状態からあるべき状態へ移すためのマイグレーションが自動生成されます。

注意すべき仕様が1つあります。この方式の `db diff` はスキーマファイルを比較対象とし、稼働中のデータベースを読みません。Studio、SQLエディタ、psql から直接加えた変更は無視されます。宣言的スキーマを選んだ時点で「Studioで触って差分を拾う」進め方は使えなくなる、と理解しておいてください。

### db diffが拾わないRLSポリシー・マテビュー・権限の手当て

公式ドキュメントは `db diff` が検出できないオブジェクトを列挙しています。ここを知らずに宣言的スキーマへ寄せると、本番で権限だけが抜け落ちます。

- INSERT・UPDATE・DELETE などのDML文
- 行レベルセキュリティのポリシー変更文、カラム単位の権限
- ビューの所有者・権限・セキュリティ呼び出し設定、マテリアライズドビュー
- スキーマ権限、コメント、パーティション、パブリケーション設定、ドメイン文

実務でいちばん刺さるのは2番目です。Supabase はアクセス制御を行レベルセキュリティに寄せた設計のため、ポリシーが移らない性質はセキュリティ事故へ直結します。ポリシーは宣言的スキーマに含めず、`supabase migration new` で手書きしたマイグレーションとして管理してください。書き方と有効化漏れの検知は[Supabase RLSの設定手順とポリシー設計の記事](https://www.issoh.co.jp/tech/details/17056/)で扱っています。

### 宣言的スキーマへ切り替えず命令型のままで進めてよい場面の条件

全案件に入れる必要はありません。テーブル数が10前後で変更が月に数回という規模なら、差分ファイルを読むコストは小さく、二重管理の手間のほうが上回ります。切り替えを勧めるのは、テーブルが数十を超えて最終形が読めなくなったとき、あるいは複数人が並行して同じテーブルを触りレビューで差分の意図が追えなくなったときです。

ポリシーとトリガーが機能の中心を占めるプロジェクトでは、守備範囲が狭すぎて恩恵が薄くなります。命令型を維持し、最終形をER図か1枚のスキーマダンプで別管理する形が現実的でしょう。

## seed.sqlとdb resetで揃える初期データと型生成の再実行タイミング

スキーマだけ揃っても開発は進みません。マスタデータとテスト用ユーザー、そしてアプリ側の型定義を、コマンド1本で再現できる状態にしておきます。

### seed.sqlの実行タイミングとconfig.tomlでの複数ファイル指定

既定では `supabase/seed.sql` に一致するファイルが読まれます。実行されるのは初回の `supabase start` 時と、`supabase db reset` を叩くたびです。すべてのマイグレーションが適用され終わった後にシードが走ります。

1本で足りなくなったら `config.toml` の `[db.seed]` で分割できます。`enabled` でシード自体の有効化を切り替え、`sql_paths` に個別ファイルの配列か `seeds` ディレクトリのグロブを指定します。基準ディレクトリは `supabase` です。グロブが複数に一致した場合は辞書順で実行されるため、依存関係のある挿入は接頭辞に番号を振ってください。

### gen typesで作る型定義とCIで型の差分を検知する仕組み

型定義は `supabase gen types typescript --local` で生成します。出力をリポジトリのファイルへリダイレクトし、コミット対象に含める運用が一般的です。リモートから生成する場合は `--project-id` にプロジェクト参照文字列を渡してください。

再生成のタイミングは、マイグレーションを追加した直後です。`supabase db reset` でスキーマを作り直したうえで型を吐かせないと、古い型が残ります。生成物をコミット必須にしておくと、CIで再生成して差分が出た時点でビルドが落ちる仕組みです。[PrismaとTypeORMの比較記事](https://www.issoh.co.jp/tech/details/4331/)で触れているORMを併用する構成では型の出所が二重化するため、どちらを正にするかを先に決めてください。ORM側のマイグレーション機構とCLIの台帳が並走する問題と、その寄せ方は[SupabaseのORM連携（Prisma・Drizzle）の記事](https://www.issoh.co.jp/tech/details/17072/)で扱っています。

### 認証ユーザーを含むシードデータで詰まりやすい外部キー制約の回避

業務アプリのシードで最初に詰まるのは、ユーザーに紐づくテーブルです。プロファイルや所属テーブルは `auth.users` の `id` を外部キーに取ることが多く、ユーザーが存在しない状態で挿入すると制約違反で止まります。

シードの先頭で `auth.users` へテスト用ユーザーをUUID固定で挿入し、以降の業務データはそのUUIDを参照させてください。UUIDをハードコードしておくと、フロントエンドのテストコードからも同じIDで参照でき、ログイン状態を前提にした画面の検証が安定します。

## GitHub Actionsに載せるdb pushの自動化と壊れた履歴の修復手順

手元で回る状態ができたら、本番反映を人の手から外します。公式が示す構成は、プルリクエストで検証しマージで反映する形です。

### supabase/setup-cliで組むプルリクエスト時の検証ジョブ

各ワークフローの先頭で `supabase/setup-cli@v1` を読み込み、`version` に `latest` を指定します。プルリクエストのジョブでは、ローカルスタックを起動してマイグレーションが頭から通ることを確かめます。

```
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: supabase/setup-cli@v1
        with:
          version: latest
      - run: supabase start
      - run: supabase db lint
```

この検証があると、レビュー段階で「手元では通るが reset で落ちるマイグレーション」を弾けます。CI基盤そのものの組み方は[GitHub Actionsでビルドと自動テストを設定する手順](https://www.issoh.co.jp/tech/details/2497/)にまとめています。反映したスキーマをVercel側の本番とプレビューへ結線する手順は[Supabase Vercelデプロイ連携の記事](https://www.issoh.co.jp/tech/details/17066/)で扱っています。

### アクセストークンとDBパスワードをシークレットに置く反映ジョブ

反映側のジョブで必要になるシークレットは3つです。個人アクセストークンの `SUPABASE_ACCESS_TOKEN`、プロジェクト固有の `SUPABASE_DB_PASSWORD`、プロジェクト参照文字列の `SUPABASE_PROJECT_ID` を暗号化シークレットに登録してください。

ジョブ本体は `supabase link --project-ref` でリンクし、`supabase db push` を叩くだけです。ブランチ戦略は機能ブランチから `develop`、`develop` から `main` へのプルリクエストで進め、`develop` への push でステージング、`main` への push で本番が反映される構成が公式の例として示されています。ステージングを挟めば、破壊的なマイグレーションが本番へ直撃する経路を1段減らせます。

### migration listとmigration repairで直す履歴のずれ

運用していると必ず起きるのが、履歴テーブルとファイルのずれです。ダッシュボードから手で流したSQLが記録されず、次の `db push` が同じDDLを二重に実行して落ちる、という形が典型例になります。

まず `supabase migration list` で記録の有無を突き合わせ、`supabase migration repair` で、実体はあるのに未記録のものを適用済みとして記録するか、逆に記録だけ残っているものを取り消し扱いへ戻してください。ここでマイグレーションファイルを消すのは誤りです。消してしまうと、次に環境を作った人が同じスキーマへ到達できません。直すのは履歴の記録側です。

## CLI前提のローカル開発を見送る3つの条件とStudio直編集の限界

ここまでの手順を踏む価値はプロジェクトの形で変わります。判断を言い切ります。

### Docker常駐のコストが見合わない小規模プロジェクトの判断条件

テーブルが5本以下、開発者が1人、リリース後の変更が年に数回という案件では、CLI一式を入れる判断を採用しません。コンテナランタイムの常駐、CIの設定、シークレットの管理という固定費が、得られる再現性を上回るためです。この規模なら開発用プロジェクトを1つ追加で作り、Studio で触って本番へ手で写す形で足ります。

境目は「環境を作り直す人が自分以外に出てくるか」です。引き継ぎや外注の見込みがあるなら、あとから移すより最初からCLIに寄せたほうが安く済みます。判断に迷う段階でしたら、[業務用・Webアプリ開発の相談窓口](https://www.issoh.co.jp/service/apps/business/)で、変更頻度と体制から環境構成を一緒に決める進め方もできます。

### Studio直編集を続けたチームで起きるスキーマ差分の事故例

複数人が Studio で本番を直接触る運用は、必ず壊れます。壊れ方には順序があり、最初にローカルと本番のカラム型がずれ、次に型定義の再生成でフロントエンドのビルドが落ち、最後に誰も本番のあるべき状態を答えられなくなる、という順です。

`supabase db pull` で本番の現状を固定し、そこを起点に差分運用へ切り替える。同時に本番プロジェクトの編集権限を絞り、DDLはCI経由のみに限定してください。権限を絞らないまま運用ルールだけ配っても、締め切り前に必ず破られます。

### ローカルで再現できない機能とステージング環境における検証の分担

ローカルスタックはよくできていますが、完全な複製ではありません。読み取りレプリカ、カスタムドメイン、外部の認証プロバイダとのコールバック、本番相当のデータ量に依存する実行計画は手元では確かめられません。同時接続数の上限に絡む挙動も、[Realtimeの実装と同時接続の上限を扱った記事](https://www.issoh.co.jp/tech/details/17058/)のとおりクラウド側でしか検証できない性質です。

役割を分けます。ローカルはスキーマとロジックの検証、ステージングは外部連携とデータ量の検証、と割り切ってください。ローカルで再現しようとして `config.toml` に例外設定を積み上げると、その設定自体が本番との差分になります。

## よくある質問

Supabase CLI とローカル環境の構築でよく検索される疑問に答えます。

### supabase startが起動しないときは何を確認しますか？

最初に見るのはコンテナランタイムの稼働状態です。Docker Desktop や OrbStack が起動していないと、CLI はイメージを取得できずに止まります。次に 54321 から 54324 のポートが他プロセスに使われていないかを確認してください。ローカルのPostgreSQLが 54322 を掴んでいる例もあります。衝突しているなら `config.toml` でポートを変更するか、`supabase stop --no-backup` で残骸を消してから再実行してください。

### Docker Desktopの代わりにOrbStackやPodmanでも動きますか？

動きます。本文で挙げた5つのランタイムはいずれも公式が対応を明記しており、有償ライセンス条件が組織の規定に触れる場合や、開発機のメモリ消費を抑えたい場合の代替になります。ただしランタイムを変えるとボリュームの扱いやネットワークの挙動が異なるため、チーム全員で同じものに揃えてください。

### db pushとdb diffはどちらを先に実行しますか？

`db diff` が先です。役割が違います。`db diff` はローカルの変更をマイグレーションファイルとして書き出すコマンドで、`db push` はそのファイルをリモートへ適用するコマンドです。ファイル化とレビューを済ませてから反映する順序を崩さないでください。

### チームで同じローカル環境を共有するにはどうしますか？

共有するのはデータベースではなく再現手順です。`config.toml`、`supabase/migrations` 配下のSQL、`seed.sql` の3つをGitに載せておけば、各自が `supabase start` と `supabase db reset` で同じ状態に到達できます。CLI のバージョン差で挙動がぶれるため、`package.json` の開発依存としてバージョンを固定してください。

### 本番のデータをローカルに持ってきてよいですか？

個人情報を含むテーブルはそのまま持ってこないでください。`supabase db pull` が取り込むのはスキーマだけで、行データは含まれません。データが必要な検証では、本番から件数と分布だけを写した匿名化済みのシードを別に作ってください。

## 関連記事

- [SupabaseとFirebaseの違いを徹底比較｜料金・認証・どっちを選ぶか](https://www.issoh.co.jp/column/details/3002/)：製品全体の機能範囲と料金を整理しています。
- [Supabase Edge Functionsとは：Deno実行環境とデプロイ・シークレット・課金単位](https://www.issoh.co.jp/tech/details/17061/)：ローカルで動く関数の実装とデプロイを扱います。
- [Supabase RLSの設定手順とAuth連携ポリシー設計｜有効化漏れの検知まで](https://www.issoh.co.jp/tech/details/17056/)：db diffが拾わないポリシーの書き方を解説。
- [Supabase Realtimeの実装｜3方式の使い分けとRLS併用・同時接続の上限](https://www.issoh.co.jp/tech/details/17058/)：クラウド側でしか検証できない制限を扱います。
- [Dev Containerのメリットとは｜devcontainer.jsonの書き方と仕組みを解説](https://www.issoh.co.jp/tech/details/3728/)：開発環境をコードで揃える方法が参考になります。

---

出典: [Supabase CLIとローカル開発環境：db diffで進めるマイグレーションとCI](<https://www.issoh.co.jp/tech/details/17063/>)（株式会社一創）
