読了 約 9 分 Cosoado Lab

Vercel で 1 リポジトリを 4 プロジェクトに紐付け、env var だけで別アプリ化する実例

Cosoado Lab は Next.js 1 リポジトリから 4 つのアプリ (SparMate / NetaPair / BoardLink + staging) を Vercel で並列稼働させている。同じ GitHub repo を 4 プロジェクトに紐付け、NEXT_PUBLIC_GENRE という env var だけ切り替える具体的な構成と、Hobby プランの concurrent build / build cache 非共有 / preview deploy 連鎖など 4 つの落とし穴を実例で解説する。

はじめに

Cosoado Lab では Next.js 1 リポジトリから 4 つのアプリを並列で動かしています(格闘技・お笑い・ボードゲーム向けマッチングアプリ + staging)。仕組みは至ってシンプルで、同じ GitHub リポジトリを Vercel の 4 プロジェクトに紐付け、NEXT_PUBLIC_GENRE という env var だけプロジェクトごとに変える——それだけです。

この記事は次のような個人開発者向け。

最後まで読むと、1 回の git push で 3 本番 + 1 staging が並列ビルドされる構成を再現できます。

構成の全体像

プロジェクト環境変数ドメイン役割
martial-matchingNEXT_PUBLIC_GENRE=martialsparmate.cosoado-lab.com格闘技
comedy-matchingNEXT_PUBLIC_GENRE=comedynetapair.cosoado-lab.comお笑い
boardgame-matchingNEXT_PUBLIC_GENRE=boardgameboardlink.cosoado-lab.comボドゲ
matching-app-template(未設定)(preview のみ)staging

ソースは Next.js App Router + Supabase + Vercel という退屈なスタック。ジャンルごとの差分は、たった 1 つの設定ファイルに集約しています。

手順

1. Vercel で同じ repo を複数プロジェクトに紐付ける

Vercel ダッシュボードで New Project → GitHub から同じリポジトリを選ぶ、を 4 回繰り返すだけ。同じ repo を選び直すと「Already imported」の警告が出ますが、プロジェクト名を変えれば普通に 2 つ目以降を作れます

公式ドキュメントには明記されていませんが、Vercel は 1 GitHub repo に対して N 個のプロジェクトを許容します。これがこの構成の根幹です。

2. プロジェクトごとに env var を切り替える

Vercel CLI で一気に設定する例。

npx vercel link --project martial-matching --yes
echo "martial" | npx vercel env add NEXT_PUBLIC_GENRE production

npx vercel link --project comedy-matching --yes
echo "comedy" | npx vercel env add NEXT_PUBLIC_GENRE production

npx vercel link --project boardgame-matching --yes
echo "boardgame" | npx vercel env add NEXT_PUBLIC_GENRE production

3. アプリ側で env var を読む

// src/config/genre.ts (記事用に 3 ジャンル抜粋。実テンプレは N ジャンル対応で追加可能)
type Genre = 'martial' | 'comedy' | 'boardgame';

export const GENRE_CONFIGS: Record<Genre, GenreConfig> = {
  martial: {
    name: 'SparMate',
    primaryColor: '#dc2626',
    defaultLanguage: 'en',
    featureFlags: { locationRequired: true, superLikeEnabled: false },
  },
  comedy: {
    name: 'NetaPair',
    primaryColor: '#f97316',
    defaultLanguage: 'ja',
    featureFlags: { locationRequired: false, superLikeEnabled: true },
  },
  boardgame: {
    name: 'BoardLink',
    primaryColor: '#7c3aed',
    defaultLanguage: 'ja',
    featureFlags: { locationRequired: true, superLikeEnabled: false },
  },
};

export function getGenreConfig(): GenreConfig {
  const g = (process.env.NEXT_PUBLIC_GENRE ?? 'martial') as Genre;
  return GENRE_CONFIGS[g];
}

NEXT_PUBLIC_ プレフィックスを付けているのは、クライアント側の UI もこの値を読むため。

4. ドメインを各プロジェクトに割り当てる

各プロジェクトの Settings → Domains で、対応するサブドメインを登録します。DNS は親ドメイン側で CNAMEcname.vercel-dns.com に向けるだけ。

やってみて初めて気づいた落とし穴 4 つ

先に全体像をまとめると以下の 4 つ。Hobby プランで N プロジェクトを運用する場合、ビルド時間と env var まわりが事前認識ポイントになる。

#落とし穴影響対策
1Hobby の concurrent build = 1N プロジェクト直列で N 倍待つPro 移行 or 緊急時のみ許容
2env var 設定漏れで全アプリ same UIデフォルト値で全部同じ表示getGenreConfig で fail-fast
3Build cache プロジェクト間で非共有初回 N 倍のビルド時間許容 or 自前構成移行
4全プロジェクトが preview をビルドPR 1 本で N プロジェクトが走るvercel.jsondeploymentEnabled 制御

落とし穴 1: Hobby プランの concurrent build は 1(=N 倍待つ)

これが個人開発で多プロジェクト構成を取るときの最大の現実。Vercel Limits 表によると、Hobby の Concurrent Builds は 1。Pro は 12。

つまり 4 プロジェクトを同じ commit で push すると、4 つのビルドが直列に流れます。1 ビルドが 2 分なら、最後のプロジェクトが緑になるまで 8 分。3 倍 4 倍待つことを最初から覚悟しておくほうが精神衛生上良い。

頻繁に push する開発フェーズでは Pro 移行($20/mo)が現実的な選択肢になる。Cosoado Lab の場合は本番が安定運用フェーズに入っているので Hobby 据え置き。緊急の hotfix が必要なときだけ「直列で 8 分待つ」のは許容しています。

落とし穴 2: env var を設定し忘れて全部 same UI になる

これが一番やった失敗。新しい Vercel プロジェクトを作った瞬間、env var は空。デフォルト値が 'martial' で起動するので、全ジャンル SparMate の UI で出てきます。

対策はシンプルで、getGenreConfig の中で「env var が未設定なら build を fail させる」検証を入れる方が安全。

export function getGenreConfig(): GenreConfig {
  const g = process.env.NEXT_PUBLIC_GENRE;
  if (!g || !(g in GENRE_CONFIGS)) {
    throw new Error(`NEXT_PUBLIC_GENRE is missing or invalid: "${g}"`);
  }
  return GENRE_CONFIGS[g as Genre];
}

これで env var 設定漏れがあれば即ビルドが落ちます。

落とし穴 3: Build Cache はプロジェクト間で共有されない

同じソース・同じ node_modules でも、プロジェクトごとに .next/cache は別物。初回ビルドは 4 プロジェクト分まるまる走ります。

回避策は今のところ無し。許容するか、複数プロジェクトの初回ビルド時間が痛いならモノレポ + next start 系の自前構成への移行を検討。

落とし穴 4: ブランチを切ると 4 プロジェクトすべてが preview をビルドする

落とし穴 1 と合わせるとここが効きます。git push origin feature/foo4 プロジェクト全部が preview deploy のキューに積まれ、Hobby なら直列に 4 本ビルドする。1 PR の体感ビルド時間が一気に伸びる。

対策は vercel.jsongit.deploymentEnabled で、本番 3 プロジェクトだけ「特定ブランチでビルドしない」設定を入れること。minimatch シンタックスで指定できる (Vercel docs — Project Configuration / git)。

{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "git": {
    "deploymentEnabled": {
      "internal-*": false
    }
  }
}

上の例は internal-* で始まるブランチを当該プロジェクトでデプロイしません。これをプロジェクトごとに使い分け、staging だけ全ブランチを許可、本番 3 つは内部開発ブランチを除外する、といった構成にすると preview ビルド枠を無駄に消費しません。

まとめ

この構成で「あと 1 人」を見つけるマッチングアプリを 3 ジャンル運用しています。次回は NEXT_PUBLIC_GENRE 1 つで配色・言語・機能フラグを切り替えるアプリ側の設計を書く予定。

関連プロダクト

他のプロダクト by Cosoado Lab

TsuriMate 釣り仲間・船割りメンバーマッチング YUMELIA 夢占い・数秘術が無料でできる AI 占い Web アプリ OshiVista 推し活を 1 つにまとめる管理アプリ(多言語対応) 謎かけメーカー AI と「その心は?」を競うなぞかけ・大喜利