dshのhttp://127.0.0.1:3080が開けない理由
dshがhttp://127.0.0.1:3080を表示するのは、Web UIがlocalhostだけで待ち受けるためです。SSHトンネルで安全に接続する方法と、3080の公開が危険な理由を説明します。
dsh web: http://127.0.0.1:3080 の意味
VPS 上で DeepSeek Harness の Web プロファイルを起動すると、2 行を表示してから待機します。
dsh web: http://127.0.0.1:3080
Ready.127.0.0.1 はループバックアドレスです。マシンが自分自身と通信するときに使用するアドレスです。127.0.0.1 にバインドされたソケットは、その同じマシン上のプロセスからの接続だけを受け付け、外部からの接続は受け付けません。つまり、この行から2つのことが分かります。Web UI が待ち受けている場所と、接続を許可されている接続元です。接続できるのは、dsh を実行しているマシンだけです。
そのため、この URL をノートパソコンのブラウザーに貼り付けても何も起こりません。ノートパソコンの 127.0.0.1 はノートパソコン自身を指します。Harness は VPS の 127.0.0.1 で待ち受けていますが、これは別のマシンにある別のループバックスタックです。故障ではありません。接続を別のマシンまで転送する必要があります。
公式 README には、デフォルト設定が明記されています。「このコマンドは Web UI を起動し、デフォルトでは http://127.0.0.1:3080 で提供します」。バインドアドレスは Webserver host plugin である @deepseek-ai/dsh-host-webserver から取得されます。このプラグインの host キーには、「待ち受けるホスト。指定できる値は loopback と all-interfaces の2つ」と記載されています。変更しない限り、loopback が使用されます。ポートに不慣れな場合は、Linux でのポートの仕組みで、この構成の基礎となるアドレスとポートの関係を確認できます。
Web UI が localhost のみで待ち受ける理由
dsh はエージェントの実行基盤です。ブラウザーのタブは、シェルコマンドを実行し、選択したワークスペースディレクトリ内のファイルを読み書きし、モデル API key を消費するプロセスの操作画面です。そのページを読み込める人は誰でも、dsh を実行しているユーザーとして、それらすべてを操作できます。
したがって、ポート 3080 は読み取り専用のダッシュボードではありません。そのページを読み込むだけで、サーバー上でコマンドを実行できます。
Web UI を開くと、すぐにセッション一覧が表示されます。ログイン画面がないのは、developer preview にユーザーアカウントとリモート認証が用意されていないためです。loopback 上では、この構成に矛盾はありません。アクセス制御はオペレーティングシステムが担い、ローカルプロセスだけが接続できるからです。同じサーバーを、パブリック IP を持つ VPS 上で 0.0.0.0 にバインドすると、そのページが何も前段にない状態でインターネット全体からアクセス可能になります。自動スキャナーは一般的でないポートも継続的に探索するため、公開した 3080 は見つかるものとして扱ってください。
ファイアウォールでポート 3080 を開かないでください。また、パブリック VPS 上で Web サーバーのhostを0.0.0.0に設定しないでください。この組み合わせでは、接続した最初の相手にサーバー上でのコマンド実行権限を与えることになります。
同じ考え方は、サーバー上に配置するすべてのエージェント実行環境に当てはまります。そのため、VPS 上でコーディングエージェントを安全に実行する方法も同じ原則から始まります。エージェントの制御ポートは非公開のままにし、信頼できる仕組みを介して接続します。
ノート PC から dsh Web UI を開くにはどうすればよいですか?
正しい方法は3つあります。いずれの方法でも、harness は loopback にバインドしたままにします。
- SSH トンネル。公開インターフェースで新たに待ち受けるものはなく、認証情報もすでに用意されています。使用するべき方法です。
- プライベートなオーバーレイネットワーク。自分のデバイスからだけ UI にアクセスでき、他のユーザーからは見えません。
- TLS (transport layer security) を終端するリバースプロキシ。転送する前にパスワードを要求します。
違いは、ブラウザーから loopback までの通信を何が運ぶかです。いずれの方法でも、harness を loopback から移動させてはいけません。
SSH トンネルで接続する
VPS ではなく、ノート PC で実行します。
ssh -N -L 3080:127.0.0.1:3080 you@your-vps実行したままにして、ローカルブラウザーで http://127.0.0.1:3080 を開きます。Web UI が読み込まれます。
-L 引数には、コロンで区切られた3つのフィールドがあります。1つ目はノート PC で開くポートです。2つ目と3つ目は、各接続の転送先となるアドレスとポートです。重要なのは、中間フィールドの 127.0.0.1 が、トラフィックがVPSに到達した後に、VPS上のSSHサーバーによって解決されることです。これはノート PC ではなく、VPSのループバックアドレスを指します。まさに dsh が表示したアドレスなので、ブラウザーから直接接続できない場合でもトンネルが機能します。
-N は、SSHにリモートコマンドを実行させない指定です。そのため、シェルではなく転送専用になります。バックグラウンドでトンネルを実行し、失敗時にエラーを表示するには、次のようにします。
ssh -N -f -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 -L 3080:127.0.0.1:3080 you@your-vps-f は、認証後にSSHをバックグラウンドへ移行させます。ExitOnForwardFailure=yes は見た目以上に重要です。これがないと、転送の設定に失敗してもSSH接続自体は成功するため、セッションは動作しているのにトンネルは停止したまま、警告も表示されません。ServerAliveInterval=30 は30秒ごとに keepalive を送信します。これにより、カフェやホテルのルーターで発生するNAT(ネットワークアドレス変換)のタイムアウト後も、アイドル状態のトンネルを維持できます。
確認する内容
VPS上で、実際に待ち受けているものを確認します。
ss -ltnp | grep 3080正常な結果では、ループバックアドレスが表示されます。
LISTEN 0 511 127.0.0.1:3080 0.0.0.0:* users:(("node",pid=1042,fd=21))ローカルアドレスの列が 0.0.0.0:3080 になっている場合、Web UIはパブリックアドレスを含むすべてのインターフェースで待ち受けています。ほかの作業をする前に停止し、bind アドレスを修正してください。ss がソケットを表示するものの、users: フィールドが空の場合は、sudo を付けて実行してください。そうしないと、別のユーザーが所有するソケットのプロセス名は非表示になります。
トンネルの起動に失敗する場合
SSHは次のメッセージを表示して終了します。
bind [127.0.0.1]:3080: Address already in use
channel_setup_fwd_listener_tcpip: cannot listen to port: 3080これはサーバーではなく、ノート PC 側の問題です。ローカルの3080番ポートを、以前のトンネルなどがすでに使用している可能性があります。空いているローカルポートを指定します。
ssh -N -L 3081:127.0.0.1:3080 you@your-vps変更したのは最初のフィールドだけです。そのため、harness は3080で待ち受けたまま、ブラウザーでは http://127.0.0.1:3081 に接続します。2つの番号を一致させる必要はありません。
トンネルは起動するものの、ブラウザーに接続拒否または空の応答が表示される場合、トラフィックはVPSまで到達していますが、転送先で待ち受けているものがありません。dsh が終了したか、別のポートに bind しています。サーバー上で ss -ltnp | grep 3080 を実行して確認してください。
ここで、もう1つ注意すべき点があります。フォアグラウンドで実行した npx @deepseek-ai/dsh web は、シェルが閉じると終了します。そのため、ログアウトした時点で harness も停止します。VPS上で coding agent を実行し続ける場合と同じように、tmux 内で起動するか、systemd の user service として実行してください。SSH側の作業中に、先に VPSのSSHを強化することも推奨します。このトンネルでは、SSHログインが agent への唯一の入口になるためです。
プライベートオーバーレイネットワーク経由でアクセスする
オーバーレイネットワークを使うと、VPS とノートパソコンに、自分のデバイスだけが参加するプライベートネットワーク上のアドレスを割り当てられます。一般的な選択肢は Tailscale です。この用途にはその serve コマンドが適しています。tailscaled は VPS 上で実行し、localhost:3080 自体に接続するため、ハーネスは loopback 上で待ち受けたままです。dsh の設定を変更する必要もありません。
tailscale serve --bg localhost:3080
tailscale serve statusこれで UI には、tailnet 内のマシン名を使って HTTPS 経由でアクセスできます。パブリックインターフェース上でポートを開く必要もありません。ただし、tailnet 用の HTTPS 証明書を有効にする必要があります。有効にしないと、serve が提示する証明書を取得できません。停止するには、off を付けて同じコマンドを再度実行します。
tailscale serve --https=443 offserve を使用し、funnel は使用しないでください。Funnel は同じ接続先をパブリックインターネットに公開するため、認証されていないエージェントランタイムを開放ポート上で実行する状態に戻ります。2 つのコマンドはほぼ同じに見えますが、動作は正反対です。どちらかを入力する前に、Tailscale Serve と Funnel の違いを確認してください。設定方法自体については、プライベートネットワークとしての Tailscaleで説明しています。
パスワードを確認するリバースプロキシ経由でアクセスする
これは、ポートを実際にインターネットへ公開する方式です。そのため、認証だけが第三者によるサーバー上でのコマンド実行を防ぎます。複数人が UI を使う必要があり、利用者ごとにトンネルを用意するのが現実的でない場合に選択します。
harness は 127.0.0.1:3080 で動作したままにします。nginx は同じホスト上で動作するため loopback に接続でき、証明書とパスワードファイルを使用して 443 で待ち受けます。
server {
listen 443 ssl;
server_name dsh.example.com;
ssl_certificate /etc/letsencrypt/live/dsh.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dsh.example.com/privkey.pem;
auth_basic "dsh";
auth_basic_user_file /etc/nginx/dsh.htpasswd;
location / {
proxy_pass http://127.0.0.1:3080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 3600s;
proxy_buffering off;
}
}パスワードファイルを作成して reload します。
sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/dsh.htpasswd you
sudo nginx -t && sudo systemctl reload nginxnginx -t は syntax is ok に続いて test is successful を出力します。ファイルが壊れている場合、reload は失敗しますが、実行中の設定は維持されます。そのため、無条件に restart するのではなく、エラーを確認してください。
これらのプロキシ設定のうち、3 行は単なる装飾ではありません。Upgrade ヘッダーと Connection ヘッダーによって WebSocket handshake が通過します。これらがないとページは読み込まれますが、更新されません。proxy_read_timeout 3600s は 60 秒のデフォルト値を置き換えます。設定しない場合、長時間の agent 実行が応答の途中で切断され、UI が停止したように見えます。proxy_buffering off は、応答が完了するまで保持せず、モデルの出力を到着した順にブラウザーへ送信します。残りの設定については nginx reverse proxy の設定を行ごとに確認する で説明しています。自動証明書を使って同じ構成を nginx、Caddy、Traefik で実現する方法については nginx、Caddy、Traefik から選択する を参照してください。
どのプロキシを選択しても、firewall では 3080 を閉じたままにしてください。外部からの経路を、認証済みのプロキシ経由だけにするためです。ルールについては ufw firewall の基本 で説明しています。TLS 上の Basic 認証は最低限の対策であり、完全なセキュリティモデルではありません。そのパスワードを持つ人は、サーバー上で shell を実行できます。可能であれば tunnel を優先してください。
dsh web が待ち受けるポートを変更するには
--port は launcher ではなく Web アプリケーションに属します。CLI ドキュメントには、次の例が直接示されています。
dsh --profile web --port 8080dsh web は --profile web の alias なので、dsh web --port 8080 も同じコマンドです。launcher は自身の flag だけを解析し、それ以降を起動した profile に渡します。したがって、launcher の flag は先に指定します。launcher が認識しない最初の token から、アプリケーションの引数として扱われます。--port は profile の後に指定し、前には置かないでください。
コマンドが出力する URL を読み、その値を推測しないでください。この行には、server が実際に bind したアドレスが示されます。次に、tunnel の最後のフィールドをその値に合わせます。
ssh -N -L 3080:127.0.0.1:8080 you@your-vps永続的に変更する場合、ポートは command line ではなく profile の設定に記述します。web と headless の profile は、初回使用時に ~/.dsh 配下の配布済み template から自動初期化されます。すべての層を反映した後、実際に有効な設定を確認するには、次を実行します。
dsh --dump-configwebserver plugin が公開する key は host と port の2つだけです。port を 0 に設定すると、空いているポートを OS に割り当てさせます。ドキュメントには「zero requests an OS-assigned port」と記載されています。これによりポート競合は発生しませんが、再起動のたびに番号が変わるため、tunnel には適していません。
dsh はなぜ address already in use で失敗しますか?
別のプロセスがそのアドレスとポートをすでに使用しているためです。カーネルは 2 つ目の bind を拒否します。Node では次のように表示されます。
Error: listen EADDRINUSE: address already in use 127.0.0.1:3080変更を加える前に、使用中のプロセスを特定します。
sudo ss -ltnp | grep 3080users:(("node",pid=1042,fd=21)) フィールドには、プロセス名と PID が表示されます。通常は、停止したと思っていた以前の dsh が原因です。デタッチされた tmux ウィンドウ内で、まだ実行中になっていることもよくあります。kill 1042 でそのプロセスを停止するか、新しいインスタンスを別のポートで起動します。127.0.0.1:3080 と 0.0.0.0:3080 も互いに競合する点に注意してください。すべてのインターフェイスに bind すると、loopback もすでに対象になるためです。
バージョンを固定する。これは開発者向けプレビューである
README には明記されています。DeepSeek Harness は開発者向けプレビューで、短い間隔で更新されており、互換性を壊す変更が行われる予定です。
npx @deepseek-ai/dsh web は実行するたびに、公開済みの最新バージョンへ解決されます。1 週間触れていないサーバーでも、次回の起動時に異なるフラグを持つ別の CLI が起動する可能性があります。再起動がアップグレードにならないよう、バージョンを固定します。
npx @deepseek-ai/dsh@0.1.0-rc.7 web2026 年 8 月時点で、公開されているパッケージのバージョンは 0.1.0-rc.7 です。受け入れる前に、単独の npx で取得される内容を確認してください。
npm view @deepseek-ai/dsh versionプレビューのリリース間で、フラグの位置がランチャーと Web アプリケーションの間で変更されます。--port がこのガイドの説明どおりに動作しなくなった場合は、推測で対応せず、アプリケーション自身にフラグ一覧を表示させてください。
dsh --profile web --helpインストール自体、workspace のセットアップ、model key については、VPS に DeepSeek Harness をインストールするを参照してください。アクセス手順だけを短く確認する場合は、VPS 上の dsh Web UI に接続するで、推論の説明を省いたトンネルの構成を確認できます。
FAQ
ノートパソコンのブラウザーで http://127.0.0.1:3080 を開けないのはなぜですか?
127.0.0.1 は、コマンドを入力しているマシンを指すためです。DeepSeek Harness Web UI は VPS の loopback アドレスにバインドされているため、VPS 上のプロセスだけが接続できます。ノートパソコンには別の loopback があり、そこでは port 3080 を待ち受けるプロセスがありません。ssh -N -L 3080:127.0.0.1:3080 you@your-vps を使って SSH 経由で port を転送し、ローカルで http://127.0.0.1:3080 を開いてください。-L 引数の中央のフィールドはサーバー側で解決されるため、harness を指すことができます。
公開 VPS で dsh Web UI を 0.0.0.0 にバインドしても安全ですか?
いいえ。Web UI は、dsh を実行するユーザーの権限で shell コマンドを実行し、ファイルを編集する agent の操作画面です。また、developer preview にはログイン画面がありません。公開 IP のすべてのインターフェースにバインドすると、port 3080 に到達できる誰でもサーバー上でコマンドを実行できます。バインド先は 127.0.0.1 に保ち、firewall では 3080 を閉じてください。接続には SSH tunnel、private overlay network、または password を要求する reverse proxy を使用します。
SSH セッションを閉じた後も dsh Web UI を実行し続けるにはどうすればよいですか?
foreground の npx @deepseek-ai/dsh web は login shell の子プロセスであるため、その shell が終了すると kill されます。tmux session 内で起動し、Ctrl-b d で detach してください。または、lingering を有効にした systemd user service として実行します。tunnel と harness は独立しています。harness 自体が login より長く存続する parent を持っていれば、実行中の harness に影響を与えず、SSH tunnel は何度でも切断して再構築できます。
nginx の背後で長時間の agent 実行を行うと、dsh Web UI が途中で停止するのはなぜですか?
nginx のデフォルトの proxy_read_timeout は 60 seconds です。そのため、1 分間データを生成しない接続を nginx が閉じます。長時間の agent step では、この状態が容易に発生します。location block で proxy_read_timeout 3600s; を設定してください。proxy_buffering off; を追加して、出力を生成時に browser へ stream します。さらに、proxy_http_version 1.1; を使って Upgrade と Connection header を渡し、WebSocket handshake を成功させます。これらの header がないとページは読み込まれますが、更新を受信できません。