Supabaseセルフホスティング - 複数サーバーからの利用

作成日:
Supabase self-hosting PostgreSQL backend infrastructure multi-tenant

概要

Supabaseをセルフホスティングし、1台のサーバーにセットアップしたインスタンスを複数の異なるサーバー(アプリケーション)から利用する構成について解説します。

結論: この構成は有効であり、一般的なユースケースです。

Supabaseは標準的なクライアント-サーバーアーキテクチャを採用しており、REST API・WebSocket経由でどこからでも接続可能です。

この構成のメリット

1. コスト効率

  • 1つのSupabaseインスタンスを複数のアプリで共有することで、インフラコストを削減
  • クラウド版Supabaseの無料枠制限(プロジェクト数制限など)を回避

2. データの一元管理

  • 複数のアプリ間でデータを共有可能
  • 分析・レポーティングが容易
  • データの整合性を維持しやすい

3. 運用の簡素化

  • 1つのデータベースインスタンスの管理で済む
  • バックアップ戦略を一元化
  • 監視・アラートの設定が簡単

4. ベンダーロックインの回避

  • 完全に自分の管理下にある
  • いつでも移行可能
  • コストの完全な予測が可能

アーキテクチャ

基本構成

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│  Server A       │    │  Server B       │    │  Server C       │
│  (Webアプリ1)   │    │  (Webアプリ2)   │    │  (モバイルAPI)  │
│                 │    │                 │    │                 │
│  Next.js等      │    │  Laravel等      │    │  Express等      │
└────────┬────────┘    └────────┬────────┘    └────────┬────────┘
         │                      │                      │
         │   HTTPS (REST API / WebSocket / GraphQL)    │
         └──────────────────────┼──────────────────────┘

                    ┌───────────▼───────────┐
                    │  Self-hosted Supabase │
                    │  (VPS / 専用サーバー)  │
                    │                       │
                    │  ┌─────────────────┐  │
                    │  │ Traefik (TLS)   │  │
                    │  └────────┬────────┘  │
                    │           │           │
                    │  ┌────────▼────────┐  │
                    │  │ Kong (API GW)   │  │
                    │  │ Port: 8000      │  │
                    │  └────────┬────────┘  │
                    │           │           │
                    │  ┌────────┴────────┐  │
                    │  │                 │  │
                    │  ▼                 ▼  │
                    │ PostgreSQL    GoTrue  │
                    │ PostgREST    Realtime │
                    │ Storage       Meta    │
                    └───────────────────────┘

Supabaseサービス一覧

サービス役割デフォルトポート
PostgreSQLデータベース5432
KongAPI Gateway(外部公開)8000
PostgRESTREST API3000
GoTrue認証サービス9999
RealtimeWebSocket通信4000
Storageファイル管理5000
Studio管理画面3000

セットアップ手順

1. Supabaseのセルフホスティング

# Supabase Dockerリポジトリをクローン
git clone --depth 1 https://github.com/supabase/supabase
cd supabase/docker

# 環境変数ファイルをコピー
cp .env.example .env

# 重要: .envファイルを編集
# - POSTGRES_PASSWORD を強力なパスワードに変更
# - JWT_SECRET を変更
# - ANON_KEY, SERVICE_ROLE_KEY を再生成

2. 外部アクセス用の設定

.env ファイルの設定例:

# 外部からアクセスするためのURL
SITE_URL=https://supabase.example.com
API_EXTERNAL_URL=https://api.supabase.example.com

# セキュリティ設定
POSTGRES_PASSWORD=your-super-secure-password
JWT_SECRET=your-super-secret-jwt-token-with-at-least-32-characters

3. Traefikとの統合(推奨)

docker-compose.override.yml の例:

services:
  kong:
    networks:
      - default
      - traefik
    labels:
      - traefik.enable=true
      - traefik.http.routers.supabase-api.rule=Host(`api.supabase.example.com`)
      - traefik.http.routers.supabase-api.entrypoints=websecure
      - traefik.http.routers.supabase-api.tls=true
      - traefik.http.routers.supabase-api.tls.certresolver=letsencrypt
      - traefik.http.services.supabase-api.loadbalancer.server.port=8000

  studio:
    networks:
      - default
      - traefik
    labels:
      - traefik.enable=true
      - traefik.http.routers.supabase-studio.rule=Host(`studio.supabase.example.com`)
      - traefik.http.routers.supabase-studio.entrypoints=websecure
      - traefik.http.routers.supabase-studio.tls=true
      - traefik.http.routers.supabase-studio.tls.certresolver=letsencrypt
      - traefik.http.services.supabase-studio.loadbalancer.server.port=3000

networks:
  traefik:
    external: true

4. 起動

docker-compose up -d

クライアント側の接続設定

JavaScript/TypeScript(各サーバーで共通)

import { createClient } from '@supabase/supabase-js'

// セルフホストSupabaseへの接続
const supabaseUrl = 'https://api.supabase.example.com'
const supabaseKey = 'your-publishable-key'

const supabase = createClient(supabaseUrl, supabaseKey)

// 通常通りデータベース操作が可能
const { data, error } = await supabase
  .from('users')
  .select('*')

環境変数での管理(推奨)

各サーバーの .env ファイル:

# Server A (.env)
SUPABASE_URL=https://api.supabase.example.com
SUPABASE_ANON_KEY=your-publishable-key
SUPABASE_SERVICE_KEY=your-secret-key  # サーバーサイドのみ
// 環境変数から読み込み
const supabase = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_ANON_KEY!
)

マルチテナント設計パターン

複数のアプリケーションが1つのSupabaseインスタンスを共有する場合の設計パターンを紹介します。

パターン1: スキーマ分離(推奨)

各アプリケーションに専用のPostgreSQLスキーマを割り当てる方法です。

-- アプリごとにスキーマを作成
CREATE SCHEMA app_a;
CREATE SCHEMA app_b;
CREATE SCHEMA app_c;

-- app_a用のテーブル
CREATE TABLE app_a.users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email TEXT UNIQUE NOT NULL,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- app_b用のテーブル
CREATE TABLE app_b.products (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name TEXT NOT NULL,
  price DECIMAL(10, 2)
);

メリット:

  • 完全なデータ分離
  • 各アプリの独立したマイグレーション
  • セキュリティが明確

パターン2: テナントIDによる分離

同じテーブルを共有し、テナントIDで分離する方法です。

-- 共有テーブルにtenant_idカラムを追加
CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id TEXT NOT NULL,  -- 'app_a', 'app_b', 'app_c'
  email TEXT NOT NULL,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  UNIQUE(tenant_id, email)
);

-- RLSでテナント分離
ALTER TABLE users ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Tenant isolation"
ON users
USING (tenant_id = current_setting('app.tenant_id', true));

メリット:

  • シンプルな構成
  • データの横断分析が容易

パターン3: 共有データ + 個別データのハイブリッド

-- 共有データ(publicスキーマ)
CREATE TABLE public.master_categories (
  id SERIAL PRIMARY KEY,
  name TEXT NOT NULL
);

-- 個別データ(各アプリのスキーマ)
CREATE TABLE app_a.orders (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  category_id INTEGER REFERENCES public.master_categories(id),
  user_id UUID NOT NULL,
  total DECIMAL(10, 2)
);

セキュリティ考慮事項

1. ネットワークセキュリティ

設定項目推奨設定
TLS/SSL必須(Traefikで終端推奨)
ファイアウォールKong(8000)とStudio(3000)のみ外部公開
PostgreSQL外部からの直接接続を禁止(5432は内部のみ)
IP制限可能であれば接続元IPを制限

2. 認証・認可

-- RLSを必ず有効化
ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;

-- アプリごとに異なるポリシーを設定可能
CREATE POLICY "App A users only"
ON app_a.users
FOR ALL
USING (
  auth.jwt() ->> 'app_id' = 'app_a'
);

3. APIキーの管理

キーの種類用途保管場所
publishable keyフロントエンドクライアントコード(公開可)
secret keyサーバーサイド環境変数のみ(絶対に公開しない)

各サーバーで異なるJWTを使用する場合:

// カスタムJWTを使用してアプリを識別
const supabase = createClient(supabaseUrl, supabaseKey, {
  global: {
    headers: {
      'x-app-id': 'app_a'
    }
  }
})

パフォーマンス考慮事項

1. レイテンシ対策

要因対策
物理的距離Supabaseサーバーを接続元の近くに配置
接続数コネクションプーリングを使用
クエリ最適化適切なインデックスを設定

2. リソース配分

# docker-compose.override.yml でリソース制限
services:
  db:
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 4G
        reservations:
          cpus: '1'
          memory: 2G

3. 監視

# PostgreSQLの接続数確認
SELECT count(*) FROM pg_stat_activity;

# アクティブなクエリの確認
SELECT pid, query, state, wait_event_type 
FROM pg_stat_activity 
WHERE state = 'active';

注意点・制限事項

1. スケーラビリティの限界

  • 複数アプリの負荷が1つのインスタンスに集中
  • 高負荷時は水平スケーリングが困難(PostgreSQLのレプリケーション設定が必要)

2. 障害の影響範囲

  • Supabaseがダウンすると全アプリに影響
  • 高可用性が必要な場合はレプリケーション構成を検討

3. アップグレード時の調整

  • Supabaseのアップグレード時に全アプリのテストが必要
  • マイグレーションの調整が必要な場合がある

代替構成

複数Supabaseインスタンスを使用する場合

完全な分離が必要な場合は、アプリごとに別のSupabaseインスタンスを立てることも検討できます。

App A → Supabase Instance A (supabase-a.example.com)
App B → Supabase Instance B (supabase-b.example.com)
App C → Supabase Instance C (supabase-c.example.com)

この構成が適している場合:

  • 各アプリの負荷が大きく異なる
  • 完全な障害分離が必要
  • 各アプリのチームが独立して運用

まとめ

観点評価
技術的な実現可能性✅ 問題なし
コスト効率✅ 高い
運用の複雑さ⚠️ 中程度(マルチテナント設計が必要)
スケーラビリティ⚠️ 計画的な設計が必要
セキュリティ✅ RLSで十分に対応可能

推奨されるユースケース:

  • 個人・小規模チームでの複数アプリ開発
  • 関連性のあるアプリ群でのデータ共有
  • コストを抑えたい場合

非推奨のユースケース:

  • 各アプリに完全な独立性が必要な場合
  • 極めて高い可用性が求められる場合
  • 各アプリの負荷が予測できない場合

関連トピック

参考リンク