RustDeskの自前サーバをVPSのWebARENAで構築する

リモートデスクトップサービスRustDeskについて以前の記事に書いた。
リモートデスクトップアプリRustDeskを使ってみる

RustDeskの公開サーバを使用していて決定的に困ることは現在は起きていないが、たまにログインを求められることはある。
自前サーバの利用を試してみたく、やってみることにした。

また、WebARENAの初期構築についても書いた。
VPS利用のためWebARENA Indigoを使用してみる
VPS利用のためWebARENA Indigoを使用してみる その2

自前サーバ「セルフホスト」に関するドキュメントは下記
https://rustdesk.com/docs/ja/self-host/

ちょっと難しいのと、前回記事に書いた通り、
WebARENA+ubuntu用として、ChatGPTに聞くと下記の手順を示された。
Step 1 WebARENA側のVPSを確認
Step 2 Ubuntuの初期確認・アップデート
Step 3 Dockerをインストール
Step 4 RustDesk Serverの配置・設定
Step 5 RustDesk Serverの起動・動作確認
Step 6 RustDeskクライアントを自前サーバーへ接続
Step 7 運用設定・最終確認

今回はいよいよ本丸というかStep 3から進める。

Step 3 Dockerをインストール

まずはDockerが入っているかどうか確認
$ docker --version
Command 'docker' not found, but can be installed with:
sudo snap install docker # version 29.8.0, or
sudo snap install docker # version 29.6.1
sudo apt install docker.io # version 29.1.3-0ubuntu3~24.04.2
sudo apt install podman-docker # version 4.9.3+ds1-1ubuntu0.2
See 'snap info ' for additional versions.

入っていない。
親切にインストール手順まで案内してくれている。
しかし、今回はRustDesk向けなので、RustDesk公式ドキュメントが推奨する下記の方法に準じてインストールすることにした。

(1)bash <(wget -qO- https://get.docker.com)
(2)wget rustdesk.com/oss.yml -O compose.yml
(3)sudo docker compose up -d

ここでもう少し丁寧にする。
・(1)に関してはインストール用スクリプトの中身を見てから実行するため、一旦ファイルに保存する手順とする。
 まぁ見てもあまりわからなかったけど。
・2回目のwget前にRustDeskディレクトリを作成し移動。
・2回目のwgetではhttps:// を明確に補うことにした。
・最後にコンテナ起動の前にdocder compose configで構文を確認する手順を入れた。
(1)wget https://get.docker.com -O get-docker.sh
(2)sudo sh get-docker.sh
(3)mkdir RustDesk
(4)cd RustDesk
(5)wget https://rustdesk.com/oss.yml -O compose.yml
(6)compose.ymlの内容を編集
(7)sudo docker compose config
(8)sudo docker compose up -d

Step 3 (1)(2)
Step 4 (3)(4)(5)(6)(7)
Step 5 (8)
とする。

(1)インストーラーをダウンロードしローカルに保存
$ wget https://get.docker.com -O get-docker.sh
--2026-09-19 21:40:13-- https://get.docker.com/
Resolving get.docker.com (get.docker.com)... 13.249.165.103, 13.249.165.78, 13.249.165.12, ...
Connecting to get.docker.com (get.docker.com)|13.249.165.103|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 23731 (23K) [text/plain]
Saving to: ‘get-docker.sh’

get-docker.sh 100%[===================================================>] 23.17K --.-KB/s in 0s
2026-09-19 21:40:14 (86.1 MB/s) - ‘get-docker.sh’ saved [23731/23731]

Dockerのインストーラーとなるスクリプトが get-docker.shとして保存された。

(2)インストーラーを実行
$ sudo sh get-docker.sh
を実行しインストール。

インストールされたかどうかを確認
$ docker --version
Docker version 29.8.1, build 4a63305

$ sudo docker compose version
Docker Compose version v5.5.1

Step 4 RustDesk Serverの配置・設定

(3)作業ディレクトリ作成
mkdir RustDesk

(4)作業ディレクトリへ移動
cd RustDesk

(5)設定ファイルをダウンロードしローカルに保存
$ wget https://rustdesk.com/oss.yml -O compose.yml
--2026-09-20 07:38:24-- https://rustdesk.com/oss.yml
Resolving rustdesk.com (rustdesk.com)... 172.66.167.80, 104.20.19.94, 2606:4700:10::ac42:a750, ...
Connecting to rustdesk.com (rustdesk.com)|172.66.167.80|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 402 [application/octet-stream]
Saving to: ‘compose.yml’

compose.yml 100%[===================================================>] 402 --.-KB/s in 0s
2026-09-20 07:38:24 (108 MB/s) - ‘compose.yml’ saved [402/402]

compose.ymlが保存された。

(6)compose.ymlの内容を編集

hbbsに常にリレーにするALWAYS_USE_RELAY=Yを入れる。後からわかったことだが、試験のときログに残らず本当に自前サーバを通っているのか? となった。ChatGPTが言うには、P2Pが使えるときはリレーにならずログに残らないのではないかとのこと。公開サーバではなく自前サーバでリレーされていることを確認するために、一旦入れて、最後には外す。
もうひとつ、hbbrに公開鍵を登録する。
公開鍵は data/id_ed25519.pub に保存されたファイルの内容(文字列)

編集前

services:
  hbbs:
    container_name: hbbs
    image: rustdesk/rustdesk-server:latest
    command: hbbs
    volumes:
      - ./data:/root
    network_mode: "host"
    depends_on:
      - hbbr
    restart: unless-stopped

  hbbr:
    container_name: hbbr
    image: rustdesk/rustdesk-server:latest
    command: hbbr
    volumes:
      - ./data:/root
    network_mode: "host"
    restart: unless-stopped

編集後

services:
  hbbs:
    container_name: hbbs
    image: rustdesk/rustdesk-server:latest
    environment:
      - ALWAYS_USE_RELAY=Y
    command: hbbs
    volumes:
      - ./data:/root
    network_mode: "host"
    depends_on:
      - hbbr
    restart: unless-stopped

  hbbr:
    container_name: hbbr
    image: rustdesk/rustdesk-server:latest
    environment:
      - KEY=<公開鍵>
    command: hbbr
    volumes:
      - ./data:/root
    network_mode: "host"
    restart: unless-stopped

(7)設定ファイルのチェック
sudo docker compose config

name: rustdesk
services:
  hbbr:
    command:
      - hbbr
    container_name: hbbr
    environment:
      KEY: <公開鍵>
    image: rustdesk/rustdesk-server:latest
    network_mode: host
    restart: unless-stopped
    volumes:
      - type: bind
        source: /home/ubuntu/RustDesk/data
        target: /root
        bind: {}
  hbbs:
    command:
      - hbbs
    container_name: hbbs
    depends_on:
      hbbr:
        condition: service_started
        required: true
    environment:
      ALWAYS_USE_RELAY: 'Y'
    image: rustdesk/rustdesk-server:latest
    network_mode: host
    restart: unless-stopped
    volumes:
      - type: bind
        source: /home/ubuntu/RustDesk/data
        target: /root
        bind: {}

設定が正しいこと、エラーがないことを確認する。

Step 5 RustDesk Serverの起動・動作確認

(8)コンテナの起動
(cd RustDesk)
sudo docker compose up -d

[+] up 6/6
✔ Image rustdesk/rustdesk-server:latest Pulled 3.6s
✔ Container hbbr Started 0.5s
✔ Container hbbs Started 0.5s

正常に起動する。

(9)コンテナが起動されていることの確認
$ sudo docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
hbbr rustdesk/rustdesk-server:latest "hbbr" hbbr About a minute ago Up About a minute
hbbs rustdesk/rustdesk-server:latest "hbbs" hbbs About a minute ago Up About a minute

起動している

(10)ログを確認
$ sudo docker compose logs --tail=50
特にエラー等はなし。

(11)必要ポートの状態を確認
$ sudo ss -lntup | grep -E ':(21115|21116|21117|21118|21119)\b'
udp UNCONN 0 0 *:21116 : users:(("hbbs",pid=5392,fd=17))
tcp LISTEN 0 128 *:21119 : users:(("hbbr",pid=5345,fd=10))
tcp LISTEN 0 128 *:21118 : users:(("hbbs",pid=5392,fd=20))
tcp LISTEN 0 128 *:21117 : users:(("hbbr",pid=5345,fd=9))
tcp LISTEN 0 128 *:21116 : users:(("hbbs",pid=5392,fd=18))
tcp LISTEN 0 128 *:21115 : users:(("hbbs",pid=5392,fd=19))

TCP 21115-21119はLISTEN、UDP 21116は待ち受けになっていて正常。

一方WebARENAではファイアウォールを入れている。

TCP 21115-21117とUDP 21116は通す。TCP 21118-21119は通さない。

(12)外部からポートへのアクセスを確認
★TCP側確認
自分の端末(Mac)のターミナルから
$ nc -vz <VPSのIPアドレス> 21115
Connection to <VPSのIPアドレス> 21115 port [tcp/] succeeded!
$ nc -vz <VPSのIPアドレス> 21116
Connection to <VPSのIPアドレス> 21116 port [tcp/] succeeded!
$ nc -vz <VPSのIPアドレス> 21117
Connection to <VPSのIPアドレス> 21117 port [tcp/*] succeeded!

すべて疎通成功。

★UDP側確認
VPSでポートを監視状態にする
sudo tcpdump -ni any udp port 21116

ログが流れているが、止めずにそのまま。

自分の端末(Mac)からVPSのポートへecho
echo test | nc -u -w1 <VPSのIPアドレス> 21116

VPSに下記のような行がでればOK。
ens10 In IP <端末のIPアドレス>.<端末のポート> > <VPSのIPアドレス>.21116: UDP, length 5

ここまででRustDeskサーバの起動と必要ポートのアクセスが確認できた。

Step 6 RustDeskクライアントを自前サーバーへ接続

遠隔操作をする側、遠隔操作をされる側の両方(どちらもRustDeskクライアントという)で、自前サーバへ繋ぐ設定をする。
RustDeskを起動し、[設定]>[ネットワーク]>[認証/中継サーバー]
認証サーバ:サーバIPアドレス
中継サーバ:サーバIPアドレス
APIキー:空欄
Key:公開鍵

サーバでは下記のコマンドでポート21116を監視状態で待機させる。

sudo tcpdump -ni any host <サーバIPアドレス> and port 21116

すると、下記のような行が出て待機させる。

tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes

ここで、クライアントのRustDeskを再起動すると、ダンプの行が増えていく。
----------
23:59:45.516564 ens10 In IP <クライアントIPアドレス>.36459 > <サーバIPアドレス>.21116: UDP, length 15 23:59:45.516709 ens10 Out IP <サーバIPアドレス>.21116 > <クライアントIPアドレス>.36459: UDP, length 2 23:59:52.660607 ens10 In IP <クライアントIPアドレス>.23996 > <サーバIPアドレス>.21116: Flags [F.], seq 1, ack 1, win 2057, options [nop,nop,TS val 2888007100 ecr 3963494463], length 0 23:59:52.660828 ens10 Out IP <サーバIPアドレス>.21116 > <クライアントIPアドレス>.23996: Flags [F.], seq 1, ack 2, win 510, options [nop,nop,TS val 3963512531 ecr 2888007100], length 0 23:59:52.685546 ens10 In IP <クライアントIPアドレス>.23996 > <サーバIPアドレス>.21116: Flags [.], ack 2, win 2057, options [nop,nop,TS val 2888007128 ecr 3963512531], length 0 23:59:52.702757 ens10 In IP <クライアントIPアドレス>.23861 > <サーバIPアドレス>.21116: Flags [S], seq 4765219, win 65535, options [mss 1412,nop,wscale 6,nop,nop,TS val 2313083021 ecr 0,sackOK,eol], length 0 23:59:52.702827 ens10 Out IP <サーバIPアドレス>.21116 > <クライアントIPアドレス>.23861: Flags [S.], seq 1059828799, ack 4765220, win 65160, options [mss 1460,sackOK,TS val 3963512573 ecr 2313083021,nop,wscale 7], length 0 23:59:52.730407 ens10 In IP <クライアントIPアドレス>.23861 > <サーバIPアドレス>.21116: Flags [.], ack 1, win 2057, options [nop,nop,TS val 2313083048 ecr 3963512573], length 0
----------

RustDeskクライアントからhbbs の21116へ通信していることが確認できた。
操作する側のクライアントと操作される側のクライアントそれぞれでこの確認を行う。

この後は実際にクライアント同士を接続して、遠隔操作できる状態を作る。
実際に問題なくつながった。
では、「本当に自前サーバを経由しているか」をサーバ上でどう確認するかである。

ChatGPTからは、
cd ~/RustDesk
sudo docker compose logs --tail=50 hbbs hbbr
これで、ログに現れるはずだと最初言われた。しかし、何も残らなかったのだ。
そこで、P2Pでいけるときは hbbr経由(リレー)しないのではないか? との推論のもと、
compose.ymlに
environment:
- ALWAYS_USE_RELAY=Y
を「テスト用に」入れた次第だ。

ALWAYS_USE_RELAY=Y
のときは必ずリレーされ、
sudo docker compose logs --tail=50 hbbs hbbr
のログに下記のような接続がのこる。

----------
hbbr | [2026-09-21 05:48:50.573495 +00:00] INFO [src/relay_server.rs:453] New relay request <リクエストID> from [::ffff:<操作する側IPアドレス>]:63750
hbbr | [2026-09-21 05:48:50.681926 +00:00] INFO [src/relay_server.rs:437] Relayrequest <リクエストID> from [::ffff:<操作される側IPアドレス>]:14219 got paired
hbbr | [2026-09-21 05:48:50.681994 +00:00] INFO [src/relay_server.rs:443] Both are raw
----------

これで自前サーバを使っていることが確認できた。

最終的には、
environment:
- ALWAYS_USE_RELAY=Y
を削除し、
Docker Composeを再起動
sudo docker compose up -d

Step 7 運用設定・最終確認

サーバ再起動後にも、Dockerが起動されるかどうかの確認。
コマンドは下記。

sudo systemctl is-enabled docker

enabled となれば問題ない。
しかし、実際にVPSを再起動してRustDesk再接続まで確認する。