SupabaseをVPSでDockerセルフホストする方法
公式Supabase Docker ComposeスタックをVPSで動かす手順です。公開デモ用secretの交換、14サービスの役割、必要なRAM、バックアップ、更新方法を解説します。
構築するもの
Supabaseのセルフホスティングとは、公式のDocker Composeスタックを自分のサーバーで実行することです。Postgres、Postgresの前段で動作するREST API、認証サービス、ファイルストレージ、リアルタイムWebSocket、Studioダッシュボードが含まれます。1つのリポジトリをcloneし、1つの.envファイルを編集して、約14個のコンテナを起動します。これらが連携して、自分で管理するSupabaseプロジェクトとして動作します。
インストール自体は短時間で完了します。問題の原因になるのは.envファイルです。このファイルにはリポジトリで公開されているデモ用のsecretが含まれています。これらのデフォルト値で起動したスタックは、見つけた人なら誰でもアクセスできます。このガイドでは、置き換える必要があるsecret、各サービスの用途、スタックに必要な実際のメモリ容量、データベースを削除せずに更新する方法を説明します。
Composeを初めて使う場合は、先にVPSでのDocker Composeの基本を読んでください。以下では、docker compose versionを実行するとすでにバージョンが表示されることを前提にします。
スタックに実際に含まれるもの
Supabaseは1つのプログラムではありません。Composeファイルは、1つのネットワーク上で複数の独立したサービスを起動します。それぞれの役割を把握すると、コンテナ名の一覧をデバッグできる構成として理解できます。
dbは、Supabaseの拡張機能を読み込んだPostgreSQLです。他のすべてのサービスがこれと通信します。このコンテナが正常でない場合、他のサービスもすべて失敗します。kongはAPIゲートウェイです。port 8000でリッスンし、/rest/v1/、/auth/v1/、/storage/v1/を適切なバックエンドにルーティングします。外部に公開するコンテナは、これだけにしてください。restはPostgRESTです。Postgresのスキーマを読み取り、REST APIとして提供します。そのため、新しいテーブルを作成すると、コードを追加しなくても新しいエンドポイントになります。authはGoTrueです。ユーザーを識別するJSON Web Token (JWT)を発行します。storageとimgproxyは、ファイルのアップロードと画像のリサイズを処理します。realtimeは、websocket経由でデータベースの変更をストリーミングします。studioとmetaは、ダッシュボードと、その背後で動作する管理APIです。analytics(Logflare)とvectorはログを収集し、supavisorはPostgresの接続プールを管理します。
この一覧から、後述するリソース数の理由が分かります。実行しているのはデータベースだけではありません。データベースに加えて、12個前後のサポートサービスも実行しています。
サイジング: 8 GBのRAMを計画する
2026年7月時点で、初期状態のインストールでは、独自のデータやトラフィックを含めず、スタックは常駐メモリを約2.5〜3 GB使用します。単一のコンポーネントとして最も多く消費するのは、analyticsサービスとStudio Node.jsプロセスです。2 GBのサーバーではコンテナが起動しますが、その後、通常はanalyticsまたはdbのいずれかがkernelのout of memory killerによって終了します。症状は、コンテナがexit code 137で再起動を繰り返す状態です。
依存する用途には、8 GBのRAMと4 vCPUを割り当ててください。4 GBでも、個人用の開発インスタンスであれば動作します。ただし、負荷の高いクエリとStudioセッションを同時に実行すると遅くなります。ディスクも重要です。Postgres、storage volume、ログデータはすべてproject directory配下に保存されるためです。まず40 GBから始め、使用量を監視してください。
インストール: 公式リポジトリをクローンする
サポートされている方法では、メインリポジトリから 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 pulldocker compose pull は数GBのイメージをダウンロードします。すべてのサービスが Pulled と表示されて終了する必要があります。ここで manifest unknown エラーが発生する場合、固定されているイメージタグが上流で削除されています。タグを手動で編集せず、リポジトリの新しいコピーを取得してください。
初回起動前に変更する必要があるシークレット
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に新しい値を設定し、.envへ書き込みます。opensslが必要ですが、通常のUbuntu imageにはあらかじめ含まれています。
設定されない値が2つあります。.envで手動編集してください。
POSTGRES_PASSWORD。英字と数字だけを使用してください。ここに句読点を入れると、複数のserviceが文字列を連結して構築するconnection stringが壊れます。この失敗はparse errorではなくauthentication errorのように見えるため、原因を誤った場所で調べることになります。DASHBOARD_USERNAMEとDASHBOARD_PASSWORD。これらはStudioのbasic authentication credentialです。提供時のdefault passwordは文字どおりthis_password_is_insecure_and_should_be_updatedです。
ANON_KEYとSERVICE_ROLE_KEYを任意に作成できない理由を理解してください。どちらもJWT_SECRETで署名されたJWTです。gatewayはすべてのrequestで署名を検証するため、secretと一致しないkeyは{"message":"Invalid authentication credentials"}で拒否されます。これはself-hostingで最も多い失敗です。operatorがJWT_SECRETを変更した一方で、demo keyをそのまま使用しています。3つすべてを必ず同時に生成してください。
SERVICE_ROLE_KEYはroot passwordと同じように扱ってください。これはrow level securityを完全に迂回します。server side codeにのみ配置し、それ以外には置かないでください。
SITE_URLとAPI_EXTERNAL_URLには、ユーザーが実際にアクセスするaddressを設定してください。たとえばhttps://supabase.example.comです。Authはこれらの値からemail confirmation linkとOAuth callback linkを生成します。http://localhost:8000のままにすると、すべてのユーザーが自分のmachineへ転送されます。
次に、設定内容を確認します。
sh run.sh secrets起動して正常性を確認する
sh run.sh start
docker compose psrun.sh start は docker compose up -d --wait をラップするため、ヘルスチェックに合格するまで戻りません。すべてのサービスに running (healthy) または running が表示される必要があります。初回起動には2〜4分かかります。Postgres が初期化スクリプトを実行してからでないと、ほかのサービスは接続できないためです。
コンテナが再起動を繰り返している場合は、サービス名を指定してログを確認します。
docker compose logs db
docker compose logs authその後、Studio はポート8000で待ち受けます。設定したダッシュボードのユーザー名とパスワードの入力を求められます。
port 8000をパブリックインターネットに公開しない
Kongのport 8000は平文のHTTPを使用します。すべてのAPI keyとユーザーパスワードがネットワーク上を平文で通過します。また、Studioの認証情報はbasic authenticationであり、暗号化ではなくbase64エンコードです。
Kongの前段にreverse proxyを配置し、そこでTLS(transport layer security)を終端します。さらに、Kongをloopback addressにbindして、他からアクセスできないようにします。docker-compose.ymlでは、kongのport mappingを127.0.0.1:8000:8000に変更し、proxyからそこへ転送します。証明書については、複数のComposeアプリの前段にTraefikを配置するで説明しています。
Dockerは独自のiptablesルールを書き込んでportを公開するため、通常のufw設定では検知できません。そのため、残りのportもfirewallで閉じてください。この問題については、Dockerコンテナがufwルールを無視する理由で説明しています。
ディレクトリではなくデータベースをバックアップする
Postgresのデータは./volumes/db/dataのbind mountに保存されています。コンテナの実行中にこのディレクトリをコピーすると、不完全なコピーになります。Postgresは書き込みをバッファリングし、ディスク上のファイルが一貫した状態になるのはcheckpoint時だけだからです。これを復元すると通常は動作しますが、最後のトランザクションがひそかに失われることがあります。これはバックアップで起こり得る最悪の障害です。
代わりにdumpを作成します。pg_dumpallはコンテナ内で実行され、一貫したsnapshotを生成します。
docker exec -t supabase-db pg_dumpall -U postgres > supabase-$(date +%F).sql信頼する前に、ファイルが空でないことを確認してください。その後、スケジュールに従ってdumpをサーバー外へ転送します。これはresticによる暗号化されたオフサイトバックアップで行います。同時に.envもバックアップしてください。JWT_SECRETを失うと、発行済みのすべてのtokenが無効になり、保存された暗号化済みsecretをすべて読み取れなくなります。
アップロードされたファイルは./volumes/storageに保存されます。これらは通常のファイルなので、単純なコピーで問題ありません。
データを失わずに更新する
Supabase は docker-compose.yml でイメージのバージョンを固定しているため、自分で更新するまで変更されません。毎回、最初にダンプを取得してください。
docker compose pull
sh run.sh recreaterecreate はスタックを停止し、新しいイメージで再起動します。データが残るのは、データがコンテナ内ではなく、ホスト上の bind mount に保存されているためです。メジャーバージョンを更新する前に、リポジトリの CHANGELOG.md を確認してください。Postgres のメジャーアップグレードは自動ではなく、ダンプとリストアが必要です。
Compose ファイル自体の変更を反映するには、upstream リポジトリを再度 clone し、その docker ディレクトリをプロジェクトにコピーします。ただし、.env を上書きしないように注意してください。
データベースを含むすべてを破棄する完全なリセットは別のスクリプトで実行し、確認を求められます。
sh reset.shFAQ
API呼び出しで「Invalid authentication credentials」が返るのはなぜですか?
ANON_KEY または SERVICE_ROLE_KEY が、現在 .env に設定されている JWT_SECRET で署名されていません。ゲートウェイはすべてのリクエストで署名を検証し、一致しない場合は拒否します。sh utils/generate-keys.sh --update-env を使用して3つすべてを同時に再生成し、その後 sh run.sh recreate を実行してサービスに新しい値を読み込ませてください。
2 GBのVPSでセルフホスト型Supabaseを実行できますか?
安定して実行することはできません。2026年7月時点で、このスタックは約14個のサービスを実行するため、アイドル状態でも約3 GBを使用します。そのため、2 GBのサーバーでは out of memory killer によってコンテナが終了し、docker compose ps に終了コード137が表示されます。本番環境には8 GBを使用し、個人開発では4 GBを最低容量としてください。
セルフホスト型Supabaseにはedge functionsが含まれますか?
はい。ComposeファイルにはDenoベースのfunctions runtimeが含まれており、./volumes/functions 以下に配置した内容を提供します。ただし、ホスト型プラットフォームのグローバルなデプロイネットワークは含まれません。そのため、functionsは1台のサーバー上の1か所で実行されます。
Postgresデータベースに直接接続するにはどうすればよいですか?
サーバー自体で対話型シェルを使用するには docker exec -it supabase-db psql -U postgres を実行します。外部クライアントから接続する場合は、port 5432のSupavisor経由で、ユーザー postgres.<POOLER_TENANT_ID> と POSTGRES_PASSWORD を使用して接続してください。このportをインターネットに公開しないでください。VPNまたはSSHトンネル経由で接続します。
認証確認メールのリンクがlocalhostになったのはなぜですか?
.env の SITE_URL と API_EXTERNAL_URL がデフォルト値のままになっています。auth serviceは、すべての確認リンクとパスワードリセットリンクをこの2つの値から生成します。そのため、設定されたアドレスを送信します。両方を実際の公開URLに設定し、stackを再作成してください。