OctopをVPSでセルフホストする方法
Octop v0.9.19をDocker Composeのtag固定でVPSへ導入します。ユーザー別の分離、OpenAI互換バックエンド、TLS設定と、curlインストーラーを避ける理由を解説します。
Octop とは何か、なぜ自分でホストするのか
Octop は、家庭や小規模チーム向けのセルフホスト型 AI アシスタントです。単なるチャットフロントエンドではなく Octop をセルフホストする理由は、ユーザー同士の環境を分離できることにあります。Open WebUI は、モデルの前段にブラウザーインターフェースを提供します。Octop はこれに、admin ロールを持つアカウント、ユーザーごとの非公開ワークスペースと認証情報セット、タスクごとに切り替えられる専門エージェントのライブラリを追加します。この分離により、1 台の VPS で 1 人ではなく 5 人にサービスを提供できます。
プロジェクトは github.com/TencentCloud/Octop にあります。1 つのプロセスで Web ダッシュボード、コマンドラインインターフェース、チャットチャネル(Feishu、DingTalk、QQ、Discord、WeCom)、スケジュール済みジョブを提供し、すべてのデータを ~/.octop/ 配下の単一の SQLite データベースに保存します。以下はすべて、2026 年 8 月 5 日にリリースされた tag v0.9.19 を前提にしています。まだプラットフォームを比較している場合は、VPS で実行できる Open WebUI 代替製品の比較で、より広い選択肢を確認できます。
導入に時間をかける前に、1 点明確にしておきます。Octop は、ベンダーの GitHub 組織から公開されている 1.0 未満のソフトウェアで、2026 年 8 月時点の star 数は約 900 です。開発の進み方は速く、バージョン番号にもそれが表れています。ここで安定したアップグレードパスを保証するものではありません。tag を固定し、changelog を読み、バックアップを保持してください。
開始前に必要なもの
- Docker Engine と Compose plugin を実行できる Ubuntu 24.04 の VPS。Compose が初めての場合は、VPS 向け Docker Compose の基本から始めてください。
git。イメージを pull するのではなく、release tag を checkout するためです。- VPS を指すドメイン名。この構成では、前段に TLS(transport layer security)を配置します。
- OpenAI API に対応するモデルバックエンド。ローカルの Ollama、自己ホスト型ゲートウェイ、または有料の key を使用できます。
Octop 自体は軽量です。Python プロセスと SQLite ファイルで構成されます。負荷の大部分はモデルバックエンドが占めるため、同じホストでモデルを実行する場合は、モデルに合わせてホストのリソースを確保してください。
curl インストーラーを推奨しない理由
README は、1 行のインストールコマンドから始まっています。
curl -fsSL https://finnie-1258344699.cos.ap-guangzhou.myqcloud.com/octop/install.sh | bashこのインストーラーは、重要なサーバーでは推奨しません。理由は明確です。このスクリプトはリポジトリに存在しません。Tencent Cloud Object Storage のバケットから配信されています。git tag や commit で管理されていないため、今日のスクリプトと先週のスクリプトを比較できず、変更理由を確認できる履歴もありません。明日、バケットが別の内容を配信しても、プロジェクトにはその変更が記録されません。結果をそのまま bash にパイプすると、内容を 1 行も確認する前にマシン上でスクリプトが実行される点も問題です。
このインストーラーは、コンテナではなくホストに書き込みます。uv を使って Python 3.12 を取得し、パッケージマネージャーが把握できない環境を構築するため、後から削除する作業は手動になります。
よりよい方法は 2 つあります。スクリプトを取得して内容を確認してから実行する方法です。30 秒で済みます。curl -fsSL <url> -o install.sh、続いて less install.sh、その後 bash install.sh を実行します。もう 1 つは Docker を使う方法で、このガイドの残りではこちらを使用します。PyPI パッケージ(pip install octop)は少なくともバージョン管理された成果物なので、特定のリリースに固定できます。
Docker Compose で Octop を v0.9.19 に固定してデプロイする
2026年8月時点では、pull できる公開イメージはありません。付属の Compose ファイルはリポジトリからイメージをビルドするため、バージョンを固定するには git tag を checkout します。
git clone https://github.com/TencentCloud/Octop.git
cd Octop
git checkout v0.9.19このファイルが定義するサービスのうち、重要な部分だけを示します。
services:
octop:
build:
context: ..
dockerfile: docker/Dockerfile
image: octop:latest
container_name: octop
restart: unless-stopped
ports:
- "${OCTOP_PORT:-8088}:${OCTOP_PORT:-8088}"
volumes:
- ${OCTOP_DATA:-~/.octop}:/data/.octop
environment:
- HOME=/data
- OCTOP_BIND_HOST=0.0.0.0
- OCTOP_PORT=${OCTOP_PORT:-8088}
- OCTOP_DEFAULT_PASSWORD=${OCTOP_DEFAULT_PASSWORD:-octop}
- OCTOP_ADMIN_USERNAME=${OCTOP_ADMIN_USERNAME:-admin}
- OPENAI_API_KEY=${OPENAI_API_KEY:-}build: ブロックに注目してください。image: octop:latest は独自にビルドしたイメージの名前であり、レジストリ参照ではありません。そのため、ここでの latest は直近にコンパイルした内容を指します。データパスはデフォルトに任せず、明示的な場所を指定してください。また、初回起動の前に管理者アカウントへ実際のパスワードを設定してください。これを docker/.env に記述します。
OCTOP_PORT=8088
OCTOP_ADMIN_USERNAME=admin
OCTOP_DEFAULT_PASSWORD=<a long random password>
OCTOP_DATA=/srv/octop-dataこのファイルには、ほかの部分より重要な注意点があります。Compose は docker/.env を、YAML 内の ${...} プレースホルダーを展開する目的にだけ読み込みます。このファイルに追加したキーは、Compose ファイルの environment: にも記載しない限り、コンテナには渡りません。OCTOP_ACCESS_TOKEN_TTL だけを .env に追加しても、何も起こらず、エラーも表示されません。別の方法として、同じキーを、マウントしたデータディレクトリ内の ~/.octop/env に記述できます。Octop は起動時にこのファイルを読み込みます。Docker Compose における env ファイルと Secret のガイドでは、これら2つの仕組みが同じではない理由を説明しています。
ビルドして起動します。
docker compose -f docker/docker-compose.yml up -d --build
docker compose -f docker/docker-compose.yml ps
curl http://127.0.0.1:8088/api/health正常なインスタンスは、ヘルスチェックに {"status":"ok","version":"..."} で応答します。それ以外の場合は、ブラウザーを操作する前に docker compose -f docker/docker-compose.yml logs -f octop を確認してください。
次に、ビルドしたイメージへ意味のある名前を付けます。次の --build によって octop:latest が上書きされるため、名前を付けないと両者を区別できなくなります。
docker image tag octop:latest octop:0.9.19初回起動では octop init が実行され、データボリュームに初期認証情報が書き込まれます。
docker exec -it octop cat /data/.octop/credential.txtデフォルト値は admin / octop です。これらが適用されるのは 初回の初期化時だけ です。これは、よくある質問への答えでもあります。コンテナを一度起動した後で OCTOP_DEFAULT_PASSWORD を変更しても、アカウントはすでに存在するため何も変わりません。パスワードはダッシュボードで変更してください。
8088 番ポートを公開しない
上記の ports: 行は、VPS のすべてのインターフェイスにバインドします。コンテナを起動した時点で、ダッシュボードが平文かつデフォルトパスワードのまま、パブリックインターネットからアクセス可能になります。Octop 自身の OCTOP_BIND_HOST のデフォルト値は 127.0.0.1 です。Compose ファイルでは、プロセスが自身のネットワーク名前空間の外部からのトラフィックを受け付ける必要があるため、0.0.0.0 に上書きしています。この上書きは正しい設定です。公開ポートが外部に公開する原因です。
docker/docker-compose.yml の ports: 行を編集し、マッピングが loopback でのみ待ち受けるようにします。
ports:
- "127.0.0.1:${OCTOP_PORT:-8088}:${OCTOP_PORT:-8088}"単純な override ファイルでこの問題を解決しようとしないでください。Compose は複数のファイルにある ports のリストを置き換えずに連結します。そのため、両方のマッピングが公開され、2 つ目のマッピングはバインドに失敗します。upstream のファイルを変更せずに維持する場合は、シーケンスに !override タグを使用してください。これは、追加ではなく置き換えを行うための文書化された方法です。Compose が複数のファイルをマージする仕組みの説明で、これらのマージ規則の詳細を確認できます。
loopback にバインドすると、ファイアウォールで発生する別の問題も解決できます。Docker は公開ポート用のルールを ufw が管理するチェーンより先に nat テーブルへ書き込むため、ufw deny 8088 では公開されたコンテナポートを停止できません。127.0.0.1 にバインドしたポートは、ufw の設定にかかわらず外部から到達できません。そのため、これは次善策ではなく、適切な修正方法です。
リバースプロキシを前段に置いて TLS を終端する
Caddy が最短の方法です。Caddy は ACME(自動証明書管理環境)を使用して自動的に証明書を取得し、明示的な設定なしで WebSocket をプロキシするためです。
octop.example.com {
reverse_proxy 127.0.0.1:8088
}nginx では、Octop が WebSocket 経由でチャットをストリーミングするため、より慎重な設定が必要です。
server {
listen 443 ssl;
server_name octop.example.com;
ssl_certificate /etc/letsencrypt/live/octop.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/octop.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8088;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_read_timeout 3600s;
}
}各行には役割があります。チャットは WS /agents/{id}/chat/ws 上で動作するため、proxy_http_version 1.1 と 2 つの upgrade ヘッダーがないと、nginx はアップグレード要求に 400 Bad Request で応答します。ダッシュボードは正常に読み込まれますが、送信したメッセージはすべてページ上にエラーを表示せず、永遠に停止します。proxy_buffering off も重要です。human-in-the-loop の resume エンドポイントは text/event-stream を返し、プロキシのバッファーに保持された SSE(server-sent events)はストリーミングされず、最後にまとめて届くためです。proxy_read_timeout は長時間のツール実行に対応します。デフォルトの 60 秒ではエージェントがタスクの途中で停止し、ログに upstream timed out (110: Connection timed out) が記録されるためです。
プロキシ配下で JWT 認証がどのように動作するか
Octop は Cookie ではなく bearer token で認証します。POST /api/auth/login は {access_token, role, user, ...} を返し、その後のリクエストには Authorization: Bearer <access_token> が付加されます。リバースプロキシにとって、これは好都合です。Cookie のドメイン、Secure フラグ、誤設定しやすい SameSite ルールがないため、http://127.0.0.1:8088 で機能していたセッションは https://octop.example.com でも同じように動作します。
実際のユーザーに公開する前に、2 つの点を把握しておく必要があります。
WebSocket は URL に token を含めて送信します。 エンドポイントは WS /agents/{id}/chat/ws?token=<jwt> です。ブラウザーの JavaScript では、WebSocket ハンドシェイクに Authorization ヘッダーを設定できないためです。TLS により、送信中の token は保護されます。ただし、独自のログへの記録までは防げません。nginx はデフォルトで、クエリ文字列を含む完全なリクエスト行を access_log に書き込みます。そのため、実際のユーザーが使用する有効な token が、サーバー上の平文ファイルに残ります。引数を除いたパスをログに記録してください。$uri はクエリ文字列がすでに除去された正規化済みパスです。これを http ブロックに記述し、server から参照します。
log_format octop_noargs '$remote_addr [$time_local] '
'"$request_method $uri $server_protocol" '
'$status $body_bytes_sent';
access_log /var/log/nginx/octop.log octop_noargs;セッション単位のログアウト機能はありません。 OCTOP_ACCESS_TOKEN_TTL のデフォルト値は 86400 です。そのため、ログイン後も token は 24 時間有効です。token を無効化する公式の方法は octop admin rotate-jwt-secret だけです。これは ~/.octop/secrets/jwt_secret に保存された署名鍵をローテーションし、発行済みのすべての token を全ユーザーについて直ちに無効化します。したがって、チームからユーザーが離脱した場合は、ユーザーを削除し、secret をローテーションしてから、残りのユーザーに再ログインを依頼します。これが重すぎる場合は、有効期間を短くしてください。その際は、変数を environment: の一覧だけでなく .env にも追加します。
OCTOP_ACCESS_TOKEN_TTL=28800総当たり攻撃への対策もあります。OCTOP_LOGIN_MAX_ATTEMPTS のデフォルト値は 5 回の失敗、OCTOP_LOGIN_LOCKOUT_SECONDS は 900 です。そのため、ロックされたユーザーは壊れたインストールを調べるのではなく、単に 15 分待つことになります。Octop には独自のユーザーストアがあり、v0.9.19 時点で公式に文書化された OIDC サポートはありません。実際のシングルサインオンが必要な場合は、認証プロキシを前段に配置します。自己ホスト型の Authentik サーバーは、そのために使用します。
モデルバックエンドを Octop に接続する
プロバイダーはダッシュボードでエージェントごとに設定し、octop provider list で現在の設定を確認できます。Octop には、OpenAI互換 API、DashScope (Qwen)、Ollama 用のプリセットが用意されています。認証情報は、自分の SQLite データベースにある providers テーブルに保存されます。選択によって、費用とサーバー外へ送信されるデータが変わります。
Ollama を使うローカルモデル。 サーバー外へ何も送信せず、トークンではなく RAM を消費します。注意が必要なのは、コンテナからホストの Ollama を 127.0.0.1:11434 で利用できないことです。このアドレスはコンテナ自身の loopback を指すためです。サービスにホストゲートウェイのエントリを追加します。
extra_hosts:
- "host.docker.internal:host-gateway"次に、プロバイダーの base URL を Ollama の OpenAI互換パスである http://host.docker.internal:11434/v1 に設定します。API key フィールドには空でない任意の文字列を入力してください。Ollama はこの値を無視しますが、OpenAI クライアントは空の値を送信しないためです。この構成を使うには、Ollama が loopback の外部でも listen する必要があります。そのため、systemd unit で OLLAMA_HOST=0.0.0.0:11434 を設定します。ここが危険な部分です。Ollama には認証機能がないため、パブリック IP 上で 11434 を公開すると、最初にスキャンした誰でも利用できるモデルサーバーになります。許可するのは Docker のプライベート範囲 sudo ufw allow from 172.16.0.0/12 to any port 11434 proto tcp だけにし、それ以外を拒否してください。VPS で Ollama を実行するではモデルのサイズ設定を説明し、Ollama と vLLM の比較では Ollama が適切なサーバーではなくなる条件を説明しています。
ローカルモデルについて、もう1つ注意点があります。これは Octop のバグに見えますが、バグではありません。エージェントはツールを呼び出して動作します。system prompt、ツール定義、履歴を合わせると、大きな prompt になります。Ollama はデフォルトの context window が比較的小さいモデルを提供するため、ツール定義がある prompt の先頭部分が window から外れます。その結果、モデルはツールの呼び出しを停止するか、存在しないツールを作り出します。num_ctx を 16k または 32k に増やし、function calling に実際に対応したモデルを選択してください。
セルフホストの gateway。 セルフホストの LiteLLM gateway を Octop と他のすべてのサービスの間に配置すると、1つの base URL、ユーザーごとの個別の key、利用上限、単一のログを利用できます。Octop を編集せずに、gateway の背後にあるモデルを交換することもできます。
有料 API。 品質は最も高くなりますが、明確なトレードオフがあります。会話の内容がサーバー外へ送信され、プロバイダーに到達します。これは、セルフホストを選んだ主な目的と相反します。key は docker/.env に OPENAI_API_KEY として設定します。Compose file がすでにこの値を渡します。
どの方式を選んでも、Compose file には OCTOP_LANGFUSE_ENABLED、LANGFUSE_PUBLIC_KEY、LANGFUSE_SECRET_KEY、LANGFUSE_BASE_URL も設定されています。そのため、自分の Langfuse インスタンスへ trace を送信し、chat window から推測するのではなく、エージェントが実際に何をしているかを確認できます。
ユーザー、ロール、共有エージェントライブラリ
初回起動時に作成される管理者アカウントで、他のユーザーを作成・管理します。各ユーザーには専用のエージェント、ワークスペース、認証情報が割り当てられます。この分離は、ブラウザーが保持するトークンによって維持されます。一方で、全員が利用できるスキルとサブエージェントの共有プールもあります。これが、家族で運用する価値を高める機能です。1 人が優れた調査エージェントを一度作成すれば、他の人が作り直す必要はありません。
ツールの扱いには注意してください。Octop にはツールの承認機能と shell コマンドのガードレールがあり、どちらも実際に機能します。ただし、shell コマンドを実行するエージェントは、データボリュームがマウントされた Octop コンテナ内でコマンドを実行します。ガードレールにより、不用意なプロンプトで実行できる操作は制限されます。しかし、ガードレールはサンドボックスの境界ではありません。shell の利用を許可しないユーザーには、ツールの承認を有効にしておいてください。他の選択肢と比較する場合は、セルフホスト AI エージェントの比較記事で、それぞれのエージェントの対応方法を確認できます。
これほど速いペースでリリースされるプロジェクトのアップグレード
The data behind this chart
[
{
"version": "v0.9.16",
"days_since_previous_release": 2
},
{
"version": "v0.9.17",
"days_since_previous_release": 3
},
{
"version": "v0.9.18",
"days_since_previous_release": 1
},
{
"version": "v0.9.19",
"days_since_previous_release": 3
}
]これは、2026 年 8 月 7 日時点でリポジトリから取得したタグの日付です。9 日間に 4 件のタグ付きリリースがあり、間隔は最短で 1 日でした。また、v0.9.19 は直前のタグから 3 日後にリリースされています。このリリース頻度は、プロジェクトの健全性を示す一方で、latest を無条件に実行する理由にはなりません。適用する前に変更内容を確認してください。
cd Octop
git fetch --tags
git tag --sort=-creatordate | head
NEW_TAG=$(git tag --sort=-creatordate | head -1)
git log --oneline "v0.9.19..$NEW_TAG"毎回、最初にバックアップを取得してください。起動時にデータベースのマイグレーションが実行されるためです。1.0 未満のプロジェクトでは、マイグレーションに失敗した場合の復旧も自分で行う必要があります。
docker compose -f docker/docker-compose.yml stop
sudo tar czf octop-backup-$(date +%F).tgz -C /srv octop-data
docker compose -f docker/docker-compose.yml start次に、新しいタグをチェックアウトし、docker compose -f docker/docker-compose.yml up -d --build で再ビルドします。問題が発生した場合、古いタグをチェックアウトして再ビルドすればコードは元に戻せます。ただし、データベースを元に戻せるのは tarball だけです。
この tarball には octop.db、config.json、JWT 署名 Secret、credential.txt が含まれているため、サーバー本体と同じように機密性の高いファイルです。モードを 600 に設定し、サーバー外にもコピーを保管してください。大規模な環境向けには、SQLite の代わりに pgvector 対応の PostgreSQL を実行する docker/docker-compose.postgres.yml もプロジェクトから提供されています。
障害モードと表示される文字列
ヘルスチェックが応答しません。 curl http://127.0.0.1:8088/api/health がハングするか、接続を拒否します。docker compose -f docker/docker-compose.yml logs -f octop を確認してください。初回初期化中に終了するコンテナは、通常、データディレクトリに書き込めません。OCTOP_DATA に設定したパスの所有者を確認してください。
ダッシュボードは読み込まれますが、チャットがハングします。 ページにエラーは表示されず、応答も返りません。ブラウザーのコンソールを開き、wss://octop.example.com/agents/.../chat/ws への接続失敗を探してください。プロキシが upgrade を転送していません。proxy_http_version 1.1 と Upgrade、Connection の各ヘッダーを追加してください。
返信全体が数秒遅れて一度に表示されます。 ストリーミングは動作していますが、バッファリングが有効です。proxy_buffering off を設定してください。
bind: address already in use。 8088 をすでに別のプロセスが使用しています。sudo ss -tlnp | grep 8088 でプロセス名を確認できます。元の ports エントリを編集せず、override ファイルに2つ目のエントリを追加した場合も、この状態になります。
正しいパスワードが拒否されます。 5回連続して誤った入力をすると、900秒間ロックアウトされます。再インストールせず、ロックアウトが解除されるまで待ってください。
.env の新しいパスワードが反映されません。 これらの認証情報が適用されるのは初回初期化時だけです。ダッシュボードで変更してください。
エージェントは返信しますが、ツールを実行しません。 ほとんどの場合、ローカルモデルの問題です。ツール定義に対してコンテキストウィンドウが小さすぎるか、モデルの function calling 対応が不十分です。num_ctx を増やし、ツール使用向けに構築されたモデルを試してください。
FAQ
Octop は Open WebUI の代替ですか?
Octop が追加する機能が必要な場合に限ります。Open WebUI はモデルの前段に置くチャットインターフェースで、1 人で使う場合や、信頼できる家庭内で使う場合には十分に機能します。Octop には、管理者ロール付きのアカウント、ユーザーごとのワークスペースと認証情報、切り替え可能な専門エージェントのライブラリがあります。そのため、複数人で 1 台のサーバーを共有しながら、同じ履歴を共有せずに済みます。1 つのアカウントで問題なければ、Open WebUI のほうがシンプルで、成熟度も大幅に高い選択肢です。
Octop の curl インストールスクリプトを使用すべきでないのはなぜですか?
このスクリプトはリポジトリではなく Tencent Cloud Object Storage バケットから提供されているため、git のタグやコミットによる管理対象ではありません。現在の動作を先週の動作と比較できず、bash にパイプすると、内容を読む前に実行されます。また、パッケージマネージャーの管理外にある独自の Python 3.12 環境を使用して、ホストへ直接インストールします。スクリプトをダウンロードしてから内容を確認するか、チェックアウトしたタグから Docker Compose でデプロイしてください。
Octop は有料 API の代わりにローカルモデルを使用できますか?
はい。Octop は OpenAI 互換 API に対応しており、Ollama のプリセットも提供しています。そのため、コンテナに extra_hosts: ["host.docker.internal:host-gateway"] を追加し、ホストで OLLAMA_HOST=0.0.0.0:11434 を設定すれば、http://host.docker.internal:11434/v1 を指定して使用できます。Ollama には独自の認証機能がないため、Docker のアドレス範囲に対するファイアウォールでポート 11434 を制限してください。ツール定義を含むエージェントのプロンプトはデフォルトのコンテキストウィンドウを超え、モデルがツールの呼び出しを停止することがあるため、Ollama の num_ctx は 16k 以上に増やしてください。
リバースプロキシは必要ですか。それともポート 8088 を開放できますか?
プロキシが必要です。Octop 付属の Compose ファイルは、TLS なしで全インターフェースにポート 8088 を公開します。そのため、パスワードと Bearer トークンが平文のままインターネット上を通過することになります。公開ポートを 127.0.0.1:8088:8088 に変更し、証明書を設定した Caddy または nginx を前段に置いてください。nginx を使用する場合は、WebSocket のアップグレードヘッダーを転送し、proxy_buffering off を設定してください。設定しないとページは読み込まれても、チャットは応答しないままになります。
Octop は本番環境で使用できる状態ですか?
2026 年 8 月時点では、まだ 1.0 未満で、タグ付きリリースを週に複数回公開しています。そのため、完成した製品ではなく、有望なソフトウェアとして扱ってください。正確なタグを固定し、アップグレードのたびにコミットログを確認し、再ビルドの前に毎回データボリュームをバックアップすれば、家族や小規模な社内チームでの利用には対応できます。latest では実行せず、顧客データもまだ保存しないでください。