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セルフホストの構成と運用の記事で整理しました。
コンテナランタイムの選択肢と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用コンテナ環境に差し替えても 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サーバーの接続手順とスコープ設計で扱っています。アプリ側の環境変数は、このホストとローカル用の anon キーを指すだけで切り替わる構成です。Next.js 側でこの値を読んでクライアントを生成する手順はSupabase Next.js連携の実装とproxyでのセッション更新にまとめています。Edge Functionsの実行環境とデプロイ手順も同じスタックで起動するため、関数も手元で叩けます。
サインアップ確認やパスワード再設定のメールは外部へ飛ばず、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 をかけます。ローカルのデータベースを作り直し、マイグレーションを古い順に全部再適用するコマンドで、途中のファイルを書き換えても頭から通るかを毎回検証できます。命名や正規化そのものは、三層スキーマの考え方を扱った記事の観点を先に通してからファイルへ落としてください。
Studioでの変更をdb diffで拾ってファイル化する手順
SQLを手書きせず、54323 の Studio でテーブルを作る進め方もあります。その場合は変更後に supabase db diff -f add_employees を叩いてください。CLI がローカルデータベースの現状とマイグレーション適用後の状態を比較し、差分をSQLとして書き出します。
- Studio でテーブルやカラムを追加する
supabase db diff -f 変更名で差分をファイル化する- 生成されたSQLを目で読み、不要な差分を削る
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の設定手順とポリシー設計の記事で扱っています。
宣言的スキーマへ切り替えず命令型のままで進めてよい場面の条件
全案件に入れる必要はありません。テーブル数が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の比較記事で触れているORMを併用する構成では型の出所が二重化するため、どちらを正にするかを先に決めてください。ORM側のマイグレーション機構とCLIの台帳が並走する問題と、その寄せ方はSupabaseのORM連携(Prisma・Drizzle)の記事で扱っています。
認証ユーザーを含むシードデータで詰まりやすい外部キー制約の回避
業務アプリのシードで最初に詰まるのは、ユーザーに紐づくテーブルです。プロファイルや所属テーブルは 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でビルドと自動テストを設定する手順にまとめています。反映したスキーマをVercel側の本番とプレビューへ結線する手順はSupabase Vercelデプロイ連携の記事で扱っています。
アクセストークンと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アプリ開発の相談窓口で、変更頻度と体制から環境構成を一緒に決める進め方もできます。
Studio直編集を続けたチームで起きるスキーマ差分の事故例
複数人が Studio で本番を直接触る運用は、必ず壊れます。壊れ方には順序があり、最初にローカルと本番のカラム型がずれ、次に型定義の再生成でフロントエンドのビルドが落ち、最後に誰も本番のあるべき状態を答えられなくなる、という順です。
supabase db pull で本番の現状を固定し、そこを起点に差分運用へ切り替える。同時に本番プロジェクトの編集権限を絞り、DDLはCI経由のみに限定してください。権限を絞らないまま運用ルールだけ配っても、締め切り前に必ず破られます。
ローカルで再現できない機能とステージング環境における検証の分担
ローカルスタックはよくできていますが、完全な複製ではありません。読み取りレプリカ、カスタムドメイン、外部の認証プロバイダとのコールバック、本番相当のデータ量に依存する実行計画は手元では確かめられません。同時接続数の上限に絡む挙動も、Realtimeの実装と同時接続の上限を扱った記事のとおりクラウド側でしか検証できない性質です。
役割を分けます。ローカルはスキーマとロジックの検証、ステージングは外部連携とデータ量の検証、と割り切ってください。ローカルで再現しようとして 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の違いを徹底比較|料金・認証・どっちを選ぶか:製品全体の機能範囲と料金を整理しています。
- Supabase Edge Functionsとは:Deno実行環境とデプロイ・シークレット・課金単位:ローカルで動く関数の実装とデプロイを扱います。
- Supabase RLSの設定手順とAuth連携ポリシー設計|有効化漏れの検知まで:db diffが拾わないポリシーの書き方を解説。
- Supabase Realtimeの実装|3方式の使い分けとRLS併用・同時接続の上限:クラウド側でしか検証できない制限を扱います。
- Dev Containerのメリットとは|devcontainer.jsonの書き方と仕組みを解説:開発環境をコードで揃える方法が参考になります。