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

HarnessRouterを自宅サーバーで構築する方法

Codex、Claude Code、Hermesを1つの自前APIで運用します。Dockerの正確な構築手順、loopback bind、変更必須の初期ログイン、TLS経由のアクセスを解説します。

HarnessRouter が取り除くもの

HarnessRouter Community Edition を自分で管理するサーバー上にホストすると、複数の agent harness の前段に 1 つの API を配置できます。agent harness は、ループでモデルを操作するコマンドラインプログラムです。セッションを保持し、ファイルを編集し、コマンドを実行し、作業を依頼した側へ進行状況をストリーミングします。Codex、Claude Code、Hermes はいずれもこの役割を担いますが、それぞれ独自のインストール方法、認証情報の形式、セッションの扱い方を持っています。HarnessRouter はこれらをすべて 1 つのコンテナ内で実行し、単一の HTTP エンドポイント、単一のログイン、単一の Secret ストアを前段に配置します。

考え方はこれだけですが、その代償は明確にしておく必要があります。複数の構成要素を 1 つにまとめるために、サーバーへコンテナ、ログイン、ボリューム、アップグレード手順を追加します。現在 1 つの harness だけを使っている場合は、その harness を直接インストールするよりも構成が複雑になります。このトレードオフについては最後のセクションで説明するため、デプロイする前に確認してください。

以下の内容は、19 August 2026 に取得した image tag 0.5.5 を基に確認しています。このプロジェクトはほとんど毎日新しいタグを公開しているため、1 か月後にこのページの内容をそのまま信頼せず、実際に実行するタグを確認してください。コマンドは、プロジェクトの README(github.com/HarnessRouter/harnessrouter)から引用しています。

Unified Harness Protocol の実体

HarnessRouter は、unifiedharnessprotocol.org で公開されている Unified Harness Protocol (UHP) を実装します。UHP は、製品がハーネス上でタスクを開始する方法、実行中のタスクを追跡する方法、セッションとファイルを管理する方法、失敗を報告する方法を定義します。仕様は日付でバージョン管理されます。2026年8月19日時点の最新バージョンは 2026-08-11 付けで、サイトでは「安全に変更できるようバージョン管理された、構築の基盤として十分に安定したドラフト標準」と説明されています。

ここでは「オープン標準」という表現を慎重に捉えてください。同じ企業が仕様、リファレンス実装、適合性を判定する 52 項目の conformance suite を作成しています。このような運用は、登場したばかりのプロトコルでは一般的です。また、Apache-2.0 ライセンスにより、その一部または全部を fork できます。一方で、UHP はまだ複数ベンダーによる標準ではありません。新興プロトコルとして扱ってください。実用的で、発展途上にあり、自分のコードを書き直さずに利用を停止できる構成にしておくべきものです。

開始前に必要なもの

Docker と、約 4 GB の空きディスク容量が必要です。すでに料金を支払っているモデルプロバイダーの API key も必要です。イメージの pull には約 700 MB が必要で、残りのディスク容量は agent CLI と、それらが書き込む workspace に使用されます。イメージにはモデルも trial key も組み込まれていないため、プロバイダーに接続するまでタスクは失敗します。HarnessRouter 自体は Apache-2.0 ライセンスです。agent CLI はこのライセンスの対象外です。そのため、イメージに含めず、初回起動時に取得します。

Docker run 1つで HarnessRouter をセルフホストする

docker pull harnessrouter/harnessrouter
docker run -d --name harnessrouter \
  -p 127.0.0.1:3000:3000 \
  -v harnessrouter:/data \
  harnessrouter/harnessrouter

続いて、コンテナが起動する様子を監視します。初回の起動は遅く、理由はログに表示されます。

docker logs -f harnessrouter

正常に動作すると、次のような行が表示されます。

installing Claude Code (Anthropic's terms apply)…
installing Codex (Apache-2.0)…
installing Hermes (check its upstream license before use)…

ready on :3000 が表示されるまで待ちます。このインストールはボリュームごとに1回だけ実行されるため、以降の起動は数秒で完了し、インストール行は一切表示されません。

このダウンロードから分かる重要な点は2つあり、どちらも VPS では重要です。1つ目は、初回起動に外向きのネットワークアクセスが必要なことです。イメージは自己完結していないため、送信トラフィックを制限するフィルターの背後にあるホストや、外部への経路がないホストでは、ここで停止して ready on :3000 が表示されません。失敗するのは docker pull の時点ではなく初回起動時です。この問題に気付く場所としては分かりにくいと言えます。2つ目は、第三者のソフトウェアを第三者の条件でインストールすることです。Claude Code には Anthropic の利用条件が適用され、Hermes には上流プロジェクトが定める条件が適用されます。そのため、商用利用する前に両方を確認してください。

-v harnessrouter:/data は名前付きの Docker ボリュームを作成します。永続化されるデータはすべて /data に保存されます。SQLite データベース、保存ファイル、Secret ストア、エージェントのワークスペースが含まれます。このボリュームを削除すると、provider key とすべてのトランスクリプトを含め、インスタンス全体を削除したことになります。バックアップはコンテナを停止してから実行してください。書き込み中の SQLite データベースをコピーすると、開けないファイルになる可能性があります。同じ停止後コピーの手順は、このホスト上のすべてのステートフルなコンテナに適用されます。ただし、サービスによって詳細は異なります。PhotoPrism と Immich には、それぞれ専用のバックアップコマンドが必要です。

docker stop harnessrouter
docker run --rm -v harnessrouter:/data -v "$PWD":/backup alpine \
  tar czf /backup/harnessrouter-data.tgz -C / data
docker start harnessrouter

Compose 版と変更が必要な行

リポジトリには compose ファイルが含まれています。ホスト上のすべてのインターフェースで "3000:3000" を公開する設定です。公開サーバーで起動する前に、この行を変更してください。

services:
  harnessrouter:
    image: harnessrouter/harnessrouter:0.5.5
    ports:
      - "127.0.0.1:3000:3000"
    env_file:
      - .env
    volumes:
      - harnessrouter-data:/data
    restart: unless-stopped

volumes:
  harnessrouter-data:

上流版との違いは、バインドアドレスと、latest ではなく固定したバージョンタグを使用している点です。2026 年 8 月 9 日から 18 日までの間に 16 個のバージョンタグがリリースされました。実行中に agent runtime が変わると、デバッグが困難になります。そのため、バージョンを固定します。次に環境ファイルをコピーし、権限を制限してから起動します。

cp .env.example .env
chmod 600 .env
docker compose up -d
docker compose logs -f

.env にはプロバイダーの key が平文で保存されるため、mode 600 が最低限必要です。docker compose サブコマンドに慣れていない場合は、Docker Compose コマンドチートシートで日常的に使うコマンドを確認できます。

ポートを 0.0.0.0 ではなく 127.0.0.1 に公開する理由

-p 3000:3000 はホストのすべてのインターフェースでポートを公開します。-p 127.0.0.1:3000:3000 は loopback のみで公開するため、接続できるのは VPS 自身からだけです。コンテナ内では常に 3000 で待ち受けるため、変更するのは左側です。設定結果を確認します。

docker port harnessrouter
sudo ss -ltnp | grep 3000

ss の出力が 127.0.0.1:3000 であれば正しい状態です。0.0.0.0:3000 はコンソールがパブリックインターネット上に公開されていることを意味します。このコンソールでは harness を作成し、すべての transcript を読み取り、agent を実行できます。さらに、agent には workspace 内の shell と実ファイルシステムへのアクセスが与えられます。そのため、一般的な self-hosted アプリケーションよりも危険です。接続した provider key も保持しています。保護されていないコンソールに到達できる人は、作業内容を読み取り、コマンドを実行し、key を使って支出できます。

ホストのファイアウォールだけでは防げません。Docker は独自のルールをカーネルの nat テーブルに書き込みます。このルールは ufw が管理する chain より前に評価されます。そのため、sudo ufw status で拒否と表示されていても、公開されたポートには接続できます。確認は VPS からではなく、別のマシンから実行してください。VPS から確認すると、何も検証できません。これは port 3080 で dsh を headless 実行する場合と同じ教訓です。サービスを loopback に bind し、その後で接続方法を意図的に決めてください。

何より先にデフォルトのログイン情報を変更する

http://localhost:3000 にユーザー名 harnessrouter とパスワード harnessrouter でサインインします。これらの認証情報は機密情報ではなくプレースホルダーであるため、README に記載されています。また、変更するまでコンテナの起動時に毎回警告が表示されます。

using the DEFAULT password. Set HR_AUTH_PASSWORD, or change it from the profile page, before exposing this instance.

Profile ページから変更するか、スクリプトによるデプロイ用に起動時に設定します。HR_AUTH_USERHR_AUTH_PASSWORD でデフォルト値を上書きできます。

docker run -d --name harnessrouter \
  -p 127.0.0.1:3000:3000 \
  -v harnessrouter:/data \
  -e HR_AUTH_USER='you' \
  -e HR_AUTH_PASSWORD='the-password-you-chose' \
  harnessrouter/harnessrouter

アカウントシステムとメールサーバーがないため、パスワードのリセットメールは送信されません。パスワードを失った場合は、ボリューム内の auth ファイルを削除して再起動し、再びデフォルトの認証情報でサインインします。

docker stop harnessrouter
docker run --rm -v harnessrouter:/data alpine rm -f /data/selfhost-auth.json
docker start harnessrouter

HR_AUTH_DISABLED=1 を指定すると、サインインによる保護を完全に無効化できます。README では、この設定の対象を「他の誰も到達できないマシン」に限定しています。パブリック IP アドレスを持つ VPS はそのようなマシンではないため、ラップトップで実行する場合を除き、保護を有効にしておきます。

バージョンを確認する。古いバージョンには認証ゲートがない

ここは特に注意が必要です。0.1.x0.2.0 には認証ゲートがまったくありませんでした。ポート 3000 に到達できる人は、すでにコンソールへ入れる状態でした。0.3.0 が、ログイン機能を備えた最初のリリースです。これらの古いタグは現在も公開され、pull できます。そのため、古いタグを固定していたり、同僚からコピーした compose ファイルを使っていたりすると、認証のないコンソールを現在でも公開ポートに置く可能性があります。

2026 年 8 月 19 日時点で、公開されている最新のタグは 0.5.5 です。これは 2026 年 8 月 18 日付で、latest はこのタグを指しています。現在使用しているバージョンを確認し、Docker Hub のタグ一覧と比較してください。

docker image ls harnessrouter/harnessrouter

0.3.0 未満のバージョンは、後で対応するのではなく、直ちに置き換えてください。0.3.0 以上のバージョンでも、パスワードの変更が必要です。ポート 3000 をスキャンしている相手にとって、デフォルトパスワードが設定されている状態と、パスワードがない状態は同じだからです。このページに記載されたバージョン番号を最新情報として扱わないでください。これらは冒頭の日付時点で正しかったものであり、このプロジェクトはリリースが速いです。

プロバイダーを接続する

モデルプロバイダーを接続するまで、何も実行されません。コンソールの Integrations ページから追加するか、環境変数で docker run に渡します。値は JSON なので、シェルでは引用符で囲みます。

-e HR_SECRET_GLOBAL_HARNESS_CONN_ANTHROPIC='{"name":"anthropic","provider":"anthropic","api_key":"sk-ant-…"}'

.env.example では、プロバイダーのファミリーごとに接続変数を指定します。HR_SECRET_GLOBAL_HARNESS_CONN_ANTHROPIC は claude-code バックエンド用、HR_SECRET_GLOBAL_HARNESS_CONN_OPENAI は codex バックエンド用、HR_SECRET_GLOBAL_HARNESS_CONN_CUSTOM は OpenAI-compatible エンドポイント用です。最後の接続先には、アグリゲーターや独自の推論サーバーを指定できます。対応する HR_SECRET_GLOBAL_HARNESS_POLICY_CLAUDEHR_SECRET_GLOBAL_HARNESS_POLICY_CODEXHR_SECRET_GLOBAL_HARNESS_POLICY_HERMES では、各バックエンドがデフォルトで使用する接続を指定します。HR_SECRET_KEY は別の項目で、データベースをエージェントに接続する場合にだけ必要です。

HR_BACKENDS では、HR_BACKENDS=claude,codex,hermes のように読み込むバックエンドを選択します。事前に把握しておくべき既知の問題があります。hermes を省略した値を指定すると、コンテナはステータス 1 で直ちに終了し、エラーメッセージも表示されません。起動から 1 秒後に docker ps -aExited (1) を確認できますが、docker logs には有用な情報が表示されません。上流で修正されるまで、リストには hermes を残してください。Hermes だけをハーネスとして使用する場合は、Hermes エージェントを独自の VPS で実行するほうが小規模な構成になります。

コンソールを使わずに API を呼び出す

コンソールは必須ではありません。同じ API が両方に対応しており、Responses 形式のコントラクトを使用します。まずサインインしてセッション Cookie を取得します。

curl -c hr.cookies http://localhost:3000/api/selfhost/login \
  -H 'content-type: application/json' \
  -d '{"username":"harnessrouter","password":"your-password"}'

次にタスクを送信します。metadata.harness_id にはハーネスを指定し、接続したプロバイダーが実際に提供しているモデルを指定します。

curl -s -b hr.cookies http://localhost:3000/api/harness/v1/responses \
  -H 'content-type: application/json' \
  -d '{"input":"Reply with exactly this and nothing else: it works.",
       "metadata":{"harness_id":"codex"},
       "model":"gpt-5.4-mini",
       "stream":false}'

出力ブロックとトークン数を含む JSON オブジェクトが返れば、ハーネスは実行されています。harness_idcodex から claude に変更すると、同じリクエストが別のハーネスに送信されます。この切り替えこそが、このソフトウェアの存在理由です。上記のカスタム接続を使うと、すでにホストしている OpenAI 互換エンドポイントをハーネスに指定できます。VPS 上の自己ホスト型 DeepSeek ハーネスもこの方法で接続されています。

ラップトップからポートを公開せずにアクセスする

方法は 2 つあり、どちらも 0.0.0.0 のポートを直接公開しません。

SSH トンネルが最も手軽で、サーバーに何もインストールする必要がありません。手元のマシンのローカルポートから VPS の loopback へ転送します。

ssh -N -L 3000:127.0.0.1:3000 you@your-vps

このコマンドを実行したまま、ブラウザーで http://localhost:3000 を開きます。SSH が bind: Address already in use を出力する場合、ラップトップ上ですでに別のプロセスがポート 3000 を使用しています。その場合は -L 3100:127.0.0.1:3000 で別のローカルポートを指定し、ポート 3100 にアクセスします。

終端リバースプロキシは、他のユーザーもアクセスする必要がある場合の方法です。プロキシが TLS(transport layer security)証明書を保持し、loopback へ転送します。README には Caddy の設定例があります。

console.example.com {
    encode zstd gzip
    reverse_proxy 127.0.0.1:3000 {
        flush_interval -1      # agent turns stream for minutes; never buffer them
    }
}

flush_interval -1 は見落とされやすい行です。Agent は数分間にわたってストリームトークンを生成します。プロキシがレスポンスをバッファリングすると、そのトークンがターンの終了まで保持されるため、コンソールが停止したように見え、最後にすべての内容が一度に表示されます。Nginx では、location ブロック内の proxy_buffering off; が同等の設定です。どちらを選ぶ場合も、DNS 名はプロキシを指し、コンテナは loopback で待ち受けるようにします。リバースプロキシとしての Nginx、Caddy、Traefik の比較では、環境に適した製品を確認できます。

独立したユーザーで実行し、root では実行しない

Docker daemon は root として実行されます。また、docker グループのメンバーになることは root 権限を持つことと同等です。メンバーはホストのファイルシステムをマウントしたコンテナを起動できるためです。したがって、「チームを docker グループに追加する」ことは、provider key を保持するサーバー上で root 権限を配布することになります。

簡単な方法は、compose file と .env を所有する service account を作成し、それらのファイルを共有の home directory の外に置くことです。

sudo adduser --disabled-password --gecos "" harness
sudo install -d -o harness -g harness -m 750 /srv/harnessrouter

より強固な方法は rootless Docker です。この構成では、daemon 自体がその非特権ユーザーとして実行されます。newuidmapnewgidmap 用に uidmap package が必要です。また、そのユーザー用に /etc/subuid/etc/subgid で少なくとも 65536 個の subordinate UID が必要です。uidmap は Ubuntu archive にありますが、docker-ce-rootless-extras はありません。docker-ce-rootless-extras は Docker engine のインストール時に追加される、Docker 独自の apt repository(download.docker.com)から提供されます。その repository から engine をインストールしていない場合、grep -rl download.docker.com /etc/apt/sources.list.d/ は何も出力せず、以下のインストール処理で package を見つけられません。

sudo apt install -y uidmap docker-ce-rootless-extras
sudo loginctl enable-linger harness
sudo -iu harness
dockerd-rootless-setuptool.sh install
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
systemctl --user enable --now docker

ここでは loginctl enable-linger は省略できません。これがないと、最後の session が閉じた時点でユーザーの systemd instance が停止します。そのため、ログアウトすると container も停止します。docker info で結果を確認してください。Security Options の下に rootless が表示されます。rootless mode では、追加設定なしに 1024 未満の port に bind できません。ただし、ここでは port 3000 を使用するため問題ありません。アカウント自体の設定については、VPS で最小権限のユーザーを作成するで説明しています。

発生する問題と確認できる内容

コンテナの起動後、1 秒で終了し、ログも空です。 docker ps -a には Exited (1) と表示されます。これは上記の HR_BACKENDS の問題です。値に hermes が含まれていません。追加してください。

初回起動が完了しません。 ログが installing 行の後で止まり、ready on :3000 が表示されません。イメージに含まれていないため、ホストはネットワークに接続してエージェント CLI を取得できません。外向きの経路またはプロキシ設定を修正してから、再起動してください。

コンソールは読み込まれますが、すべてのタスクが失敗します。 プロバイダーが接続されていません。イメージにはモデルが組み込まれておらず、無料利用枠も含まれていません。そのため、新しいインスタンスでサインインできても、何も実行できません。

プロキシの背後で、回答の途中でコンソールが停止します。 ターンが終了すると、出力が 1 つのブロックで表示されます。これはレスポンスのバッファリングが原因です。Caddy では flush_interval -1 を、Nginx では proxy_buffering off; を設定してください。

トンネルは起動していますが、ノート PC から接続できません。 サーバー上で docker port harnessrouter を実行してください。何も表示されない場合、コンテナは何も公開していません。-p を指定せずに起動されたためです。

実行する価値はありますか?

複数の harness を実際に使っており、それぞれ 3 つずつ用意する代わりに、1 つのエンドポイントと 1 つの認証情報ストアにまとめたい場合は、実行する価値があります。また、その上に製品を構築し、harness を書き換えではなく設定値として扱いたい場合にも有効です。これが UHP で得られるものです。ただし、前述のとおり、このプロトコルはまだ新しい点に注意してください。

harness を 1 つしか使わない場合は、実行する価値はありません。サーバーにその CLI だけをインストールするほうが構成要素が少なく、ログインが間に入ることもありません。また、必要なのが 1 つの API の前段に複数の harness を置く構成ではなく、複数のエージェントが 1 つのタスクに協調して取り組む構成であれば、この方式は適していません。その場合は別のツールが必要です。この構成については、Omnigent などのマルチエージェント harnessを参照してください。いずれの場合も、デプロイ時のルールは変わりません。Loopback に bind し、パスワードを変更し、0.3.0 以上のタグを固定して使用し、専用ユーザーで実行します。

FAQ

HarnessRouter をポート 3000 で公開しても安全ですか?

いいえ。コンソールでは harness の作成、すべての transcript の読み取り、shell および filesystem にアクセスする agent の実行、接続した provider key の保持が可能です。そのため、ポートを開放するとこれらすべてが外部に公開されます。-p 127.0.0.1:3000:3000 を使用して loopback で公開し、SSH tunnel または TLS 終端を担う reverse proxy 経由でアクセスしてください。ホストの firewall だけでは不十分です。Docker は独自のルールを kernel の nat table に書き込むため、ufw で拒否と表示されていても、公開ポートはインターネットからの接続に応答します。sudo ss -ltnp | grep 3000 で確認してください。127.0.0.1:3000 と表示されるはずです。

ログインゲートが追加された HarnessRouter のバージョンはどれですか?

0.3.0 です。0.1.x0.2.0 は認証機能なしでリリースされており、現在も両方の tag が公開され、pull できます。そのため、これらを実行している場合は、誰にもポートを見つけられないことに依存している状態です。2026 年 8 月 19 日時点で最新の tag は 0.5.5 で、2026 年 8 月 18 日付けです。docker image ls harnessrouter/harnessrouter を実行して現在のバージョンを確認し、このページではなく Docker Hub の tag 一覧と比較してください。現行バージョンでもデフォルトパスワードを変更してください。

HR_BACKENDS を設定すると、なぜコンテナがすぐに終了するのですか?

hermes を含まない HR_BACKENDS の値を指定すると、既知の問題としてプロジェクトの README に記載されているとおり、コンテナはエラーメッセージを表示せず、status 1 ですぐに終了します。症状は、1~2 秒以内に docker ps -a 内へ Exited (1) が出力され、docker logs には有用な情報が何もないことです。upstream で修正されるまで、HR_BACKENDS=claude,codex,hermes のように、リストへ hermes を含めてください。

初回起動時に HarnessRouter はインターネットアクセスを必要としますか?

はい。agent CLI は image に同梱されず、初回起動時に取得されます。それぞれが独自のライセンスを持つためです。外向きの route がないホストでは installing の行が表示され、その後 ready on :3000 に到達しません。ダウンロードは volume ごとに 1 回だけ行われます。そのため、2 回目以降の起動は数秒で完了し、接続した model provider 以外へのネットワークアクセスは必要ありません。

コンソールのパスワードを紛失しました。再びログインするにはどうすればよいですか?

reset email はありません。account system も mail server も存在しないためです。コンテナを停止し、volume から /data/selfhost-auth.json を削除して、再度起動してください。その後、デフォルトの認証情報でサインインし、Profile ページから新しいパスワードを設定します。コンテナと volume の名前がともに harnessrouter の場合は、docker stop harnessrouter、次に docker run --rm -v harnessrouter:/data alpine rm -f /data/selfhost-auth.json、最後に docker start harnessrouter を実行します。