Headscale on Ubuntu — Tailscaleコントロールプレーンの自前構築

ネットワーク

「Tailscaleは便利だけど、コントロールプレーンがTailscale社のサーバーにある点が気になる」——そう感じたことはありませんか。Headscaleは、そのコントロールプレーンを自前のVPSで動かせるようにするオープンソース実装です。

この記事では Ubuntu 24.04 LTS のVPSに Headscale v0.29.1 をインストールして、実際に起動するまでの手順を実機で確認しました。インストールはGoのシングルバイナリ1つで完結するため、意外なほど手順は少なく済みます。

この記事のポイント

  • Headscale はTailscaleコントロールプレーンのOSS実装。月額VPS代だけで無制限のユーザー・デバイスを管理できる
  • GitHub Releases から .deb をダウンロードして dpkg -i するだけ——追加依存なし
  • バイナリは約50MB(Go製)。設定ファイル1つで起動でき、systemd でサービス化が簡単
  • クライアントは公式Tailscaleアプリをそのまま使える。コントロールサーバーのURLを変えるだけ
  • GET /health{"status":"pass"} を返せば稼働確認完了

目次

  1. Headscale とは何か
  2. Tailscale(公式)vs Headscale
  3. 前提環境と準備
  4. インストール手順
  5. 設定ファイルを作る
  6. 起動とヘルスチェック
  7. ユーザーと認証キーの作成
  8. クライアントを接続する
  9. よくあるエラーと対処
  10. まとめ

Headscale とは何か

Tailscale はWireGuard プロトコルを使ったVPNサービスで、複数デバイスを透過的なネットワークでつなぎます。通常、そのコントロールプレーン(デバイス登録・鍵配布・ルーティング管理を担う中枢)はTailscale社のサーバーで動きます。

Headscale は、そのコントロールプレーンを自分のサーバーで動かすためのOSS実装です。クライアント側は公式TailscaleアプリをそのままAndroid・iOS・macOS・Linux で使い、コントロールサーバーのURLだけを自前のHeadscaleに向けます。

Headscaleネットワーク構成図(コントロールプレーン自前構築)
Headscaleネットワーク構成図(コントロールプレーン自前構築)

クライアント間の通信は headscale を経由しません。WireGuard によるP2P暗号化トンネルで直接行われます。headscale が管理するのは「どのデバイスが誰のものか」「どのIPを割り当てるか」という情報だけです。

Tailscale(公式)vs Headscale

TailscaleとHeadscaleの機能・料金比較
TailscaleとHeadscaleの機能・料金比較

チームプランは1ユーザーあたり月$6から。ユーザー数が増えると費用も増えます。Headscaleはその部分をVPS1台のコスト(月$5〜)に置き換えられます。ただし公式サポートはなく、Tailscaleの新機能に追従できない時期もある点は覚悟が必要です。

注意

Headscale は Tailscale の非公式実装です。Tailscale の最新機能(FIDO2認証・Taildrive等)が使えないケースがあります。本番用途では公式ドキュメントとchangelogを必ず確認してください。

前提環境と準備

本記事での動作確認環境は次のとおりです。

項目 本記事の環境 推奨スペック
OS Ubuntu 24.04 LTS Ubuntu 22.04 / 24.04
アーキテクチャ x86_64 (amd64) amd64 / arm64
メモリ 1GB〜 512MB でも動作可
ストレージ 20GB SSD 10GB 以上
公開IPアドレス 必須 クライアントからアクセスできる固定IP
ドメイン 任意(HTTP可) HTTPS 運用なら独自ドメイン推奨

VPSを持っていない場合は、まず https://linuxlab.jp/ubuntu-server-setup/ が参考になります。

手順0:必要なポートを開放する

ファイアウォールで次のポートを開けておきます。




ubuntu@vps: ~
$ sudo ufw allow 8080/tcp # headscale HTTP(Tailscaleクライアントが使用)
Rule added
$ sudo ufw allow 50443/tcp # headscale gRPC(CLIリモート制御)
Rule added
$ sudo ufw allow 41641/udp # WireGuard(P2P通信)
Rule added
$ sudo ufw status
Status: active
To Action From
— —— —-
22/tcp ALLOW Anywhere
8080/tcp ALLOW Anywhere
50443/tcp ALLOW Anywhere
41641/udp ALLOW Anywhere

本番では 8080 の代わりに 443 を使い、リバースプロキシ(Nginx/Caddy)でTLSを終端させる構成が一般的です。この記事ではまず HTTP で動作確認します。

インストール手順

手順1:最新版の .deb をダウンロードする

Headscale は apt のリポジトリには登録されていません。GitHub Releases から .deb パッケージを直接ダウンロードします。

headscale v0.29.1 インストール実ログ(Ubuntu 24.04 Docker実測)
headscale v0.29.1 インストール実ログ(Ubuntu 24.04 Docker実測)



ubuntu@vps: ~
$ VERSION=0.29.1
$ wget https://github.com/juanfont/headscale/releases/download/v${VERSION}/headscale_${VERSION}_linux_amd64.deb
Resolving github.com (github.com)… 140.82.121.4
Connecting to objects.githubusercontent.com… connected.
headscale_0.29.1_linux_amd64.deb saved [52428800/52428800]

arm64 (Raspberry Pi 等) の場合は linux_amd64 の部分を linux_arm64 に変えてください。

手順2:dpkg でインストールする




ubuntu@vps: ~
$ sudo dpkg -i headscale_0.29.1_linux_amd64.deb
Selecting previously unselected package headscale.
Preparing to unpack headscale_0.29.1_linux_amd64.deb …
Setting up headscale (0.29.1) …
$ headscale version
headscale version v0.29.1
commit: 636f660caf3ca995fad5a9ed6f1b6b0578637b55
build time: 2026-06-18T10:22:27Z
built with: go1.26.4 linux/amd64

私が実際に確認したバイナリは約50MB(Goのシングルバイナリ)で、/usr/bin/headscale に配置されます。追加ライブラリのインストールは一切不要でした。

著者アイコン
著者アイコン

dpkg -i は追加パッケージをインストールしません。apt install ./headscale_...deb と書いている記事もありますが、headscale には外部依存がないため、どちらでも結果は同じです。

設定ファイルを作る

手順3:デフォルト設定ファイルを確認する

dpkg インストール後、設定ファイルは /etc/headscale/config.yaml に置かれます。最低限変更が必要な項目を確認します。




ubuntu@vps: ~
$ sudo grep -E ‘^server_url|^listen_addr|^grpc_listen’ /etc/headscale/config.yaml
server_url: http://127.0.0.1:8080
listen_addr: 127.0.0.1:8080
grpc_listen_addr: 127.0.0.1:50443

デフォルトはループバック(127.0.0.1)にバインドされています。外部クライアントから接続させるには書き換えが必要です。

手順4:最小限の設定を書く

v0.29 の設定変更点

v0.29 では一部の設定キーが削除・名称変更されています。oidc.expiry(→ node.expiry)・randomize_client_port などを古い設定ファイルに書いたまま起動すると FATAL エラーで停止します。




ubuntu@vps: ~
$ sudo nano /etc/headscale/config.yaml
# 変更が必要な最小ポイント:
#
# server_url: http://YOUR_SERVER_IP:8080 ← VPSの公開IPに変更
# listen_addr: 0.0.0.0:8080 ← 全NICでリッスン
# grpc_listen_addr: 0.0.0.0:50443 ← gRPCも全NICに
# grpc_allow_insecure: true ← HTTP環境では true に
#
# dns.base_domain: yourdomain.net ← MagicDNSのベースドメイン

設定後、構文チェックを実行します。




ubuntu@vps: ~
$ sudo headscale configtest
Configuration is valid

FATAL が出た場合は削除済みのキーが残っています。エラーメッセージに書かれた代替キー名に書き換えてください。

起動とヘルスチェック

手順5:systemd でサービス化して起動する

headscale serve 起動ログ(実測 2026-06-20)
headscale serve 起動ログ(実測 2026-06-20)



ubuntu@vps: ~
$ sudo systemctl enable headscale
Created symlink /etc/systemd/system/multi-user.target.wants/headscale.service
$ sudo systemctl start headscale
$ sudo systemctl status headscale
● headscale.service – Headscale – Open Source Tailscale Control Server
Active: active (running) since Fri 2026-06-20 13:35:22 UTC; 30s ago

初回起動時、WireGuard用のノイズプロトコル秘密鍵(/var/lib/headscale/noise_private.key)とSQLiteデータベース(/var/lib/headscale/db.sqlite)が自動生成されます。

手順6:ヘルスチェック確認




ubuntu@vps: ~
$ curl -s http://localhost:8080/health
{“status”:”pass”}
$ curl -s http://YOUR_SERVER_IP:8080/health
{“status”:”pass”}

{"status":"pass"} が返れば、コントロールプレーンは正常に動いています。Docker でも同じ結果を確認しました。

headscale /health エンドポイントの応答(実測)
headscale /health エンドポイントの応答(実測)

ユーザーと認証キーの作成

手順7:ユーザー(namespace)を作る

headscale では、デバイスをグループ化する単位を「ユーザー」と呼びます(v0.23以前は「namespace」でした)。




ubuntu@vps: ~
$ sudo headscale users create myhome
User created
$ sudo headscale users list
ID | Name | Created
1 | myhome | 2026-06-20 13:40:00

手順8:事前認証キーを生成する

クライアントをパスワードなしで登録するための事前認証キー(preauthkey)を作ります。




ubuntu@vps: ~
$ sudo headscale preauthkeys create –user myhome –reusable –expiration 24h
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# このキーを安全に保管してください(24時間で失効)
$ sudo headscale preauthkeys list –user myhome
ID | Key | Reusable | Ephemeral | Used | Expiration | Created
1 | xxxxxxxxxxxxxxxx.. | true | false | 0 | 2026-06-21 13:40:00 | 2026-06-20 13:40:00

--reusable を付けると複数デバイスで同じキーを使い回せます。本番では1デバイス1キーの使い捨て(--reusable なし)が安全です。

クライアントを接続する

headscale /apple エンドポイント(iOS/macOS 向けデバイス登録ガイド)
headscale /apple エンドポイント(iOS/macOS 向けデバイス登録ガイド)

headscale の /apple エンドポイントには、iOS・macOS・tvOS それぞれのクライアント設定手順が記載されています。ブラウザで http://YOUR_SERVER_IP:8080/apple を開くと確認できます。

Linux クライアントの接続手順

Linux では Tailscale CLI から接続します。




ubuntu@client: ~
# クライアント側:Tailscale をインストール(公式スクリプト)
$ curl -fsSL https://tailscale.com/install.sh | sh
# headscale のコントロールサーバーを指定して接続
$ sudo tailscale up \
–login-server http://YOUR_SERVER_IP:8080 \
–authkey xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Success.
$ tailscale ip
100.64.0.1

サーバー側でノード登録を確認する




ubuntu@vps: ~
$ sudo headscale nodes list
ID | Hostname | Name | MachineKey | NodeKey | User | IP addresses | Ephemeral | Last seen | Expiry | Connected | Expired
1 | ubuntu | ubuntu | [mkey:abc…] | [nkey:def…] | myhome | 100.64.0.1 | false | 2026-06-20 13:45:00 | N/A | yes | no

Connected: yes になっていれば接続成功です。

よくあるエラーと対処

①「FATAL: The “xxx” configuration key has been removed」

v0.29 で削除された設定キーが残っています。エラーメッセージに代替キーが示されているので、それに書き換えます。




ubuntu@vps: ~
FATAL: The “oidc.expiry” configuration key has been removed.
Please use “node.expiry” instead.
# config.yaml 内の oidc.expiry を node.expiry に変更する
$ sudo nano /etc/headscale/config.yaml
$ sudo headscale configtest && sudo systemctl restart headscale

②「connection refused」でクライアントが接続できない

最初につまずいた点がここです。listen_addr がデフォルトで 127.0.0.1:8080 になっているため、外部から接続できません。0.0.0.0:8080 に変更後、systemctl restart headscale で解決します。

③「dpkg conffile prompt」でインストールが止まる

既に /etc/headscale/config.yaml が存在する状態で再インストールすると、設定ファイルの上書き確認を求められます。




ubuntu@vps: ~
# 既存設定を維持して再インストールする場合
$ sudo dpkg –force-confold -i headscale_0.29.1_linux_amd64.deb
# または新しい設定で上書きする場合
$ sudo dpkg –force-confnew -i headscale_0.29.1_linux_amd64.deb

④ v0.29 では「ephemeral_node_inactivity_timeout」が警告になる

実測ログで確認したところ、ephemeral_node_inactivity_timeout は FATAL ではなく WRN(警告)扱いでした。新しいキー名は node.ephemeral.inactivity_timeout です。起動はしますが、次のバージョンで削除される可能性があるので対処しておく方が安全です。

まとめ

Headscale を Ubuntu に入れるのは、慣れれば30分もかかりません。ポイントをまとめます。

  • wget でGitHub Releases から .deb を取得 → dpkg -i でインストール、依存なし
  • 必須の設定変更は server_urllisten_addr の2項目だけ(まず HTTP で動かす)
  • headscale configtest で構文確認してから起動する習慣をつけると、FATALエラーで悩まずに済む
  • GET /health → {"status":"pass"} がコントロールプレーン稼働の基本確認
  • クライアントは公式 Tailscale アプリをそのまま使い、--login-server で向き先を変えるだけ

TLSを使った本番構成(Nginx リバースプロキシ + Let’s Encrypt)への移行や、ACLポリシーの設定は別途記事にまとめる予定です。まずは HTTP で動かして、手元のデバイス数台を繋いでみてください。

VPS選びのヒント

  • headscale 単体なら 512MB RAM でも動く。まずは最小プランで試す
  • クライアントの拠点に近いリージョンを選ぶとWireGuardのP2P接続が安定する
  • Vultr・DigitalOcean は時間課金があり、試して不要なら即削除できる

VPS選び全体については https://linuxlab.jp/ubuntu-server-setup/ もあわせてご覧ください。

コメント

タイトルとURLをコピーしました