SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor ・更新日 2026-10-11

VPSでSupabaseをDockerセルフホストする方法

公式のDocker ComposeスタックをVPSで動かす手順です。11個のサービスの役割、必ず置き換える.envのSecret、必要なRAM容量、バックアップと更新方法を解説します。

構築するもの

Supabase のセルフホスティングでは、公式の Docker Compose スタックを自分のサーバーで実行します。構成には、Postgres、それを前段で利用する REST API、認証サービス、ファイルストレージ、リアルタイム WebSocket、Studio ダッシュボードが含まれます。1 つのリポジトリを clone し、1 つの .env ファイルを編集して、合計 11 個のコンテナを起動します。これらのコンテナが連携し、管理下にある Supabase プロジェクトとして動作します。

インストール自体は短時間で完了します。問題が発生しやすいのは .env ファイルです。このファイルにはリポジトリで公開されているデモ用 Secret が含まれており、そのデフォルト値のまま起動したスタックは、見つけた人なら誰でも利用できます。このガイドでは、置き換える必要がある Secret、各サービスの役割、スタックに実際に必要なメモリ容量、データベースを削除せずに更新する方法を説明します。

Compose が初めての場合は、先に VPS での Docker Compose の基本を読んでください。以下では、docker compose version でバージョンが表示される状態を前提とします。

実際に含まれる構成要素

Supabase は単一のプログラムではありません。Compose ファイルは、1 つのネットワーク上で複数の独立したサービスを起動します。それぞれの役割を把握すると、コンテナ名の一覧をデバッグ可能な構成として理解できます。

  • db は、Supabase の拡張機能を読み込んだ PostgreSQL です。他のすべてのサービスがこれと通信します。このコンテナが正常でない場合、他のサービスもすべて失敗します。
  • api-gw は API ゲートウェイです。August 2026 release 以降は Envoy が動作しています。それ以前は kong というサービスでした。Kong も opt-in override として引き続き利用できます。/rest/v1/、/auth/v1/、/storage/v1/ を適切なバックエンドへルーティングするために、port 8000 で待ち受けます。外部公開してよいコンテナはこれだけです。
  • rest は PostgREST です。Postgres のスキーマを読み取り、REST API として提供します。そのため、新しいテーブルを作成すると、コードを書かなくても新しい endpoint が追加されます。
  • auth は GoTrue です。ユーザーを識別する JSON web token (JWT) を発行します。
  • storage と imgproxy は、ファイルのアップロードと画像のリサイズを処理します。
  • realtime は、websocket 経由でデータベースの変更をストリーミングします。
  • studio と meta は、dashboard と、その背後で動作する admin API です。
  • functions は Deno ベースの edge runtime です。supavisor は Postgres の connection pooler です。

Logs and analytics(Logflare と vector)は、June 2026 にデフォルトの Compose ファイルから外れました。現在は opt-in override ファイルであり、sh run.sh config add logs を指定して有効にします。これは、複数の Compose ファイルから 1 つの stack を実行するで説明した Compose ファイルの同じレイヤー化です。

この一覧が、以下のリソース値の根拠です。実行しているのはデータベースだけではありません。データベースに加えて、10 個のサポートサービスも実行しています。

8 GB の RAM を前提にサイジングする

2026 年 7 月時点の新規インストールでは、独自のデータやトラフィックを加える前のスタックは、アイドル状態でおよそ 2.5 ~ 3 GB の常駐メモリを使用しました。この測定では、analytics サービスと Studio の Node.js プロセスが、単体では最大のメモリ使用量でした。Analytics はデフォルトのスタックに含まれなくなったため、logs の override を有効にした場合にだけメモリを消費します。2 GB のサーバーではコンテナが起動した後、いずれかのコンテナが kernel の out of memory killer によって終了します。症状は、コンテナが exit code 137 で再起動を繰り返す状態です。2026 年 10 月に確認した Supabase のセルフホスティング向けドキュメントでは、最小要件として 4 GB の RAM、2 CPU cores、40 GB の SSD、推奨要件として 8 GB、4 cores、80 GB を挙げています。

継続的に利用する環境には、8 GB の RAM と 4 vCPU を割り当ててください。4 GB でも、重いクエリと Studio のセッションを同時に実行すると遅くなることを許容できるなら、個人開発用のインスタンスとして動作します。ディスク容量も重要です。Postgres、storage volume、logs を有効にした場合の log data は、すべて project directory 配下に保存されるためです。まず 40 GB から始め、使用量を監視してください。プランを選ぶ前にサービス数を数える習慣は、セルフホストするすべての環境で役立ちます。PhotoPrism と Immichにも実用上の RAM の下限があるためです。2026 年 10 月に確認した Immich の quick start page では、少なくとも 6 GB の RAM と 2 CPU cores が必要とされています。まだプロジェクトに対してサーバーが過剰なら、Supabase の無料 cloud plan に実際に含まれる内容と比較してください。500 MB 未満で動作し、1 週間アイドル状態にならない小規模なアプリであれば、無料で実行できる可能性があります。

インストール: 公式リポジトリをクローンする

サポートされている方法では、メインリポジトリから docker ディレクトリを自分で用意したプロジェクトディレクトリへコピーします。この分離が重要なのは、後で git pull を実行しても .env が上書きされないためです。

git clone --depth 1 https://github.com/supabase/supabase
mkdir supabase-project
cp -rf supabase/docker/* supabase-project
cp supabase/docker/.env.example supabase-project/.env
cd supabase-project
docker compose pull

docker compose pull は数 GB のイメージをダウンロードします。すべてのサービスが Pulled と表示されて終了することを確認してください。ここで manifest unknown エラーが発生する場合、固定されたイメージタグが上流で削除されています。タグを手作業で編集せず、リポジトリの新しいコピーを取得してください。

初回起動前に変更する必要がある Secret

stack を起動する前に実施してください。起動後ではありません。これらの値の一部は初回起動時にデータへ書き込まれるため、後から変更するにはデータベースをリセットする必要があります。

この repository には、すべての値を正しく生成する generator が含まれています。新しい JWT secret で署名する必要がある2つの API key も生成されます。

sh utils/generate-keys.sh --update-env

この script は、JWT_SECRET、ANON_KEY、SERVICE_ROLE_KEY、SECRET_KEY_BASE、REALTIME_DB_ENC_KEY、VAULT_ENC_KEY、PG_META_CRYPTO_KEY、Logflare token、S3 protocol key、MINIO_ROOT_PASSWORD の新しい値を .env に書き込みます。2026年10月時点では、POSTGRES_PASSWORD と DASHBOARD_PASSWORD にもランダムな値を書き込みます。実行には openssl が必要です。これは通常の Ubuntu image であれば存在します。現在は database password も書き換えるため、--update-env を指定して実行するのは初回起動前だけにしてください。すでにデータを保持している stack に対して実行してはいけません。

.env では、次の2つの値を手動で確認してください。

  • POSTGRES_PASSWORD。script は安全なランダムな hex 値を設定します。独自の値に置き換える場合は、英字と数字だけを使用してください。ここに句読点を含めると、複数の service が文字列を連結して作成する connection string が壊れます。この場合、parsing error ではなく authentication error のように見えるため、誤った箇所を調べることになります。
  • DASHBOARD_USERNAME と DASHBOARD_PASSWORD。これは Studio の basic authentication credential です。script はランダムな password を設定しますが、username はデフォルトの supabase のままにします。提供時のデフォルト password は文字どおり this_password_is_insecure_and_should_be_updated です。script を実行しなかった場合は、ここで変更してください。

ANON_KEY と SERVICE_ROLE_KEY を任意に作成できない理由を理解してください。どちらも JWT_SECRET で署名された JWT です。gateway は .env にある正確な key だけを受け入れます。また、その背後の service は JWT_SECRET に対して署名を検証します。そのため、一致しない key は HTTP 401 で拒否されます。デフォルトの Envoy gateway は 401 Unauthorized を返します。現在の Kong override も同じです。これは Kong 3.9 を実行し、{"message":"Unauthorized"} を返します。2026年3月より前の install では Kong 2.8 が実行され、{"message":"Invalid authentication credentials"} を返します。これは self-hosting で最も多い失敗です。運用者が JWT_SECRET を変更しても、demo key を残してしまいます。3つすべてを常に同時に生成してください。

SERVICE_ROLE_KEY は root password と同じように扱ってください。これは row level security を完全に回避します。server side code にだけ配置し、それ以外の場所には置かないでください。

SITE_URL には app の address を設定し、API_EXTERNAL_URL には /auth/v1 を末尾に付けた API の public address を設定します。たとえば https://supabase.example.com/auth/v1 です。SUPABASE_PUBLIC_URL には、path を除いた同じ public address を設定します。Auth はこれらの値から email confirmation link と OAuth callback link を作成します。そのため、localhost のデフォルト値のままにすると、すべての user が自分自身の machine に誘導されます。

次に、設定値を確認します。

sh run.sh secrets

起動して正常性を確認する

sh run.sh start
docker compose ps

run.sh start は docker compose up -d --wait を待機するため、ヘルスチェックに合格するまで戻りません。すべてのサービスが running (healthy) または running と表示されることを確認します。初回起動には 2〜4 分かかります。Postgres が初期化スクリプトを実行してからでないと、ほかのサービスは接続できないためです。

コンテナが再起動を繰り返している場合は、サービス名を指定してログを確認します。

docker compose logs db
docker compose logs auth

Studio はポート 8000 で利用できます。設定したダッシュボードのユーザー名とパスワードの入力を求められます。

パブリックインターネットにポート 8000 を公開しない

8000 番ポートのゲートウェイは、平文の HTTP を使用します。すべての API key とユーザーパスワードがネットワーク上を平文で通過します。Studio の認証情報も、暗号化ではなく base64 エンコードを使用する basic 認証です。

ゲートウェイの前段にリバースプロキシを置き、そこで TLS (transport layer security) を終端します。さらに、ゲートウェイを loopback アドレスに bind して、他の接続元から到達できないようにします。docker-compose.yml では、api-gw service 配下の port 行を 127.0.0.1:${API_GW_HTTP_PORT:-${KONG_HTTP_PORT:-8000}}:8000/tcp に変更し、プロキシからそのポートへ転送します。2026 年 8 月に Envoy へ切り替わる前のインストールでは、同じ port 行が kong という service の配下にあります。証明書の設定については、複数の Compose アプリの前段に Traefik を置くで説明しています。同じプロキシを、この stack から 90 年代のビデオ店として再構築した Jellyfin library のような実用性を重視しないものまで、box 上の他のすべてのサービスの前段にも配置できます。各サービスには、別の open port ではなく hostname を割り当てます。dashboard を自分だけが使う場合は、プロキシを省略し、代わりに SSH tunnel 経由で loopback port へ接続します。self-hosted open-kritt でも、scanning interface をパブリックインターネットから完全に切り離すために同じ方法を使用しています。

残りのポートも firewall で閉じてください。Docker は独自の iptables ルールを書き込んでポートを公開するため、単純な ufw 設定ではそのルールが認識されません。この落とし穴については、Docker コンテナが ufw ルールを無視する理由で説明しています。

データベースをバックアップし、ディレクトリはバックアップしない

Postgres のデータは ./volumes/db/data の bind mount に保存されています。コンテナの実行中にこのディレクトリをコピーすると、不完全なコピーになります。Postgres は書き込みをバッファーし、ディスク上のファイルが整合するのは checkpoint の時点だけだからです。これを復元すると通常は動作しますが、最後のトランザクションが気付かないうちに失われることがあります。これはバックアップで起きる障害として最悪です。

代わりに dump を作成します。pg_dumpall はコンテナ内で実行され、整合性のあるスナップショットを作成します。

docker exec -t supabase-db pg_dumpall -U postgres > supabase-$(date +%F).sql

信頼する前に、ファイルが空でないことを確認してください。その後、スケジュールに従って dump をサーバー外へ転送します。これは restic による暗号化されたオフサイトバックアップで行います。スケジュールされた dump が何も通知せずに失敗すると、バックアップが存在しない場合と区別できません。そのため、cron または systemd のジョブが非ゼロで終了したときに スマートフォンへアラートを送信するよう設定してください。同時に .env もバックアップします。JWT_SECRET を失うと、発行済みのすべての token が無効になり、保存されている暗号化済みの Secret を読み取れなくなります。

アップロードされたファイルは ./volumes/storage に保存されます。これらは通常のファイルなので、単純なコピーで問題ありません。

データを失わずに更新する

Supabase は docker-compose.yml でイメージのバージョンを固定しているため、自分で変更するまで更新されません。手作業で構築するスタックでも、バージョンを固定する方法を取り入れる価値があります。そのため、セルフホスト型の RustDesk リレーでは、変動するタグを追従せず、2 つのサーバーイメージを固定しています。アップグレードは、時間に余裕のある朝に自分で実施する作業にすべきです。毎回、最初にダンプを取得してください。

docker compose pull
sh run.sh recreate

recreate はスタックを停止し、新しいイメージで再度起動します。データはコンテナ内ではなく、ホスト上の bind mount に保存されているため、保持されます。メジャーバージョンを更新する前に、リポジトリの CHANGELOG.md を確認してください。Postgres のメジャーアップグレードは別の手順であり、recreate は実行しないためです。Postgres 17 は 2026 年 6 月以降のデフォルトです。Postgres 15 の既存インストールは sudo bash utils/upgrade-pg17.sh で移行できます。このコマンドは pg_upgrade を使用してデータをインプレースで移行し、各メジャー段階で確認を求めます。すべてのコンテナが起動している必要があります。また、データベースサイズの少なくとも 2 倍に 5 GB を加えた空きディスク容量が必要です。Supabase は、開始前に独立したバックアップを自分で取得することも引き続き推奨しています。

Compose ファイル自体の変更を取り込むには、プロジェクトディレクトリで sh update.sh を実行します。先に変更内容を確認する場合は sh update.sh --dry-run を実行します。この処理は、上流のファイルと自分の編集内容をマージします。.env の既存の値を上書きすることはありません。新しいキーを追加するだけです。書き込み前に設定を backups/ にアーカイブしますが、データベースは対象外です。.supabase-version ファイルがないインストールでは、--from と、インストールを開始したリリースが必要です。特に 2026 年 8 月のリリースでは重要です。このリリースでは kong サービスが api-gw に置き換えられ、docker-compose.yml の更新が必要になりました。手動での方法も引き続き利用できます。上流のリポジトリを再度 clone し、その docker ディレクトリをプロジェクトにコピーしてください。ただし、.env を上書きしないよう注意してください。

データベースを含むすべてを破棄し、.env をデフォルトに戻す完全リセットは、別のスクリプトで実行します。以前の設定は .env.old として保持されます。実行時には確認を求められます。

sh reset.sh

FAQ

API 呼び出しが「Invalid authentication credentials」を返すのはなぜですか?

このメッセージは、2026年3月より前にインストールしたスタックで使用されていたゲートウェイ、Kong 2.8 が出力します。デフォルトの Envoy gateway と、Kong 3.9 を実行する現在の Kong override は、同じ問題を単純な 401 Unauthorized として報告します。いずれの場合も、送信した key が .env 内の ANON_KEY または SERVICE_ROLE_KEY と一致していないか、それらの key が現在 .env にある JWT_SECRET で署名されていません。まだデータを保持したことのないスタックでは、sh utils/generate-keys.sh --update-env を使って3つすべてを同時に再生成します。稼働中のスタックでは、flag を付けずにスクリプトを実行して no と回答し、新しい値だけを出力させます。JWT_SECRET、ANON_KEY、SERVICE_ROLE_KEY を手動で .env にコピーし、POSTGRES_PASSWORD は変更しません。その後、sh run.sh recreate を実行して、サービスに新しい値を読み込ませます。

2 GB の VPS で self-hosted Supabase を実行できますか?

安定した運用はできません。Supabase の self-hosting ドキュメントでは、2026年10月時点で RAM の最小要件を 4 GB としています。また、2026年7月時点の測定では、現在は opt-in になっている logging services を含め、スタックはアイドル時に約 3 GB を使用していました。2 GB のサーバーでは out of memory killer によってコンテナが停止し、docker compose ps に exit code 137 が表示されます。本番環境には 8 GB を使用し、個人開発でも 4 GB を最低限としてください。

self-hosted Supabase には edge functions が含まれますか?

はい。Compose file には Deno ベースの functions runtime が含まれており、./volumes/functions 配下に配置した内容を提供します。ただし、ホスト型プラットフォームのグローバルなデプロイネットワークは含まれません。そのため、functions は1台のサーバー上の1か所で実行されます。

Postgres database に直接接続するにはどうすればよいですか?

サーバー自体で interactive shell を使用するには docker exec -it supabase-db psql -U postgres を使います。外部クライアントから接続する場合は、port 5432 の Supavisor 経由で、user postgres.<POOLER_TENANT_ID> と POSTGRES_PASSWORD を使用します。この port をインターネットに公開しないでください。VPN または SSH tunnel 経由で接続します。

auth confirmation email のリンクが localhost になったのはなぜですか?

.env 内の SITE_URL と API_EXTERNAL_URL がデフォルト値のままになっていました。auth service は、すべての confirmation link と password reset link をこの2つの値から生成します。そのため、指定された address を送信します。両方を実際の public URL に設定し、スタックを再作成してください。

この記事は役に立ちましたか?