SSD Nodes Learn 🎉 VPS $5.50/月〜
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-13

Open ConnectorをVPSでセルフホストする方法

AIエージェントにSaaSトークンを持たせず、Open Connectorを自分のVPSで運用します。固定イメージ、TLS origin、OAuth callback、SQLiteバックアップの設定を解説します。

AI エージェントに対する Open Connector の役割

Open Connector をセルフホストすると、AI エージェントと、エージェントが呼び出すすべての software as a service (SaaS) API の間に、1 つの認証ゲートウェイを置けます。そのため、エージェントがプロバイダーのトークンを保持する必要はありません。これは OOMOL Lab が提供するオープンソースのゲートウェイで、Apache 2.0 ライセンスで提供されています。1 つのコンテナとして動作し、状態を単一の SQLite ファイルに保存し、プロバイダーのアクションを HTTP と MCP (model context protocol) で公開します。

問題は、2 つ目のインテグレーションから始まります。プロバイダーごとに OAuth (open authorization) フロー、refresh token の有効期間、scope 名が異なります。5 つのプロバイダーを手作業でエージェントに接続するには、5 つの redirect handler、5 つの credential store、トークンの期限が切れる前に実行する 5 つの refresh loop が必要です。このコードを作成する人はほとんどいません。その代わり、サービスごとに有効期間の長い personal access token を 1 つ発行し、エージェントの設定、環境ファイル、またはプロンプト自体に貼り付けます。そのトークンはエージェントが実行するすべてのツールから読み取れる状態になり、transcript にも記録されます。これは、AI エージェントからシークレットを排除するで説明している問題です。

認証ゲートウェイは、credential を 2 つに分離します。ゲートウェイがプロバイダーの credential を保存し、OAuth フローを実行します。エージェントが受け取るのは、ゲートウェイに対してのみ有効な runtime token です。エージェントがアクションを呼び出すと、ゲートウェイは保存済みの credential を読み込み、サーバー側で外向きのリクエストに挿入し、レスポンス本文だけを返します。エージェントがプロバイダーの access token を受け取ることはありません。そのため、エージェントの transcript が漏えいしても、失うのは GitHub アカウントではなく、失効可能な runtime token 1 つです。

カタログでは、1,000 を超えるプロバイダーと 10,000 の事前構築済みアクションを提供するとされています。これはプロジェクト独自の数値であり、外部から検証できるものではありません。検証できるのは構成です。アクションごとに HTTP endpoint が 1 つ、プロバイダーごとに保存済み connection が 1 つ、エージェントごとに token が 1 つあります。

ホスト型コネクターサービスではなく Open Connector を自己ホストする理由

ホスト型コネクターサービスも同じ処理を行いますが、接続するすべてのプロバイダーの refresh token を保持します。Google や GitHub の refresh token は、メールやリポジトリへの長期間有効な暗号鍵であり、通常はパスワードを変更しても無効になりません。ホスト型サービスが侵害されると、あなたの環境も侵害されます。自己ホストすれば、これらの記録は自分で借りて管理するマシン上の SQLite に保存されます。鍵はそのマシンの外部に出ません。

開始する前に、コストを明確に把握してください。この VPS は、運用する中で最も価値の高いサーバーになります。1 つのファイルに多数のサービスの有効な認証情報を保持するため、パスワードマネージャーのホストと同じ扱いが必要です。つまり、443 だけを公開するファイアウォール、共有ログインの禁止、一度は実際にリストアしたバックアップ、応答しなくなったときのアラートが必要です。このマシンにパスワード保管庫を置けないなら、コネクターも置かないでください。

インストール前にバージョンを固定する

Open Connector は新しいソフトウェアです。リポジトリが最初に登場したのは 29 June 2026 で、2026 年 8 月 1 日時点の最新タグ付きリリースは v1.3.3 です。このリリースは 30 July 2026 に公開され、latest タグも付いています。レジストリには tip タグも公開されています。このタグは main 上の最新コミットからビルドされています。

このように新しいプロジェクトでは、変動するタグが頻繁に更新されます。2 つ先のリリースへ移動する docker compose pull によって、エージェントが依存するエンドポイントが変更されることがあります。その結果、エージェントの問題だと考えて、夜通し原因を調査することになりかねません。イメージはリリースタグで固定し、リリースノートを確認してから、必要なタイミングでアップグレードしてください。

TLS 終端を担うリバースプロキシの背後で、自分の VPS に Open Connector をデプロイする

コンテナを起動する前に、次のものが必要です。

  • Ubuntu 24.04 またはそれに近い環境上の、Compose plugin を備えた Docker
  • A レコードがこの VPS を指すホスト名。例: connect.example.com
  • そのホスト名の TLS (transport layer security) をすでに終端しているリバースプロキシ
  • 以下で生成するランダムな Secret 2 つ

複数の Docker Compose アプリに対応する Traefik リバースプロキシでは、プロキシ側の構成を説明しています。1 つのアプリについて、証明書の設定から起動までを扱う内容は、Docker と HTTPS を使って VPS 上で n8n を運用するガイドにあります。

最初に Secret を生成します。暗号化キーは保存された認証情報を保護します。管理者トークンは Web コンソールと /api 全体を保護します。どちらにもデフォルト値はなく、これらを設定しなくてもランタイムは正常に起動します。

mkdir -p ~/open-connector && cd ~/open-connector
umask 077
printf 'OOMOL_CONNECT_ENCRYPTION_KEY=%s\n' "$(openssl rand -base64 32)" > .env
printf 'OOMOL_CONNECT_ADMIN_TOKEN=%s\n' "$(openssl rand -base64 32)" >> .env
chmod 600 .env

最初の起動前に、2 つの値をパスワードマネージャーへコピーしてください。暗号化キーには復旧手段がありません。その理由は、後述の障害一覧に記載しています。

次に compose.yaml を作成します。upstream の例とは 2 箇所異なり、どちらも重要です。

services:
  connector:
    image: ghcr.io/oomol-lab/open-connector:v1.3.3
    restart: unless-stopped
    ports:
      - "127.0.0.1:3000:3000"
    volumes:
      - connector-data:/app/data
    environment:
      OOMOL_CONNECT_DATA_DIR: /app/data
      OOMOL_CONNECT_ORIGIN: "https://connect.example.com"
      OOMOL_CONNECT_ENCRYPTION_KEY: "${OOMOL_CONNECT_ENCRYPTION_KEY:?set this in .env}"
      OOMOL_CONNECT_ADMIN_TOKEN: "${OOMOL_CONNECT_ADMIN_TOKEN:?set this in .env}"

volumes:
  connector-data:

1 つ目の変更は、latest ではなくタグを固定していることです。2 つ目はポートです。upstream のファイルは 3000:3000 を公開し、ホスト上のすべてのインターフェースにバインドします。Docker は ufw の filter chain がパケットを確認する前に、公開ポートを NAT (network address translation) テーブルへ書き込みます。そのため、ufw deny 3000 ではそのポートを閉じられません。これは Docker のポートが ufw をバイパスする理由で説明している注意点です。127.0.0.1:3000:3000 と記述すると loopback インターフェースだけで公開され、リバースプロキシは同じホストから接続します。

:? は各変数を必須として扱います。そのため .env がない場合、認証情報を暗号化しないまま起動するのではなく、スタックの起動に失敗します。値を compose ファイルではなく .env に保持する方法は、Docker Compose の env ファイルと Secretで説明しているパターンです。

docker compose up -d
docker compose logs -n 30 connector
curl -s http://127.0.0.1:3000/health
sudo ss -tlnp | grep 3000

/health はランタイムの起動後に { "ok": true } へ応答します。ss127.0.0.1:3000 を出力する必要があります。0.0.0.0:3000 と表示される場合、ポートマッピングがまだ upstream の設定であり、ゲートウェイがインターネット全体に直接応答しています。health check で Connection refused が返る場合、コンテナはまだ待ち受けていません。プロキシを確認する前にログを読みます。

同じサービス用の Traefik ラベル
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.connector.rule=Host(`connect.example.com`)"
      - "traefik.http.routers.connector.entrypoints=websecure"
      - "traefik.http.routers.connector.tls.certresolver=le"
      - "traefik.http.services.connector.loadbalancer.server.port=3000"

Traefik を同じホスト上の Docker で実行する場合は、このサービスを Traefik network に接続し、ports: ブロックを削除します。Traefik は内部 network 経由でコンテナへ接続するため、ホストへ公開する必要はありません。certresolver=le は Traefik の static config にある resolver 名と一致させる必要があります。一致しない場合、router は証明書なしで起動します。

OAuth で実ホスト名が必要な理由

OOMOL_CONNECT_ORIGIN は省略されがちな設定ですが、省略すると OAuth がプロバイダーの不具合のように見える形で失敗します。ランタイムはこのオリジンから、<origin>/oauth/callback の形式でリダイレクト URI を生成します。オリジンを設定しない場合、デフォルト値は http://localhost:3000 です。そのためランタイムはプロバイダーに http://localhost:3000/oauth/callback をリダイレクト URI として送信しますが、OAuth アプリには https://connect.example.com/oauth/callback が登録されています。2 つの文字列が異なるため、GitHub は次のように応答します。

The redirect_uri MUST match the registered callback URL for this application.

OAuth プロバイダーはブラウザーをその URI にリダイレクトします。そのため、その URI は外部から到達できるアドレスでなければなりません。また、プロバイダーは localhost 以外の用途で通常の http:// を拒否します。これが、この構成にホスト名と証明書が必要な理由のすべてです。オリジンは初回起動前に設定してください。この値は起動時に読み込まれるため、.env または compose.yaml を編集した後、docker compose up -d をもう一度実行して反映します。

OAuth 経由で最初のプロバイダーに接続する

まずプロバイダー側で OAuth アプリを作成します。GitHub では、Settings、Developer settings、OAuth Apps、New OAuth App の順に進みます。認可コールバック URL に https://connect.example.com/oauth/callback を設定します。Client ID と client secret を保存します。

すべての /api 呼び出しには admin token が必要なため、シェルセッションで一度だけ export します。

export ADMIN_TOKEN='paste-the-admin-token'
curl -s https://connect.example.com/api/oauth/configs \
  -H "authorization: Bearer $ADMIN_TOKEN"

この一覧には、各プロバイダーでランタイムが想定する redirect URI が表示されます。そのため、origin の設定が反映されたことを最も早く確認できます。localhost のままの場合、コンテナは古い値で実行されています。この状態では OAuth フローが最後の手順で失敗します。

client credentials を保存してから、認可を開始します。

curl -s -X PUT https://connect.example.com/api/oauth/configs/github \
  -H "authorization: Bearer $ADMIN_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"clientId":"...","clientSecret":"..."}'

curl -s -X POST https://connect.example.com/api/oauth/authorizations \
  -H "authorization: Bearer $ADMIN_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"service":"github"}'

2 回目の呼び出しは authorizationUrl を返します。ブラウザーで開き、scope を承認します。プロバイダーはブラウザーを /oauth/callback に戻します。そこでランタイムが code を交換し、credential を保存します。origin の Web コンソールでも、同じ admin token の下でフォームを使って同じ手順を実行できます。通常の API key を使用するプロバイダーでは、これらの手順は不要です。{"authType":"api_key","values":{"apiKey":"..."}} を指定した PUT /api/connections/<service> で key を直接保存します。

各エージェントには認証情報ではなく、実行時トークンを付与します

エージェントは、管理 API が発行する実行時トークンを使用してゲートウェイで認証します。

curl -s -X POST https://connect.example.com/api/runtime-tokens \
  -H "authorization: Bearer $ADMIN_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"name":"research-agent"}'

レスポンスには oct_ で始まるトークンが含まれます。エージェントごとに1つ発行し、そのエージェント名を付けてください。識別できないトークンを失効させる場合、すべてのトークンを失効させることになるためです。エージェントは通常の HTTP でアクションを呼び出します。

curl -s -X POST https://connect.example.com/v1/actions/github.get_current_user \
  -H "authorization: Bearer oct_..." \
  -H 'content-type: application/json' \
  -d '{"input":{}}'

正常なレスポンスは、success フィールドが true で、プロバイダーのペイロードが data に含まれるエンベロープです。このレスポンスのどこにも GitHub トークンは含まれません。MCP クライアントでは、同じ bearer ヘッダーを付けて https://connect.example.com/mcp を指定します。ゲートウェイは API ごとに1つのツールを提供するのではなく、search_actionsexecute_action などの検出用ツールを提供するため、エージェントのツール一覧を小さく保てます。VPS で MCP サーバーを実行するでは、この接続のクライアント側を説明しています。

完了と判断する前に、もう1つ確認します。authorization ヘッダーを削除して、アクション呼び出しを繰り返してください。プロジェクト独自のクイックスタートでは、bearer なしで /v1 を呼び出しています。そのため、実行時認証を設定せずにインストールすると、ポートに到達できる誰もがアクションを実行できます。認証なしの呼び出しが成功した場合は、2つの対処方法があります。実行時トークンを設定して、匿名の呼び出しが失敗することを確認する方法と、リバースプロキシで /api/v1/mcp へのアクセスを、エージェントからの接続元アドレスに制限する方法です。外部に開放したままにする必要があるのは /oauth/callback だけです。プロバイダーのブラウザーリダイレクトが必要とする経路はこれだけだからです。

エージェントに必要なアクションだけに絞る

背後に 1000 個のプロバイダーがあるゲートウェイは、言語モデルに渡す対象として広すぎます。モデルが自分で作成していないテキストを読み始めると、対象はさらに広がります。たとえば、エージェントの Web 検索に応答する独自の SearXNG インスタンスが返すページには、エージェントが持つアクションの実行を狙った指示が含まれている可能性があります。コーディングエージェントに動作する最小限の変更を行わせるのと同じ抑制を、権限にも適用します。ジョブに実際に必要な少数のアクションだけを許可し、それ以外は許可しません。対象を絞る設定は 2 つあります。

OOMOL_CONNECT_ALLOWED_ACTIONS にはコンマ区切りの許可リストを指定でき、service.** に対応しています。OOMOL_CONNECT_BLOCKED_ACTIONS は拒否リストで、拒否リストが優先されます。許可リストを github.get_current_user,github.list_issues に設定すると、エージェントが要求しても他のすべてのアクションが拒否されます。これは単なるミスとインシデントを分ける設定です。ランタイムトークンには、グローバル設定に加えて独自のアクションルールがあります。トークンの allowedProxies リストは空で始まるため、POST /v1/proxy/:service は許可するまで拒否されます。このプロキシエンドポイントは、認証情報を付けてプロバイダーに raw リクエストを転送します。そのため、特定のエージェントに必要な場合を除き、空のままにしてください。

OOMOL_CONNECT_ALLOW_PRIVATE_NETWORK のデフォルト値は false です。これにより、セルフホストしたプロバイダー接続が、169.254.169.254 のクラウドメタデータサービスや同じネットワーク上のデータベースなど、プライベートアドレスを指すことを防ぎます。無効のままにしてください。自分でホストするプロバイダーに対してのみ有効にします。

すべてのトークンを保持する環境をバックアップする

重要なのは2つあり、どちらももう一方がなければ役に立ちません。connector-data ボリューム内の /app/data/connect.sqlite にあるデータベースが、封印された認証情報を保持しています。.env にある暗号鍵で、それらの認証情報の封印を解除します。鍵なしのボリュームバックアップからは何も復元できず、ボリュームなしの鍵からも何も復元できません。そのため、鍵はパスワードマネージャーに保管し、ボリュームは通常のバックアップローテーションに含めます。

SQLite ファイルをコピーする間はコンテナを停止してください。書き込み中に取得したコピーを復元すると、データベースが破損する可能性があります。

docker volume ls | grep connector-data
docker compose stop connector
docker run --rm -v open-connector_connector-data:/data -v "$PWD":/backup alpine \
  tar czf /backup/connector-data.tgz -C /data .
docker compose start connector

ボリューム名はプロジェクトディレクトリに _connector-data を加えたものです。そのため、最初のコマンドで実際の名前を確認し、その名前を3つ目のコマンドに貼り付けます。VPS の restic バックアップを使用してアーカイブを VPS の外部へ転送してください。転送前に暗号化されるためです。このアーカイブが認証情報ストアです。

ランタイムは、直近のアクション実行を監査レコードとしてデフォルトで 5,000 件保持します。そのため、コンソールでどのエージェントが何をいつ実行したかを確認できます。エージェントの動作が不審な場合は、まずこのログを確認してください。Uptime Kuma のステータスページから https://connect.example.com/health も監視してください。ゲートウェイが応答を停止すると、エージェントは分かりにくい形で失敗します。ゲートウェイが停止していると分かれば、エージェントの出力を1時間読み続けずに済みます。

発生する問題と表示されるメッセージ

プロバイダー側のredirect_uri_mismatch オリジンと登録済みのコールバック URL が一致していません。/api/oauth/configs の正確な文字列をプロバイダーのアプリ設定と比較してください。httpshttp の組み合わせや末尾のスラッシュも確認します。

すべての /api 呼び出しが 401 を返す。 管理者トークンのヘッダーがないか、名前が間違っています。ヘッダーは Authorization: Bearer <token> です。Web コンソールでも同じトークンを指定します。

コンテナは実行され、認証情報が平文で保存されている。 これは OOMOL_CONNECT_ENCRYPTION_KEY がコンテナに渡っていない場合に発生します。ランタイムは起動を拒否する代わりに、認証情報のレコードを暗号化せずに保存します。自分の環境で確認できます。判別可能な API キーを使用してプロバイダーを接続し、データベース内でそのキーを検索してください。

docker compose cp connector:/app/data/connect.sqlite /tmp/connect.sqlite
grep -c 'github_pat_' /tmp/connect.sqlite
shred -u /tmp/connect.sqlite

0 より大きい値は、キーが有効になっていないことを示します。.envcompose.yaml と同じディレクトリにあることを確認し、docker compose config で値が表示されることを確認してください。キーを設定すると、同じ検索結果は 0 になります。レコードが AES-256-GCM(Advanced Encryption Standard、256 ビットキー、Galois/Counter Mode)で暗号化されるためです。

リストア後に何も復号できない。 暗号化キーが変更されたか、失われています。設計上、キーがデータの隣に書き込まれることはありません。そのため復旧手段はなく、サポートチケットでも解決できません。すべてのプロバイダーを再接続してください。ローテーションは、ランタイム内の別のキー変数とデータコマンドを使用して実行できます。実行前に、現在のリリースノートを確認してください。

エージェントがカタログに表示されるアクション名のエラーを返す。 検出と実行は別の処理です。アクションが search_actions に表示されていても、OOMOL_CONNECT_ALLOWED_ACTIONS、denylist、またはそのランタイムトークン自身のルールによって拒否されることがあります。

アップグレード。 ボリュームをバックアップし、イメージタグを新しいリリースに変更してから、docker compose pull && docker compose up -d を実行します。docker compose logs -n 50 connector を監視し、migration の行が出力されることを確認してください。その後、ヘルスチェックと実際のアクションを 1 つ再実行してから、再び使用します。ロールバックする場合は古いタグに戻します。タグを固定しているため、この方法で戻せます。

FAQ

Open Connector をセルフホストする場合、パブリックドメインは必要ですか?

API key を使用するプロバイダーでは必要ありません。127.0.0.1 上のゲートウェイで十分です。OAuth では、実質的に必要です。プロバイダーはブラウザーを callback URL にリダイレクトするため、その URL はパブリックインターネットから解決できなければなりません。また、プロバイダーは localhost 以外での通常の http:// を拒否します。初回起動前に OOMOL_CONNECT_ORIGINhttps:// hostname に設定し、プロバイダーの OAuth app に <origin>/oauth/callback を登録してください。

Open Connector の encryption key を失うとどうなりますか?

保存された credentials を復号できなくなり、復旧手段もありません。この key は意図的にデータと同じ場所へ保存されないため、データベースを保持している人は誰も読み取れません。あなた自身も例外ではありません。唯一の方法は新しい key を設定し、すべてのプロバイダーを再接続することです。key は password manager に保存し、データベースはバックアップのローテーション対象にしてください。リストアには両方が必要です。

AI agent はプロバイダーの access token を参照できますか?

ゲートウェイ経由で呼び出す場合は参照できません。agent は oct_ で始まる runtime token で認証し、ゲートウェイがサーバー上で provider credential を outbound request に挿入します。agent には response だけが返されます。この性質を損なうものは2つあります。1つ目は /v1/proxy/:service endpoint です。これは credential を付加した raw request を転送します。grant が空の状態で始まるのには理由があります。2つ目は、API key を自分で agent に貼り付けることです。この場合、ゲートウェイを完全に迂回します。

ゲートウェイをパブリックインターネットから到達可能にする必要がありますか?

必要なのは /oauth/callback だけです。Docker の NAT rules が firewall の外側へ公開できないように、container port を 127.0.0.1 で公開し、前段に reverse proxy を置いてください。その後、authorization header なしで1つの action call をテストします。成功した場合は、認証済みの call だけが動作する状態になるまで、proxy で /api/v1/mcp を agent が使用するアドレスに制限してください。

Open Connector は本番利用の準備ができていますか?

Apache 2.0 licensed で、開発も速いペースで進んでいます。repository は 29 June 2026 に公開され、v1.3.3 は 30 July 2026 に release されたため、このガイドのすべての version number は 1 August 2026 時点のスナップショットとして扱ってください。release tag を指定して実行し、latesttip では決して実行しないでください。各 upgrade の前に release notes を読み、1度リストアしたことのある volume backup を保持してください。自分で管理するマシンで使用する設計としては堅実です。リスクは architecture ではなく、version の変動です。