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 を広告すると、ノート PC からそれらのプライベートアドレスへ直接アクセスできます。セグメント上の他の構成を変更する必要はなく、データベースにも引き続きパブリックアドレスはありません。
もう 1 つは、VPS の向こう側にネットワークがあるケースです。独自のルーター配下にある自宅またはオフィスの LAN(local area network)、あるいは Tailscale を実行できない機器群が該当します。たとえば、管理対象スイッチや、ファームウェアが固定されている古い NAS です。そのネットワーク上の 1 台の Linux ホストを、ネットワーク上の他の機器に代わるサブネットルーターにします。
どちらのケースにも共通する要件があります。サブネットルーターは、広告する範囲へ、自身のルーティングテーブルとファイアウォールを使って、あらかじめ到達できなければなりません。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 から見ると同じに見えます。そのため、ほかの調査を始める前にコンソールを確認してください。
ポリシーファイルに 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 は、受け入れたルートをメインのルーティングテーブルには追加しません。ルーティングテーブル 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つの重複は、クライアントに近い場所で発生します。192.168.1.0/24 のホテルネットワーク上で、サブネットルーターが 192.168.1.0/24 を広告していると、同じ宛先をめぐって両者が競合します。どちらが選択されるかは、プラットフォームによって異なります。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 がどの経路にも含まれないアドレスを解決する
この問題は、エラーを報告するものがないため、デバッグが難しい場合があります。名前解決は成功しますが、接続はタイムアウトします。
プライベート nameserver を通じて 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 つ目の明示的な prefix のいずれかで、そのアドレスを含む範囲を広告し、新しい prefix をコンソールで承認します。
nameserver 自体にも、対応する落とし穴があります。管理コンソールで 10.0.0.53 のようなプライベートアドレスをグローバル nameserver に設定する場合、そのアドレスは承認済みの経路内に存在する必要があります。そうでなければ、デバイスは resolver にまったく到達できません。到達できない resolver を指定したまま、ローカル DNS サーバーを上書きするオプションを有効にすると、直前まで動作していたデバイスも含め、tailnet 内のすべてのデバイスで名前解決が一斉に失敗します。まず resolver への経路を広告して承認し、その後で DNS 設定を変更してください。トンネル内の DNS で繰り返し問題が発生する場合は、WireGuard トンネルで DNS が壊れる仕組みで、上位の調整レイヤーを除いた同じ仕組みを説明しています。
送信元 NAT とサイト間リンク
デフォルトでは、サブネットルーターが転送するすべてのパケットの送信元アドレスを、自身のプライベートアドレスに書き換えます。これは SNAT(送信元ネットワークアドレス変換)です。プライベートネットワーク側を変更しなくても応答を返せるようにするために使用します。10.0.0.20 のデータベースは、すでに到達方法を認識している VPS に応答します。その代わり、データベースからはすべての tailnet 接続が VPS からの接続に見えます。そのため、送信元ごとのファイアウォールルールやアクセスログからは、接続元を判別できません。
クライアントの実際の tailnet アドレスを保持したい場合は、Linux で SNAT を無効にします。
sudo tailscale set --snat-subnet-routes=falseこの場合、プライベートネットワーク内のホストには、Tailscale がデバイスに割り当てる範囲である 100.64.0.0/10 への戻り経路が必要です。この経路はサブネットルーターを経由するように指定します。戻り経路がないと、応答はデフォルトゲートウェイへ送られて到達しません。そのため、接続は最初のパケットの後で停止します。プライベートネットワークのゲートウェイに静的ルートを追加するか、SNAT を有効にしたままにしてください。
サイト間リンクでは、2 台のサブネットルーターが同時にこの処理を行います。それぞれが自身のネットワークを広告し、相手側のネットワークを受け入れます。
sudo tailscale up --advertise-routes=10.0.0.0/24 --snat-subnet-routes=false --accept-routesもう一方のルーターでは、そのルーター自身の範囲を指定して対応するコマンドを実行します。2 つの範囲は異なる必要があります。ssh と ping は正常なのに大容量転送が停止する場合、原因は MSS(最大セグメントサイズ)です。MSS は、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 が期限切れになると、その node はサインアウトされ、広告していた範囲全体に到達できなくなります。どこにも設定変更がないため、原因を説明できないまま発生することもあります。管理コンソールの Machines ページで、このマシンの key の有効期限を無効にしてください。無効にしたことも記録しておきます。
Tailscale は、peer 間の直接接続を優先します。直接接続できない場合は、relay server にフォールバックします。relay は機能しますが、遅延が増加します。公開アドレスを持つ VPS では、受信 UDP 41641 を許可すれば、多くの peer が直接接続できます。ファイアウォールを ufw で管理している場合は、VPS に実際に必要な ufw ルールで構文を確認できます。
アクセスルールも重要です。デフォルトの tailnet では、自分のすべてのデバイスから他のデバイスへ接続できます。そのため、承認した route はそのまま機能します。ACL policy を作成すると、ルールの宛先側で private range を指定する必要があります。10.0.0.20 は tailnet address ではないため、tailnet IP または tag を対象にしたルールでは許可されません。
最後に、自分で運用しない coordination server を使用するか決めます。Tailscale の control plane は hosted service です。key は自分のマシンに残りますが、account と policy file は hosted service 側に保存されます。self hosted Tailscale control server である Headscaleを使用すると、これを自分の VPS で運用できます。ただし、保守が必要です。同じ懸念への別の対応として、Tailscale の client も自分で運用する方法があります。NetBird VPN server を self-hosting する方法では、coordination layer と独自の mesh client を、自分で管理する 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 rulesを通じてその経路へ到達します。そのため、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解決とルーティングは別の処理です。名前が、承認済みの経路でカバーされていないアドレスへ解決される場合があります。その場合、パケットはクライアントのデフォルトゲートウェイから送信されます。クライアントで ip route get <address> を実行してください。結果に dev tailscale0 が含まれていない場合は、そのアドレスをカバーする範囲を広告し、admin consoleで新しいプレフィックスを承認してください。