デフォルトのTailscaleが抱える問題
Tailscaleは非常に便利だ——インストールしてログインするだけで、ファイアウォールを一切触らずにデバイス同士が繋がる。しかししばらく本番環境で使っていると、気になる点が見えてきた。コントロールプレーン全体がTailscale Inc.のサーバー上に存在しているのだ。デバイス情報、IPアドレス、ルーティングポリシーのすべてが、自分でコントロールできない第三者に依存している。
個人利用なら問題ない。だがGDPR、PDPA、または社内セキュリティポリシーなどのデータレジデンシー要件に従う必要がある企業にとって、これは過剰な心配ではなく、真剣に対処すべき問題だ。さらにフリープランは100台までという制限がある——多く聞こえるが、IoTやコンテナで規模を拡大するとあっという間に上限に達する。
そこでHeadscaleに移行した。TailscaleコントロールプレーンのオープンソースなImplementationで、自分のVPS上でセルフホストできる。
Headscaleとは何か、どう動くのか
Tailscaleはスプリットアーキテクチャモデルで動作しており、完全に分離した2つの部分で構成されている。
- コントロールプレーン: デバイスリストの管理、キーの配布、認証、ACLポリシーを担当する。デフォルトでは
controlplane.tailscale.com上に存在する。 - データプレーン: 実際のトラフィックはWireGuardを使ってピア間で直接やり取りされる(P2P)。コントロールプレーンはデータに触れない。
Headscaleはそのコントロールプレーン部分を置き換える。自分のサーバー上でホストし、各デバイス上のTailscaleクライアントはそのまま使える——Tailscaleのサーバーの代わりにHeadscaleサーバーを指すだけでいい。
より具体的に言うと、実際のトラフィックはHeadscaleを経由しない。データはWireGuardを通じてP2Pで直接やり取りされる——HeadscaleはあくまでBrokerとして機能し、公開鍵の配布、ノードリストの管理、認証処理を担う。処理が軽量なため、CPU 1コア・RAM 1GBのVPSで数十台のノードを余裕で捌ける。CPU使用率は通常5%以下だ。
VPS上にHeadscaleをインストールする
動作環境の要件
- Ubuntu 22.04またはDebian 12が動作するVPS(筆者はUbuntu 22.04を使用)
- VPSのIPに向けたドメイン(例:
hs.example.com) - ファイアウォールで443番と80番ポートを開放
- リバースプロキシとしてNginxを使用(推奨)
ステップ1: Headscaleのダウンロードとインストール
# 最新バイナリをダウンロード(GitHubリリースでバージョンを確認すること)
wget https://github.com/juanfont/headscale/releases/download/v0.23.0/headscale_0.23.0_linux_amd64.deb
# インストール
sudo dpkg -i headscale_0.23.0_linux_amd64.deb
# バージョン確認
headscale version
ステップ2: Headscaleの設定
設定ファイルは/etc/headscale/config.yamlにある。開いて重要な項目を編集する:
server_url: https://hs.example.com
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 127.0.0.1:9090
grpc_listen_addr: 0.0.0.0:50443
grpc_allow_insecure: false
private_key_path: /var/lib/headscale/private.key
noise:
private_key_path: /var/lib/headscale/noise_private.key
ip_prefixes:
- 100.64.0.0/10
derp:
server:
enabled: false # TailscaleのDERPサーバーを使用(またはセルフホスト)
urls:
- https://controlplane.tailscale.com/derpmap/default
dns_config:
nameservers:
- 1.1.1.1
domains: []
magic_dns: true
base_domain: mesh.example.com
db_type: sqlite3
db_path: /var/lib/headscale/db.sqlite
log:
level: info
acme_url: https://acme-v02.api.letsencrypt.org/directory
acme_email: [email protected]
tls_letsencrypt_hostname: ""
tls_letsencrypt_cache_dir: /var/lib/headscale/cache
tls_letsencrypt_challenge_type: HTTP-01
server_urlには特に注意が必要だ——使用するドメインと完全に一致していなければならない。ここが間違っているとクライアントが接続できず、明確なエラーメッセージも出ないため、半日デバッグする羽目になる。
ステップ3: NginxリバースプロキシYAMLの設定
sudo apt install nginx certbot python3-certbot-nginx -y
# SSL証明書を取得
sudo certbot --nginx -d hs.example.com
/etc/nginx/sites-available/headscaleにNginx設定を作成する:
server {
listen 80;
server_name hs.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name hs.example.com;
ssl_certificate /etc/letsencrypt/live/hs.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/hs.example.com/privkey.pem;
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
}
}
sudo ln -s /etc/nginx/sites-available/headscale /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
ステップ4: Headscaleの起動
sudo systemctl enable headscale
sudo systemctl start headscale
sudo systemctl status headscale
Headscaleネットワークにデバイスを追加する
ユーザー(ネームスペース)の作成
Headscaleはデバイスをグループ化するために「ユーザー」という概念を使う:
# 新しいユーザーを作成
headscale users create myteam
# ユーザー一覧を表示
headscale users list
Linux/macOSデバイスの登録
接続したいマシン上で(通常通りTailscaleクライアントをインストール済みの状態で):
# TailscaleサーバーではなくHeadscaleサーバーを指定
tailscale up --login-server https://hs.example.com
このコマンドを実行すると認証用のURLが表示される。Headscaleサーバー側で次のコマンドで承認する:
# 承認待ちのノード一覧を取得
headscale nodes list --user myteam
# ノードを承認(NODE_KEYは上記の出力で確認したキーに置き換える)
headscale nodes register --user myteam --key NODE_KEY
数台程度なら手動承認でも問題ないが、大量デプロイ時はAuth Keyを使うと格段に楽になる:
# 再利用可能なAuth Keyを作成
headscale preauthkeys create --user myteam --reusable --expiration 24h
# クライアント側でAuth Keyを使用
tailscale up --login-server https://hs.example.com --authkey tskey-auth-XXXX
接続確認
# Headscaleサーバー側で確認
headscale nodes list
# 出力例:
# ID | Hostname | Name | MachineKey | NodeKey | User | IP addresses | Ephemeral | Last seen
# 1 | web-server | web-server | ... | ... | myteam | 100.64.0.1 | false | 2026-04-25 10:30
# 2 | dev-laptop | dev-laptop | ... | ... | myteam | 100.64.0.2 | false | 2026-04-25 10:31
# クライアント側で接続確認
tailscale status
tailscale ping 100.64.0.1
実際のデバッグ体験談
本番運用から約3ヶ月後、厄介な問題に遭遇した。ピーク時間帯——午前9〜11時と午後14〜16時——にのみ断続的なパケットロスが発生するのだ。Tailscaleのステータス上は接続が「up」のままなのに、レイテンシが5msから200ms以上に跳ね上がり、パケットがドロップする。
最初はISPのネットワーク問題かと思った。しかしtailscale ping --verboseで確認すると、トラフィックがダイレクト(P2P)ではなくDERPサーバー経由でリレーされていることが判明した。NATトラバーサルが失敗し、2つのピアがお互いにパンチスルーできていなかったのだ。
解決策は、地理的に近い場所にDERPサーバーをセルフホストで追加し、可能な場合はダイレクト接続を強制するACLを設定することだった。それ以降は安定して動作している。
得た教訓: Tailscale/Headscaleは見た目はシンプルだが、内部ではWireGuard + DERP + NATトラバーサルが動いている。問題が発生した際はどのレイヤーで障害が起きているかを特定する必要があり、tailscale statusを見るだけでは不十分だ。
ACLポリシーの管理
HeadscaleはHuJSON形式のポリシー(Tailscale ACLと同様)をサポートしている。/etc/headscale/acl.yamlを作成する:
acls:
# 開発チームは全てにアクセス可能
- action: accept
src: ["myteam:*"]
dst: ["myteam:*:*"]
# 本番サーバーは特定のノードからの接続のみ受け付ける
- action: accept
src: ["myteam:dev-laptop"]
dst: ["myteam:prod-server:22,80,443"]
# ポリシーを適用
headscale policy set --path /etc/headscale/acl.yaml
まとめ
約30台のノードでHeadscaleを6ヶ月間本番運用した率直な感想: 安定して動作しており、信頼性も高い。Tailscaleに慣れていてセルフホストが必要な場合——プライバシー、コンプライアンス、あるいは単に100台制限を回避したい場合——Headscaleは現時点で最も成熟した選択肢だ。
Headscaleの最大のメリット: 各ノードのTailscaleクライアントは何も変更不要で、--login-serverを追加するだけでいい。Tailscale SSH証明書や一部の高度なACLタグなど、Tailscaleクラウドの機能の一部はまだ移植されていない。しかしサーバー接続、開発チーム、ホームラボといった用途であれば、機能的に不足を感じることは全くない。
実際の運用コスト: 30ノードのHeadscaleを動かすVPSは月$5——Tailscale Personal Proの月$6/ユーザーと比べると、小規模チームなら1〜2ヶ月でペイできる。さらにデータ主権という、金銭では計れないメリットもある。

