個人開発でSupabaseを使っていると、いつかこの壁に当たります。
- 3つ目のサービスを作ろうとしたら、無料枠の「アクティブ2プロジェクト」上限に引っかかった
- まだ収益もユーザーもいない実験サービスのために、有料プランへ上げる決心はつかない
- 枠を空けるために古いプロジェクトを消したいが、「本当に消して大丈夫か」が怖くて手が止まる
この記事では、筆者が実際にやっている解決策 — 1つのSupabaseプロジェクトに複数サービスを「相乗り」させ、テーブル名のprefixで名前空間を分ける運用 — を紹介します。
この記事で分かること
- 「1サービス=1プロジェクト」をやめて相乗りさせるという発想
- テーブルprefixによる名前空間設計の具体例
- 相乗り運用で事故らないための4つのルール(台帳・撤退・RLS・マイグレーション)
- PostgreSQLのschema分割との比較、相乗りのデメリットと「卒業」の判断基準
前提:Supabase無料プランは「アクティブ2プロジェクト」まで
Supabaseの無料プランには、アクティブにできるプロジェクト数の上限があります。執筆時点(2026年7月)で2プロジェクトです。
⚠️ プラン仕様は変わる可能性があります。最新の上限・条件は必ずSupabase公式のPricingページ( https://supabase.com/pricing )で確認してください。
2つと聞くと十分に思えますが、個人開発者のサービスは2個では止まりません。X自動投稿ボット、リサーチツール、ログ収集、次のアイデアの実験——「DBが欲しい小さなサービス」は増え続けます。そして3つ目を作った瞬間に詰みます。
このとき取れる選択肢は、大きく3つです。
| 選択肢 | 問題点 |
|---|---|
| 有料プランに上げる | まだ検証段階のサービスに固定費を払うことになる |
| 既存プロジェクトを消して枠を空ける | 稼働中サービスのデータを失うリスク。撤退判断のたびにDBが人質になる |
| 1プロジェクトに相乗りさせる | 設計と運用ルールが必要(→本記事) |
解決策:テーブルprefixで名前空間を分けて相乗りさせる
発想の転換はシンプルで、「1サービス=1プロジェクト」という前提を捨てることです。
Supabaseのプロジェクトの実体は1つのPostgreSQLデータベースです。個人開発の小規模サービスであれば、複数サービスのテーブルが1つのDBに同居しても構造上は何の問題もありません。問題は「どのテーブルがどのサービスのものか」が分からなくなることなので、そこを**テーブル名のprefix(接頭辞)**で解決します。
命名例
| サービス | prefix | テーブル例 |
|---|---|---|
| メディア自動投稿 | media_ |
media_posts, media_post_logs
|
| 商品リサーチツール | radar_ |
radar_products, radar_price_history
|
| 通知ボット | bot_ |
bot_logs, bot_subscribers
|
命名のポイントは3つです。
-
prefixは「サービスの実体が分かる短い英単語」にする。
app1_のような連番は半年後に読めなくなります - 例外なく全テーブルに付ける。「これは共通で使うから」とprefixなしのテーブルを作ると、撤退時の棚卸しで必ず迷子になります
- ダッシュボードのテーブル一覧は名前順に並ぶので、prefixを揃えるだけでサービスごとに固まって表示される。これが運用上、地味に効きます
相乗り運用で事故らないための4ルール
相乗りは「入れるだけ」なら誰でもできますが、事故らずに回し続けるには運用ルールが要ります。筆者が実運用で守っているのは次の4つです。
| # | ルール | 破るとどうなるか |
|---|---|---|
| 1 | prefix台帳を必ず残す | どのテーブルがどのサービスのものか誰にも分からなくなる |
| 2 | プロジェクトごと削除は絶対にしない | 同居中の稼働サービスを巻き込んで消す |
| 3 | RLSとキーの扱いをサービスごとに分けて設計する | あるサービス経由で別サービスのデータに届いてしまう |
| 4 | マイグレーションもprefixごとに管理する | 変更履歴がサービス横断で混ざり、追跡不能になる |
ルール1:どのprefixがどのサービスか、台帳を必ず残す
READMEでもリポジトリ内のMarkdownでも構いませんが、「このプロジェクトには何と何が相乗りしていて、それぞれのprefixは何か」の対応表を必ず文書化します。
## Supabase相乗り台帳(プロジェクト: main-shared)
| prefix | サービス | 状態 | リポジトリ |
|---------|--------------------|--------|-------------------|
| media_ | メディア自動投稿 | 稼働中 | infra_media_bot |
| radar_ | 商品リサーチ | 稼働中 | infra_radar |
| bot_ | 通知ボット | 撤退済 | (アーカイブ) |
頭の中にしかない対応表は、数ヶ月後の自分には存在しないのと同じです。
ルール2:撤退してもプロジェクトは消さない。棚卸しはテーブル単位で
相乗り運用でいちばん危険な操作が「プロジェクト削除」です。あるサービスから撤退したとき、単独プロジェクトの感覚で「もう使ってないから」とプロジェクトを消すと、同居している稼働中サービスのデータごと消えます。
撤退時の片付けは、必ずテーブル単位で行います。
- 台帳でそのprefixのテーブルを洗い出す
- 必要ならエクスポートしてから、
DROP TABLEをテーブル単位で実行する - 台帳の状態を「撤退済」に更新する
「プロジェクト削除ボタンは押さない」を鉄の掟にしておくだけで、最悪の事故は防げます。
ルール3:anonキーは全サービス共有になる。RLSとアクセス経路を分けて考える
相乗りの技術的な急所はここです。Supabaseのanonキー(クライアント公開用キー)はプロジェクト単位なので、相乗りした全サービスで同じanonキーを共有することになります。つまり、サービスAのフロントエンドに埋めたキーは、理屈の上ではサービスBのテーブルにも届き得ます。
筆者の設計判断は次の通りです。
- 基本はサーバーサイド経由に寄せる。クライアントからSupabaseを直接叩かず、API Route等のサーバー側からアクセスする構成にすれば、anonキー共有の問題面はかなり小さくなります。サーバー専用キーは環境変数でサーバーにのみ置きます
- クライアント直アクセスがどうしても必要なテーブルだけ、RLSポリシーをサービス単位で明示的に書く。「RLSはprefixごとに設計する」と決めておくと、ポリシーの抜け漏れに気づきやすくなります
単独プロジェクトなら「anonキー+RLS」で素直に済む場面でも、相乗りでは「このキーは他サービスにも届く」ことを常に前提に置く必要があります。
ルール4:マイグレーションもprefixごとに管理する
マイグレーションファイルにもサービスのprefix(またはサービス名)を入れておくと、後から「このサービスのスキーマ変更履歴」だけを追えます。
migrations/
media_001_create_posts.sql
media_002_add_post_logs.sql
radar_001_create_products.sql
これをやらずに時系列だけで積むと、複数サービスの変更が1本の履歴に混ざり、「radar側だけロールバックしたい」ときに泣きます。
PostgreSQLのschema分割と比べてどうか
「名前空間を分けたいならPostgreSQLのスキーマ機能(CREATE SCHEMA media; → media.posts)を使うべきでは?」という意見は当然あります。比較すると次の通りです。
| 観点 | prefix方式(public内) | schema分割方式 |
|---|---|---|
| 名前空間としての厳密さ | 命名規約ベース(緩い) | DBレベルで分離(厳密) |
| Supabaseダッシュボードとの相性 | デフォルト表示にそのまま載る | スキーマ切替の操作が挟まる |
| クライアントライブラリ | デフォルト(publicスキーマ)でそのまま動く | 公開スキーマの追加設定やスキーマ指定が必要 |
| 導入コスト | テーブル名を決めるだけ | スキーマ作成・公開設定・接続側の指定 |
schema分割の方がデータベース設計としては「正しい」形です。ただし、Supabaseのダッシュボードやクライアントライブラリはpublicスキーマをデフォルトの前提にしているため、カスタムスキーマ運用は一手間ずつ設定が増えます。
個人開発の相乗りで欲しいのは厳密な分離ではなく「迷子にならないこと」なので、ツールのデフォルトの路線から外れないprefix方式で十分、というのが筆者の判断です。分離の厳密さが本当に必要になったときは、後述する「卒業」を検討するタイミングです。
デメリットも正直に:相乗りは「ずっと」の解ではない
相乗りには、はっきりしたデメリットがあります。
- 容量・負荷の枠を全サービスで共有する。無料枠のDB容量や帯域は1プロジェクト分を分け合うことになり、1つのサービスが枠を食いつぶすと全員が困ります
- 事故時の巻き込み範囲が広い。プロジェクトが止まれば相乗りしている全サービスが止まりますし、SQLの誤操作が他サービスのテーブルに及ぶ可能性も単独運用より高くなります
- いつか分離したくなったときに移行コストがかかる。テーブルのエクスポート/インポートに加え、接続情報・キー・RLSの作り直しが必要です
だからこそ、相乗りは「検証段階のサービスを安く並走させるための設計」と割り切り、卒業の基準を決めておきます。筆者は次のどれかに当たったら、専用プロジェクト(有料プラン含む)への移行を検討することにしています。
- 外部ユーザーが本格的に付き、止められないサービスになった
- 1つのサービスが容量・負荷の共有枠を明らかに食い始めた
- クライアント直アクセスが本格的に必要になり、キー共有前提のRLS設計が複雑になりすぎた
成長したサービスを卒業させれば無料枠の相乗りプロジェクトには余白が戻り、また次の実験を安く始められます。
まとめ
- Supabase無料プランはアクティブプロジェクト数に上限がある(執筆時点で2。最新は公式Pricingページで要確認)
- 「1サービス=1プロジェクト」を捨て、テーブルprefixで名前空間を分けて1プロジェクトに相乗りさせれば、3つ目以降のサービスも無料枠で並走できる
- 事故防止の4ルール:①prefix台帳を残す ②プロジェクトごと削除は絶対にしない(棚卸しはテーブル単位) ③anonキー共有を前提にRLSとアクセス経路を設計する ④マイグレーションもprefixごとに管理する
- schema分割より厳密さは劣るが、Supabaseのデフォルト(publicスキーマ)とそのまま噛み合うのがprefix方式の実利
- 相乗りは検証段階の設計。成長したサービスは専用プロジェクトへ卒業させ、空いた枠で次の実験を回す
無料枠の上限は「制約」ですが、相乗り設計にしてからは「どのサービスを本気で育てるか」を考えるきっかけにもなっています。同じ悩みを持つ個人開発者の参考になれば幸いです。