背景&なぜHeadscaleをセルフホストするのか?
AWSやHetzner、自宅のホームラボなどに分散したサーバーの管理は、多くのエンジニアにとって悩みの種です。SSHのポート22を開放してIPホワイトリストで制限する運用は、カフェなどの出先から作業する際に非常に不便です。また、従来のOpenVPNを構築する手法にも大きなデメリットがあります。すべてのトラフィックが中央サーバーを経由するため、VPSがボトルネックとなり、Ping(レイテンシ)が大幅に悪化してしまいます。
Tailscaleは、WireGuardをベースにしたMesh VPNアーキテクチャにより、この課題をスマートに解決します。ゲートウェイを中継する代わりに、2台のマシンが直接P2Pで接続されます。しかし、Tailscaleの無料SaaSプランには制限もあります。ユーザー数が3人に制限されていること、サードパーティのコントロールプレーンに依存すること、そして社内データの厳格なガバナンスが求められるプロジェクトには適さない点です。
そこで登場するのがHeadscaleです。これはTailscaleの協調サーバー(coordination server)をオープンソースで再実装したプロジェクトです。専用のVPS上で調整サーバー全体を完全にセルフホスト・管理できます。ルーティングテーブル、ACL、ノード一覧の管理権限はすべて手元に維持できます。その一方で、クライアント端末側はApp Store、Google Play、Linuxリポジトリから提供される公式のTailscaleアプリをそのまま利用可能です。
LinuxサーバーへのHeadscaleのインストール
1 vCPU・1GB RAMを搭載した安価なVPS(Ubuntu 22.04/24.04またはDebian 12)で十分動作します。100ノード未満のホームラボ規模であれば、Headscaleデーモンの実際のメモリ消費量はわずか30MB〜50MB程度です。事前に、サーバーの固定IPにAレコードを向けたサブドメイン(例:vpn.itfromzero.com)を用意しておいてください。
ステップ1:バイナリファイルのダウンロードとインストール
プロジェクトのGitHub Releasesページにアクセスし、amd64アーキテクチャ向けの最新debパッケージを取得します。
export HEADSCALE_VERSION="0.22.3"
wget --output-document=headscale.deb \
https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_amd64.deb
sudo dpkg -i headscale.deb
このパッケージにより、システムユーザーheadscaleが自動作成され、/var/lib/headscaleのデータディレクトリに対する権限設定と、/etc/headscale/config.yamlへのサンプル設定ファイルの配置が行われます。
ステップ2:NginxとLet’s Encryptによるリバースプロキシの構築
HeadscaleにはACMEを介してSSL証明書を自動取得する機能が備わっています。しかし、前面にNginxを配置する構成が最も安全かつ柔軟です。Nginxを挟むことで、証明書の一元管理、スムーズなWebSocket処理が可能になり、同一VPS上で他のWebサービスと共存させる際のルーティングも容易になります。
sudo apt update && sudo apt install -y nginx certbot python3-certbot-nginx
Headscale用の新しいバーチャルホストファイルを作成します。
sudo nano /etc/nginx/sites-available/headscale
以下の設定内容を貼り付けます。
server {
listen 80;
server_name vpn.itfromzero.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name vpn.itfromzero.com;
ssl_certificate /etc/letsencrypt/live/vpn.itfromzero.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/vpn.itfromzero.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $server_name;
proxy_redirect http:// https://;
proxy_buffering off;
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;
}
}
/etc/nginx/nginx.confを開き、http {}ブロック内にWebSocket用の設定ブロックが定義されていることを確認します。
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
バーチャルホストを有効化し、Certbotを実行してSSL証明書を発行後、サービスをリロードします。
sudo ln -s /etc/nginx/sites-available/headscale /etc/nginx/sites-enabled/
sudo certbot --nginx -d vpn.itfromzero.com
sudo nginx -t && sudo systemctl reload nginx
Headscaleの詳細設定
config.yamlファイルの設定
Headscaleのメイン設定ファイルを開き、接続パラメータを調整します。
sudo nano /etc/headscale/config.yaml
以下の重要項目を確認し、環境に合わせて修正します。
server_url: https://vpn.itfromzero.com:443
listen_addr: 127.0.0.1:8080
metrics_listen_addr: 127.0.0.1:9090
ip_prefixes:
- 100.64.0.0/10
- fd7a:115c:a1e0::/48
derp:
server:
enabled: false
urls:
- https://controlplane.tailscale.com/derpmap/default
paths: []
auto_update_enabled: true
update_frequency: 24h
database:
type: sqlite3
sqlite:
path: /var/lib/headscale/db.sqlite
systemd経由でサービスを有効化し、起動します。
sudo systemctl enable --now headscale
sudo systemctl status headscale
管理者ユーザーの作成
Headscaleでは、デバイスをユーザー(User、バージョン0.23未満ではNamespace)ごとにグループ分けして管理します。社内チーム用の最初のユーザーを作成しましょう。
sudo headscale users create admin-team
クライアントをHeadscaleサーバーに登録する
クライアントマシン(別のUbuntuサーバーや個人のノートPCなど)で、公式のTailscaleエージェントをインストールします。
curl -fsSL https://tailscale.com/install.sh | sh
# セルフホストしたコントロールサーバーに接続
sudo tailscale up --login-server https://vpn.itfromzero.com
ターミナルに以下のような認証リンクが表示されます。
To authenticate, visit:
https://vpn.itfromzero.com/register/nodekey:xxxxxxxxxxxxxxxxxxxxxx
プレフィックスnodekey:(クライアントのバージョンによってはmkey:)に続くキー文字列をコピーし、Headscaleサーバーに戻って承認コマンドを実行します。
sudo headscale nodes register --user admin-team --key nodekey:xxxxxxxxxxxxxxxxxxxxxx
AnsibleやDockerのentrypointなどを用いて自動ブートストラップを行いたい場合は、有効期限付きの使い捨てPre-auth keyを事前生成しておきます。
# 24時間有効な使い捨てキーを生成
sudo headscale preauthkeys create --user admin-team --expiration 24h
これにより、クライアント側では以下のコマンドを1回実行するだけで、即座にネットワークに参加できます。
sudo tailscale up --login-server https://vpn.itfromzero.com --authkey <YOUR_PREAUTH_KEY>
VPNネットワークの動作確認と監視
接続確認とMeshパフォーマンスの検証
Headscaleサーバー上で、現在オンラインになっているデバイス一覧を確認します。
sudo headscale nodes list
結果一覧には、Node ID、Hostname、所属ユーザー、CGNATアドレス(100.64.0.0/10の範囲)、およびオンライン状態が表示されます。
次に、クライアントマシンから接続品質をテストします。
# ネットワーク内のピアと対応するIPアドレスを一覧表示
tailscale status
# 直接ルーティングの確認(Ping)
tailscale ping 100.64.0.2
pong from ... via [public-ip]:port in 14ms (direct)という応答が返ってくれば、2台のマシンがWireGuardを介してP2Pハンドシェイクに成功したことを示しています。この状態では、ISP回線の理論上限に近い帯域幅が得られます。一方、via DERP(syd)と表示される場合は、両端が厳しいNAT(Symmetric NAT)配下にあるためリレー接続となっており、Pingが80〜150ms程度まで増加します。
Prometheusによるメトリクスの収集
Headscaleには、ポート9090でPrometheusメトリクスエンドポイントが組み込まれています。localhostからcurlコマンドを実行して動作確認できます。
curl -s http://127.0.0.1:9090/metrics | grep -E '^headscale_'
特に注目すべきメトリクスはheadscale_nodes_online_countとheadscale_sqlite_queries_totalです。150ノード未満のホームラボ規模であれば、デフォルトのSQLiteファイルで極めて軽快に処理できます。200ノードを超え、頻繁なルート更新が継続的に発生する規模に拡張する場合は、データベースロックを防ぐためにdatabase.typeをpostgresに切り替えることをおすすめします。

