VPSでTailscaleサブネットルーターを構築する方法
VPSからプライベートネットワークをtailnetへ広告する手順です。ルート承認、再起動後も有効なIP転送、Linuxで必要な--accept-routesフラグを確認できます。
Tailscale subnet router の役割
Tailscale subnet router は、プライベート IP アドレスの範囲全体を tailnet に広告するマシンです。その範囲内のデバイスで Tailscale が動作していなくても、tailnet 上のすべてのデバイスからそのアドレスへアクセスできます。tailnet は、1 つのアカウントまたは組織にサインインしているデバイスで構成される、プライベートな Tailscale ネットワークです。これと混同されやすいのが exit node で、役割は逆です。exit node は、デバイスのすべてのトラフィックを VPS 経由で外部へ送信し、そのデバイスのパブリックインターネットへの経路を VPS にします。
要点を 1 文で説明すると、subnet router は 1 つのプライベートネットワークを tailnet から到達可能にします。exit node は、パブリックトラフィックが外部へ出る場所を変更します。後者が必要な場合は、VPS で Tailscale exit node を実行する方法を参照してください。これらは別々のフラグであり、1 台の VPS で同時に両方を実行できますが、解決する問題は異なり、失敗する原因も異なります。
VPS でサブネットルーターが必要になる場合
一般的なのは、プロバイダーからすでにプライベートネットワークが提供されているケースです。VPS にはパブリックアドレスと、プライベートセグメント上の 2 つ目のインターフェースがあります。そのセグメント上の他のサーバーには、パブリックアドレスがありません。たとえばデータベースは 10.0.0.20、バックアップ先は 10.0.0.30 です。1 台の VPS に Tailscale をインストールして 10.0.0.0/24 を広告すると、ノートパソコンからそれらのプライベートアドレスへ直接アクセスできます。セグメント上の他の設定を変更する必要はなく、データベースにも引き続きパブリックアドレスはありません。そのセグメントから必要なのが 1 つのポートで動作する 1 つの Web アプリだけなら、範囲全体を広告するのは過剰です。その場合は、Tailscale serve でその単一ポートに HTTPS を設定できます。同じ考え方は、意図的に localhost のみで待ち受けるデーモンにも当てはまります。たとえば systemd の下でヘッドレスに動作する dsh では、その VPS の tailnet アドレスを使うことで、UI にアクセスするために別途維持していた SSH トンネルを置き換えられます。
もう 1 つは、VPS の反対側にあるネットワークです。独自のルーターの背後にある自宅やオフィスの LAN (local area network)、または Tailscale を実行できない機器のラックが該当します。管理対象スイッチや、ファームウェアが固定された古い NAS などです。そのネットワーク上の 1 台の Linux マシンを、同じネットワーク上にある他のすべての機器のサブネットルーターにします。自宅では、すでに運用しているハイパーバイザー上の小さな VM をそのマシンにすることがよくあります。どちら側にサービスを配置するか決める前に、自宅の Proxmox ホストとレンタル VPS のコストを比較する計算を確認しておく必要があります。
どちらのケースにも共通する要件が 1 つあります。サブネットルーターは、広告する範囲へ、自身のルーティングテーブルとファイアウォールを使って、すでに到達できなければなりません。Tailscale がこの接続を構築するわけではありません。Tailscale はルーターまでトラフィックを運び、転送処理をカーネルに渡します。
Tailscale をインストールし、最初にローカルルートを確認する
curl -fsSL https://tailscale.com/install.sh | shこのスクリプトはディストリビューションを検出し、Tailscale のパッケージリポジトリを追加し、tailscale コマンドと tailscaled デーモンをインストールしてから、サービスを有効化します。systemctl is-active tailscaled で確認すると、active と表示されるはずです。
何よりも先に、VPS から広告する予定のネットワークへ到達できることを確認します。
ip route show
ping -c3 10.0.0.20ip route show には、10.0.0.0/24 dev enp7s0 proto kernel scope link src 10.0.0.5 のように、実際のインターフェース上のプライベート範囲が表示されている必要があります。ここで、つまりルーター自体への ping が失敗する場合、Tailscale のどのフラグを指定しても解決できません。原因は VPS のネットワーク設定か、対象ホストのファイアウォールです。後続のテストはすべてこの接続に依存するため、まず問題を解決してください。
IP 転送を有効にし、再起動後も維持する
Linux マシンは、転送が有効でない限り、自分宛てではないパケットを破棄します。他のマシン宛てのパケットを転送することがサブネットルーターの役割なので、この手順は省略できません。
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.confsysctl net.ipv4.ip_forward で確認します。net.ipv4.ip_forward = 1 と表示されるはずです。
この手順を中途半端に行うケースは少なくありません。sudo sysctl -w net.ipv4.ip_forward=1 はすぐに適用されますが、次回のブートで失われます。そのため、サブネットルーターは数週間動作した後、カーネル更新後の再起動の翌朝に停止します。分かりにくいのは、問題が発生しているように見えないことです。tailscale status ではノードがオンラインのままで、管理コンソールでもルートが承認済みと表示され、クライアントにもルートがインストールされたままです。パケットは VPS に到達しますが、カーネルがログを記録せずに破棄します。値を /etc/sysctl.d/99-tailscale.conf に書き込むことで、再起動後も設定が復元されます。
転送を無効にしたままルートを広告すると、tailscale up がその時点で警告します。Warning: IPv4 forwarding is disabled. Subnet routes and exit nodes may not work correctly. に近い行が表示されます。そのコマンドの出力を読み、スクロールして通り過ぎないでください。
ルートを広告する
sudo tailscale up --advertise-routes=10.0.0.0/24すでに tailnet にサインインしている VPS では、代わりに設定をその場で変更します。
sudo tailscale set --advertise-routes=10.0.0.0/24以降の変更では tailscale set を使用します。単一のフラグを指定して tailscale up を再実行すると、再指定しなかったフラグがリセットされます。また CLI は、設定をこの方法で変更するには、デフォルト以外のすべてのフラグを指定する必要があるというエラーを表示して処理を停止します。tailscale set は 1 つの設定だけを変更し、その他の設定はそのまま保持します。
複数の範囲は、スペースを入れずにカンマ区切りの 1 つのリストに指定します: --advertise-routes=10.0.0.0/24,192.168.50.0/24。各エントリは、CIDR 表記(classless inter-domain routing、10.0.0.0/24 形式)のネットワークアドレスである必要があります。誤って自分のホストアドレス(10.0.0.5/24)を指定すると、プレフィックスの後ろのビットが 0 ではないため拒否されます。エラーには、おそらく意図していたプレフィックスも示されます。広告を停止するには、sudo tailscale set --advertise-routes= で空のリストを設定します。
管理コンソールで経路を承認する
経路の広告はリクエストであり、変更ではありません。管理者が承認するまで、クライアントはその経路を受信せず、レンジ内のどの宛先にも到達できません。これは意図的な仕様です。自分自身を全員のルーティングテーブルに追加できるマシンがあると、任意のレンジのトラフィックを傍受できるためです。
管理コンソールの Machines ページで承認します。VPS にはサブネットバッジが表示されます。その行を開き、サブネットセクションで経路設定を編集し、経路にチェックを入れて保存します。
承認はプレフィックス単位で行われます。今日 10.0.0.0/24 を広告し、来月 192.168.50.0/24 を広告すると、新しいプレフィックスは未承認の状態で追加され、古いプレフィックスは引き続き機能します。承認済みの経路と無視された経路は VPS からは同じように見えるため、ほかの調査を始める前にコンソールを確認してください。
tailnet ポリシーファイルに autoApprovers ブロックを追加すると、手動の手順を省略できます。
{
"autoApprovers": {
"routes": {
"10.0.0.0/24": ["tag:subnet-router"]
}
}
}次に、そのタグ(sudo tailscale up --advertise-routes=10.0.0.0/24 --advertise-tags=tag:subnet-router)を付けてノードを起動すると、経路が広告された時点で承認されます。タグは、同じポリシーファイルの tagOwners セクションにあらかじめ存在している必要があります。スクリプトで VPS を再構築する場合は、この設定を行う価値があります。再構築されたノードは新しいノードとして扱われ、その経路は再び未承認の状態で始まるためです。
--accept-routes を指定しないと Linux クライアントがルートを無視する理由
ルートは広告され、承認されています。スマートフォンと Mac からは 10.0.0.20 に接続できます。しかし Linux ラップトップからは接続できず、管理コンソールにも問題は表示されません。
サブネットルートを受け入れるとは、クライアントのルーティングテーブルにエントリを書き込むことです。Android、iOS、macOS、tvOS、Windows では、Tailscale クライアントがこれを自動的に行います。Linux では行いません。Linux マシンは、意図してルーティングテーブルを設定したサーバーまたはルーターであることが多く、ネットワークから学習した /24 を黙って追加すると、そのマシンがすでに処理しているトラフィックを壊す可能性があるためです。そのため Linux では、各クライアントで明示的に有効化します。
sudo tailscale set --accept-routes次に、ルートがどこに登録されたかを確認します。
ip route show table 52
ip route get 10.0.0.20Linux の Tailscale は、受け入れたルートを main ルーティングテーブルには追加しません。ルーティングテーブル 52 に追加し、ポリシールールを設定します。これらのルールは、優先度 5210 から 5270 の範囲で ip rule show に表示され、一致しないパケットをそのテーブルへ送ります。そのため、ip route show だけでは 10.0.0.0/24 は表示されません。このコマンドだけを確認した読者は、--accept-routes が何もしていないと判断します。実際の状態を表示するコマンドは ip route show table 52 であり、tailscale0 に広告された範囲が表示されるはずです。
知っておくべき例外が 1 つあります。この Linux ノード自体が、ローカルネットワーク用の 2 台目のサブネットルーターである場合、--accept-routes によって、自身に直接接続されたサブネット宛てのトラフィックが自身のインターフェースではなく、もう一方のルーターを経由するようになります。高可用性ペアの待機ルーターでは、--accept-routes を無効のままにして、広告だけを行ってください。
障害パターン: 2 台のルーターが重複する範囲を広告している
2 台のサブネットルーターが同一の範囲を広告してはいけません。プレフィックス長が異なる範囲の重複は許容され、Tailscale は最も具体的な経路を選択します。ルーター A が 10.0.0.0/24、ルーター B が 10.0.0.0/16 を広告している場合、10.0.0.20 宛てのトラフィックは A に送られます。
A がオフラインになった場合の動作は、予想と異なることがあります。Tailscale は、より具体性の低い経路へフォールバックしません。10.0.0.20 宛てのトラフィックは停止しますが、10.1.0.20 宛てのトラフィックは B 経由で引き続き機能します。症状としてはプライベートネットワークの半分が停止したように見えますが、原因は、より具体的なプレフィックスを保持するノードが 1 台オフラインになったことです。フェイルオーバーが必要な場合は、広い範囲を広告するルーターでも狭いプレフィックスを広告し、両方のルーターが同じアドレスをカバーするようにします。
もう 1 つの重複は、クライアントに近い場所で発生します。192.168.1.0/24 のホテルネットワークに接続しているときに、サブネットルーターが 192.168.1.0/24 を広告すると、同じ宛先をめぐって 2 つの経路が競合します。どちらが優先されるかはプラットフォームによって異なります。Linux では、Tailscale 自身のルールより前にルールを追加し、ローカルアドレスが main table を使用するようにします。
sudo ip rule add to 192.168.1.0/24 priority 2500 lookup mainこのルールは永続化されないため、次回のブート時に消えます。根本的な解決策は、実際のネットワークで遭遇しないプライベート範囲を選ぶことです。192.168.0.0/24 と 192.168.1.0/24 は、ほとんどのホームルーターでデフォルトとして使われているため、意図的に選んだ 10.0.0.0/8 内の範囲を使用してください。同じ衝突は、手動で設定する通常の WireGuard VPN でも発生します。理由は同じで、より具体的なローカル経路が優先され、トラフィックがトンネルに入らなくなるためです。
DNS がどのルートにも含まれないアドレスへ解決される場合
この問題は、エラーを報告する箇所がないため、デバッグが困難です。名前解決は成功しますが、接続はタイムアウトします。
db.internal.example.com がプライベートネームサーバーを介して 10.0.5.20 に解決され、10.0.0.0/24 を広告したとします。DNS(domain name system)の名前解決と IP ルーティングは別々の処理であり、相互に検証しないため、名前解決は成功します。その後、10.0.5.20 宛てのパケットに tailnet 上の一致するルートがないため、クライアントのデフォルトゲートウェイから送信され、到達しなくなります。
次の 2 つのコマンドで、2 つの処理を切り分けられます。
nslookup db.internal.example.com
ip route get 10.0.5.20名前解決でアドレスが返る一方、ip route get が dev tailscale0 を返さない場合、名前は正常で、ルートが不足しています。そのアドレスを含む範囲を 10.0.0.0/16 または 2 つ目の明示的なプレフィックスとして広告し、コンソールで新しいプレフィックスを承認します。
ネームサーバー自体にも、同じ問題が発生します。管理コンソールで 10.0.0.53 のようなプライベートアドレスのグローバルネームサーバーを設定する場合、そのアドレスは承認済みのルート内に存在する必要があります。そうでなければ、デバイスはリゾルバーに到達できません。到達できないリゾルバーを指定したまま、ローカル DNS サーバーを上書きするオプションを有効にすると、直前まで正常に動作していたデバイスを含め、tailnet 内のすべてのデバイスで名前解決が一斉に失われます。先にリゾルバーへのルートを広告して承認し、その後で DNS 設定を変更します。トンネル内の DNS で問題が続く場合は、WireGuard トンネルで DNS が機能しなくなる仕組みで、上位の調整層を除いた同じ仕組みを説明しています。
Source NAT とサイト間リンク
デフォルトでは、サブネットルーターが転送するすべてのパケットの送信元アドレスを、自身のプライベートアドレスに書き換えます。これが SNAT(source network address translation)です。プライベートネットワーク側を変更しなくても応答できるようにするために使用します。10.0.0.20 のデータベースは、到達方法をすでに認識している VPS に応答します。その代わり、データベースから見ると、tailnet からのすべての接続が VPS から来たように見えます。そのため、送信元ごとのファイアウォールルールやアクセスログからは、実際の接続元を把握できません。
クライアントの実際の tailnet アドレスを保持したい場合は、Linux で SNAT を無効にします。
sudo tailscale set --snat-subnet-routes=falseプライベートネットワーク内のホストには、100.64.0.0/10 への戻りの経路が必要です。100.64.0.0/10 は Tailscale がデバイスに割り当てるアドレス範囲で、サブネットルーターを経由するように指定します。この戻りの経路がないと、応答はデフォルトゲートウェイへ送られて到達しません。そのため、最初のパケットの後で接続が停止します。プライベートネットワークのゲートウェイに静的ルートを追加するか、SNAT を有効なままにします。
サイト間リンクでは、2 台のサブネットルーターが同時にこの処理を行います。それぞれが自身のネットワークを広告し、相手側のネットワークを受け入れます。
sudo tailscale up --advertise-routes=10.0.0.0/24 --snat-subnet-routes=false --accept-routesもう一方のルーターでは、そのルーター自身のアドレス範囲を指定して対応するコマンドを実行します。2 つのアドレス範囲は異なっていなければなりません。ssh と ping は正常なのに大容量転送が停止する場合、原因は MSS(maximum segment size)です。これは、TCP パケットが運べるデータの最大サイズです。トンネルのオーバーヘッドによって、転送されたパケットが経路上の一部のリンクで大きすぎることがあります。MSS のクランプでこの問題を解決できます。
sudo iptables -t mangle -A FORWARD -o tailscale0 -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtuiptables-persistent でこのルールを保存してください。保存しないと、次回のブート時に消えます。
運用を継続するための保守
2026 年 8 月時点では、Node key はデフォルトで 180 日後に期限切れになります。サブネットルーターの key が期限切れになると、ノードはサインアウトし、広告していた範囲全体に到達できなくなります。どこにも設定変更がないため、原因を説明できない状態で発生します。管理コンソールの Machines ページで、このマシンの key の期限切れを無効にしてください。無効にしたことも記録しておきます。
Tailscale はピア間の直接接続を優先し、直接接続できない場合はリレーサーバーにフォールバックします。リレーは機能しますが、遅延が増えます。パブリックアドレスを持つ VPS では、受信 UDP 41641 を許可すれば、多くのピアが直接接続できます。ファイアウォールを ufw で管理している場合は、VPS に実際に必要な ufw ルールで構文を確認できます。
アクセスルールも重要です。デフォルトの tailnet では、自分のすべてのデバイスから他のすべてのデバイスへ接続できるため、承認したルートはそのまま機能します。ACL ポリシーを作成すると、ルールの宛先側でプライベート範囲を指定する必要があります。10.0.0.20 は tailnet アドレスではなく、tailnet IP やタグを対象にしたルールでは適用されないためです。
最後に、自分で運用しない coordination server を利用するか決めます。Tailscale の control plane はホステッドサービスです。key は自分のマシンに残りますが、アカウントとポリシーファイルはそこに保管されます。control plane が侵害された場合や identity login が盗まれた場合に、実際に何をされる可能性があるかを、プライベートネットワークへのルートを許可する前に確認しておくことが重要です。Tailscale の trust modelでは、その境界が示されています。コストが移行の主な理由になることはあまりありません。無料プランでは最大 6 ユーザーが、それぞれ無制限のデバイスを利用できるためです。ただし、タグの下で起動した subnet router は、自分としてサインインしたものとは別の扱いになります。その上限を超えると、料金はマシン数ではなくユーザー数に応じて増えるため、上限を超えるアカウントを追加する前に、無料プラン終了後に家庭や 5 人のチームで実際にかかる費用を確認しておくとよいでしょう。Headscale、self-hosted の Tailscale control serverを実行すれば、これを自分の VPS で管理できますが、保守が必要になります。同じ懸念への別の答えは、Tailscale のクライアントも使わないことです。NetBird VPN server を self-host する方法なら、coordination layer と独自の mesh clients を自分で管理する 1 台のマシンに配置できます。この方式と手書きの設定のどちらにするかまだ決めていない場合は、WireGuard と Tailscale の比較で、coordination layer が提供する機能とそのコストを確認できます。
FAQ
サブネットルーターと exit node の違いは何ですか?
サブネットルーターはプライベートアドレスの範囲を広告するため、tailnet 内のデバイスから Tailscale を実行していないマシンへ接続できます。exit node はインターネット全体への経路として自身を広告するため、デバイスはすべてのトラフィックをそのノードのパブリックアドレス経由で送信します。1 台の VPS を両方として使用できます。これらは --advertise-routes と --advertise-exit-node という別々のフラグで、それぞれ admin console で個別に承認する必要があります。
Linux クライアントが広告されたサブネット経路を無視するのはなぜですか?
Linux クライアントは、明示的に指定しない限りサブネット経路を受け入れません。クライアントで sudo tailscale set --accept-routes を実行します。次に、ip route show ではなく ip route show table 52 で確認します。Tailscale は受け入れた経路を routing table 52 にインストールし、policy rule 経由でその経路へ到達します。そのため、main table には経路が表示されず、動作している経路が存在しないように見えます。
再起動後にサブネットが機能しなくなりました。何が壊れたのですか?
最も可能性が高いのは IP forwarding です。sysctl -w で設定した値は再起動後も維持されないため、/etc/sysctl.d/99-tailscale.conf に書き込み、sysctl net.ipv4.ip_forward で確認します。forwarding が有効で、範囲にも到達できない場合は、admin console でノードを確認します。node key はデフォルトで 180 日後に期限切れになります。期限切れのサブネットルーターは、アカウントの問題ではなくネットワーク障害のように見えます。
2 台のサブネットルーターで同じ範囲を広告できますか?
完全に同一の範囲は広告できません。プレフィックス長が異なる重複範囲は問題なく、最も具体的な経路が優先されます。フェイルオーバーには注意が必要です。より具体的なプレフィックスを保持するルーターがオフラインになると、Tailscale はより広い経路へフォールバックしないため、そのトラフィックは停止します。実際の待機系を構成する場合は、両方のルーターで同じ具体的なプレフィックスを広告します。
ホスト名は名前解決できますが、接続がタイムアウトします。なぜですか?
DNS 名前解決と routing は別の手順です。名前が、承認済みの経路でカバーされていないアドレスに解決される場合があります。その場合、パケットはクライアントのデフォルトゲートウェイから送信されます。クライアントで ip route get <address> を実行します。結果に dev tailscale0 が含まれていない場合は、そのアドレスをカバーする範囲を広告し、admin console で新しいプレフィックスを承認します。