SSD Nodes Learn Hosting plans →
Посібники Matt ConnorВід Matt Connor · Оновлено 2026-09-28

Headscale: власний сервер керування Tailscale

Запустіть власний сервер керування Tailscale на VPS: встановіть headscale з офіційного .deb, задайте server_url, перезапустіть сервіс і підключіть перший вузол.

Verified Every command ran end-to-end on a fresh Ubuntu 24.04 server, July 30, 2026.

Що таке headscale

Headscale — це self-hosted реалізація сервера керування Tailscale, тому машиною, яка координує вашу приватну мережу, є VPS у вашому користуванні. Це community-проєкт, яким не керує Tailscale Inc. На кожній машині й далі працює офіційний клієнт tailscale. Щоб указати ваш сервер, достатньо передати один прапорець — --login-server.

Сервер керування визначає, хто належить до мережі. Він надає кожному вузлу адресу з діапазону 100.64.0.0/10, поширює публічні ключі та повідомляє вузлам, де знаходити один одного. Тунелі залишаються WireGuard і створюються безпосередньо між вузлами. Трафік між двома вашими машинами не проходить через сервер headscale, якщо неможливо побудувати прямий шлях і вузли не переходять на relay. Самостійне виконання цієї ролі змінює того, хто нею керує, але не змінює її можливостей. Тому перед тим, як сприймати такий перехід як самостійне підвищення безпеки, варто зрозуміти до яких ресурсів сервер керування може і не може отримати доступ у цій моделі.

Один екземпляр headscale обслуговує один tailnet (одну мережу Tailscale). Проєкт описує таку схему як придатну для особистого використання або невеликої організації. Якщо у вас три або чотири машини, звичайний WireGuard VPN на VPS у вашому користуванні потребує менше програмного забезпечення та створює менше можливостей для збоїв. Headscale стає виправданим, коли ви більше не хочете вручну створювати блок [Peer] для кожного нового ноутбука. Часто саме вартість спонукає користувачів шукати альтернативи, тому перед орендою сервера варто ознайомитися з тим, що насправді охоплює безкоштовний тариф hosted-версії, оскільки кілька особистих машин зазвичай у нього вкладаються. Якщо ви вже перевищили це обмеження, порівняйте витрати з вартістю платних тарифів, де оплата стягується за користувача, а не за пристрій, оскільки домогосподарство з одним обліковим записом може залишатися дешевим навіть після того, як кількість пристроїв перестає мати значення. Якщо вам потрібна self-hosted control plane, але ви хочете використовувати власний клієнт і вебінтерфейс для керування peer-вузлами, а не drop-in replacement для Tailscale, варто розглянути NetBird на одному VPS як альтернативу. Ширше порівняння цих двох моделей наведено в матеріалі чим відрізняються WireGuard і Tailscale.

Що потрібно підготувати перед встановленням

  • VPS під керуванням Ubuntu 24.04 із публічною IPv4-адресою та доступом через sudo. Якщо сервер новий, спочатку виконайте перші десять хвилин на новому VPS.
  • DNS A-запис, що вказує на цю адресу. У цьому посібнику використовується headscale.example.com.
  • Другий домен або піддомен для MagicDNS. У цьому посібнику використовується tailnet.example.net. Він не має збігатися з доменом у server_url.
  • Один клієнтський пристрій для підключення під керуванням Linux, macOS, Windows, Android або iOS.

Встановлення headscale з офіційного пакета .deb

Проєкт публікує пакети .deb на сторінці релізів GitHub. Станом на July 2026 поточний реліз — 0.29.3. Спочатку перевірте архітектуру, оскільки вона зазначена в імені файлу.

sudo apt update
sudo apt install -y wget
dpkg --print-architecture

На звичайному x86 VPS команда виводить amd64, а на плані в стилі Ampere або Graviton — arm64. Запишіть результат у наведену нижче змінну.

HEADSCALE_VERSION="0.29.3"
HEADSCALE_ARCH="amd64"
wget --output-document=headscale.deb \\
  "https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_${HEADSCALE_ARCH}.deb"
sudo apt install -y ./headscale.deb
headscale version

Префікс ./ перед іменем файлу обов’язковий. Без нього apt шукає у ваших репозиторіях пакет із назвою headscale.deb і завершується з помилкою.

Пакет створює системного користувача headscale, записує типовий файл /etc/headscale/config.yaml, встановлює unit systemd, а потім вмикає та запускає сервіс. Тому headscale вже працює, коли apt повертає результат, а sudo systemctl is-active headscale виводить active. Однак сервіс працює з конфігурацією, що постачається разом із пакетом. Вона спрямовує server_url до http://127.0.0.1:8080, а це адреса, до якої жоден ваш клієнт не зможе підключитися. Поки що нічого не відкрито назовні, оскільки типовий listen_addr також прив’язаний до loopback. Проте не підключайте жодного клієнта, доки не встановите server_url і не перезапустите сервіс. Це буде зроблено на наступних двох кроках.

Налаштуйте server_url до підключення будь-якого клієнта

Відредагуйте /etc/headscale/config.yaml за допомогою sudo nano /etc/headscale/config.yaml або внесіть ті самі три зміни за допомогою sed. Збережіть копію оригіналу, оскільки файл довгий і містить багато коментарів. Це найкраще джерело довідкової інформації для решти параметрів.

sudo cp /etc/headscale/config.yaml /etc/headscale/config.yaml.orig
sudo sed -i 's|^server_url:.*|server_url: https://headscale.example.com|' /etc/headscale/config.yaml
sudo sed -i 's|^listen_addr:.*|listen_addr: 127.0.0.1:8080|' /etc/headscale/config.yaml
sudo sed -i 's|^  base_domain:.*|  base_domain: tailnet.example.net|' /etc/headscale/config.yaml
sudo grep -E '^(server_url|listen_addr):|^  base_domain:' /etc/headscale/config.yaml

server_url — це адреса, яку headscale записує під час реєстрації кожного клієнта. Після цього клієнти назавжди підключаються саме до цього рядка. Тому тут має бути публічне ім’я з https:// на початку, а не 127.0.0.1.

listen_addr — це адреса, на якій процес приймає підключення. Залиште її на loopback. Reverse proxy на тому самому сервері завершує TLS (transport layer security) і пересилає запити до цього процесу. Тому зовнішнім клієнтам не потрібно мати доступ до порту 8080.

base_domain — це суфікс MagicDNS, тобто домен, у межах якого вузли отримують імена. Це має бути повністю визначене доменне ім’я без крапки в кінці. Воно також має відрізнятися від домену в server_url, інакше два простори імен конфліктуватимуть.

Не змінюйте розділ бази даних. Типово використовується SQLite у /var/lib/headscale/db.sqlite, у каталозі, який створив пакет і власником якого він є. SQLite достатньо для tailnet такого розміру.

Перезапустіть headscale і переконайтеся, що він працює

sudo systemctl restart headscale
sudo systemctl is-active headscale
curl -sS -o /dev/null -w '%{http_code}\\n' http://127.0.0.1:8080/health

is-active виводить active, а curl виводить 200. Перезапуск завантажує внесені зміни. Headscale зчитує конфігурацію один раз під час запуску, тому процес, запущений пакетом, продовжує видавати наданий пакетом server_url, доки ви не перезапустите його. sudo systemctl enable --now headscale не виконує це завдання: пакет уже ввімкнув і запустив сервіс, тому ця команда нічого не змінює, а старе значення залишається активним. Автозапуск під час завантаження системи вже налаштований, а sudo systemctl is-enabled headscale виводить enabled, щоб це підтвердити.

Якщо is-active виводить щось, крім active, або curl не виводить 200, прочитайте журнал за допомогою sudo journalctl -u headscale -n 50 --no-pager. На цьому етапі причиною майже завжди є файл конфігурації, оскільки headscale аналізує весь файл до відкриття сокета. Тому неправильний відступ або невідомий ключ зупиняє процес ще до того, як він починає прослуховувати порт. Виправте файл, а потім виконайте sudo systemctl restart headscale. Після кожної наступної зміни конфігурації потрібен такий самий перезапуск. Клієнти після цього підключаються повторно автоматично. Якщо ви лише починаєте працювати з unit systemd, у матеріалі власні сервіси й таймери за допомогою systemd описано команди, використані тут.

Перевірте файли стану, поки працюєте в shell:

stat -c '%U %n' /var/lib/headscale/db.sqlite /var/lib/headscale/noise_private.key

Обидва рядки починаються з headscale — непривілейованого користувача, якого створив пакет. noise_private.key — це ідентифікатор сервера для його клієнтів. Не видаляйте його. Якщо видалити цей файл, headscale згенерує новий і кожен вузол доведеться зареєструвати повторно.

Налаштуйте TLS перед headscale

Клієнти мають підключатися до server_url через HTTPS. Caddy — найпростіший варіант, оскільки він сам запитує та поновлює сертифікат.

sudo apt install -y caddy

Замініть /etc/caddy/Caddyfile блоком із документації headscale:

headscale.example.com {
    reverse_proxy 127.0.0.1:8080 {
        header_up True-Client-IP {remote_host}
        header_up X-Real-IP {remote_host}
    }
}
sudo caddy validate --adapter caddyfile --config /etc/caddy/Caddyfile
sudo systemctl restart caddy
sudo systemctl is-active caddy

validate виводить adapted config to JSON, якщо файл має коректний синтаксис. Попередження про неформатований файл не впливає на роботу. З ноутбука команда curl -sS -o /dev/null -w '%{http_code}\\n' https://headscale.example.com/health також має вивести 200. Ця перевірка підтверджує, що DNS, firewall, сертифікат і проксі працюють разом.

Є одна важлива деталь проксі, через яку часто втрачають цілий вечір. Керувальне з’єднання Tailscale використовує HTTP upgrade, встановлюється через POST, а значення заголовка Upgrade — tailscale-control-protocol. Caddy передає його без додаткової конфігурації. nginx цього не робить, тому для фронтенду на nginx потрібна мапа upgrade:

map $http_upgrade $connection_upgrade {
    default keep-alive;
    ''      close;
}

server {
    listen 443 ssl;
    server_name headscale.example.com;
    location / {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $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_buffering off;
        proxy_pass http://127.0.0.1:8080;
    }
}

Якщо не додати ці рядки, звичайні запити все одно виконуватимуться успішно. Саме тому /health повертає 200 і все виглядає справним, тоді як довготривале керувальне з’єднання не встановлюється, а ваші вузли реєструються й одразу залишаються offline. Якщо ви оберете nginx, у матеріалі Certbot на Ubuntu 24.04 з nginx описано налаштування сертифікатів.

Які порти потрібно відкрити в UFW

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose

Через порт 443 проходить увесь трафік клієнтів. Порт 80 потрібен лише для HTTP challenge ACME (середовища автоматичного керування сертифікатами) і перенаправлення на HTTPS. Крім того, Caddy не зможе отримати сертифікат без цього порту.

Порт 8080 залишається закритим. listen_addr має значення 127.0.0.1:8080, тому proxy підключається до headscale через loopback interface, і правило firewall не потрібне. Відкриття порту 8080 для інтернету надасть клієнтам незашифрований канал керування, але не дасть жодних переваг. Пам’ятайте, що більшість провайдерів мають окремий firewall у панелі керування, незалежний від UFW. Тому порт може бути відкритим на сервері, але закритим на периферії мережі. У статті Основи firewall UFW на VPS докладніше розглянуто синтаксис правил.

Створення користувача та preauth key

sudo headscale users create alice
sudo headscale users list

Команда headscale є клієнтом. Вона взаємодіє із запущеним daemon через unix socket за адресою /var/run/headscale/headscale.sock. Цей socket має режим 0770, а його власником є група headscale. З цього випливають два наслідки. Команда завершується помилкою, якщо сервіс зупинений. Це ще одна причина дотримуватися порядку дій у цьому посібнику. Також для виконання команди потрібен sudo, якщо ви не додасте власний обліковий запис до групи headscale.

Команда users list виводить ID поруч із кожним іменем. Вам потрібен цей номер, оскільки команда для роботи з key приймає числовий user ID, а не ім’я.

sudo headscale preauthkeys create --user 1 --expiration 24h

Key виводиться лише один раз. Скопіюйте його зараз. Preauth key можна використати лише один раз, і за замовчуванням він дійсний протягом однієї години. Тому під час тестування варто налаштувати --expiration 24h. Додайте --reusable, щоб key міг зареєструвати кілька машин. Ставтеся до нього як до пароля, оскільки будь-хто, хто має цей key, може приєднатися до вашої мережі.

Підключіть першого клієнта за допомогою --login-server

На машині, яку потрібно приєднати:

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --login-server https://headscale.example.com --auth-key 'hskey-auth-PASTE-YOUR-KEY-HERE'
tailscale status
tailscale ip -4

tailscale ip -4 виводить адресу, яку призначив headscale, наприклад 100.64.0.1. Поверніться на сервер і виконайте sudo headscale nodes list, щоб побачити вузол із його ID, користувачем і статусом online.

Значення --login-server має точно збігатися зі значенням server_url, включно зі scheme і без trailing slash. Вони порівнюються як рядки. Якщо значення не збігаються, клієнт реєструється за однією адресою, а потім отримує вказівку звертатися до іншої.

Машина, на якій раніше виконували вхід до hosted-сервісу Tailscale, зберігає цей login. Спочатку виконайте на ній sudo tailscale logout, а потім tailscale up із --login-server.

Якщо не вказати --auth-key, клієнт натомість виведе URL. Відкрийте його. На сторінці буде показано ідентифікатор цієї спроби реєстрації. Підтвердьте її на сервері:

sudo headscale auth register --user alice --auth-id PASTE-THE-ID-FROM-THE-PAGE

Ця форма зручніша для власного ноутбука. Preauth keys краще підходять для сценаріїв, оскільки за процесом не потрібно стежити вручну. Після підключення самого VPS як вузла він також може передавати через себе інтернет-трафік інших машин. Це описано в розділі налаштування exit node. Відмінність полягає в тому, що оголошений маршрут потрібно підтвердити на сервері командою headscale, а не в hosted admin console. Якщо потрібно отримати доступ до приватної мережі за цим VPS, а не забезпечити вихід в інтернет, той самий крок підтвердження охоплює оголошення цієї підмережі для решти tailnet. Публікація одного застосунку з вузла, а не маршрутизація через нього цілих мереж, є окремим завданням. Для цього використовують serve і funnel, але обидва механізми покладаються на власні certificate та ingress machinery Tailscale. Тому їх слід розглядати як функції hosted-tailnet, а не як можливості, які надає headscale.

DERP і передавання трафіку через ретранслятори, коли прямий шлях недоступний

DERP (designated encrypted relay for packets) — це резервний шлях. Якщо два вузли не можуть встановити пряме WireGuard-з’єднання, зазвичай тому, що обидва перебувають за суворим NAT (network address translation), вони натомість передають пакети через ретранслятор. Ретранслятор не має ключів, тому не може прочитати ваш трафік. Проте він бачить, які вузли обмінюються даними та який обсяг даних передається.

Важливо розуміти, що робить конфігурація за замовчуванням. Headscale постачається з налаштуваннями, які вказують на https://controlplane.tailscale.com/derpmap/default, а також використовують auto_update_enabled: true і update_frequency: 3h, тому control plane належить вам, а ретранслятори — Tailscale. Для більшості користувачів це прийнятний компроміс. Якщо ні, запустіть власні ретранслятори.

Щоб запустити власний ретранслятор, задайте enabled: true у розділі derp.server файлу config.yaml, перезапустіть headscale та відкрийте порт STUN (session traversal utilities for NAT) за допомогою sudo ufw allow 3478/udp. У конфігураційному файлі вимогу зазначено однозначно: server_url має використовувати https, оскільки DERP потребує TLS. Якщо очистити список derp.urls, ретранслятори Tailscale буде вилучено з карти. Якщо після цього вбудований ретранслятор не працює, будь-яка пара вузлів, яка не може встановити пряме з’єднання, взагалі не зможе з’єднатися.

На клієнті команда tailscale netcheck виводить затримку до кожного регіону ретрансляторів, відомого клієнту, а tailscale status позначає кожен вузол як direct з адресою або relay із кодом регіону. Вузол, який застряг у стані relay, має проблему з NAT, а не з headscale. Вузол у стані direct, який усе одно працює повільно, потребує іншої діагностики; зазвичай проблема полягає в MTU, а не в самому тунелі.

Чому вузол має статус offline?

Проксі відкидає upgrade-запит. Це найпоширеніша причина. Її ознака: усе інше працює нормально — /health повертає 200, headscale nodes list показує вузол, але вузол не переходить в online. Керувальне з’єднання — це POST-запит із Upgrade: tailscale-control-protocol. Проксі, який не передає цей запит, розриває єдиний канал, що повідомляє стан вузла. Порівняйте конфігурацію nginx із наведеним вище блоком map або тимчасово перейдіть на Caddy, щоб виключити проксі як причину.

server_url змінився після реєстрації вузлів. Вузли продовжують підключатися за значенням, яке отримали під час реєстрації. Якщо ви змінили це значення, виконайте sudo tailscale up --login-server https://headscale.example.com --force-reauth на кожному вузлі.

Клієнт не запущений. На вузлі виконайте sudo systemctl is-active tailscaled і sudo journalctl -u tailscaled -n 50 --no-pager. Клієнт, який не може визначити адресу вашого домену або підключитися до нього, записує спроби повторного підключення саме там.

Ключ минув. Цю причину розглянуто в наступному розділі.

Щоб переглядати журнали на сервері під час перевірки, виконайте sudo journalctl -u headscale -f на VPS і перезапустіть tailscaled на клієнті. Вузол, який досягає headscale, одразу створює записи в журналі. Відсутність записів означає, що запит не надходить. Спочатку перевірте DNS, firewall і проксі, а вже потім headscale.

Термін дії ключа та вузол, який перестає працювати через кілька тижнів

Є два окремі терміни дії. Якщо їх плутати, усунення проблеми займе більше часу.

Термін дії preauth keys навмисно короткий. Значення за замовчуванням — одна година та одне використання. Якщо tailscale up відхиляє ключ, згенеруйте новий на сервері, а не редагуйте щось на клієнті.

Node keys — це довготривала частина. Розділ node у config.yaml задає expiry: 0, а 0 означає відсутність терміну дії за замовчуванням: зареєстрований вузол залишається чинним, доки ви не завершите його дію. Для вузлів із тегами термін дії не спливає в будь-якому разі. Установіть expiry: 180d, якщо потрібно, щоб реєстрації автоматично ставали недійсними, і врахуйте наслідки: кожному вузлу без тегу тоді буде потрібен sudo tailscale up --login-server https://headscale.example.com --force-reauth за цим розкладом, а headless-сервер, на якому ніхто повторно не проходить автентифікацію, самостійно зникне з мережі.

Виконайте це вручну, якщо хтось втратив ноутбук. sudo headscale nodes list надає ідентифікатор, потім sudo headscale nodes expire -i 3 завершує сеанс цього вузла, а sudo headscale nodes delete -i 3 повністю видаляє його з мережі.

Резервні копії та оновлення

/var/lib/headscale і /etc/headscale разом містять увесь сервер. Зупиніть сервіс перед копіюванням, оскільки SQLite може ще виконувати операції запису, а база даних, скопійована під навантаженням, може бути непослідовною.

sudo systemctl stop headscale
sudo tar czf /root/headscale-state.tgz -C /var/lib headscale
sudo tar czf /root/headscale-config.tgz -C /etc headscale
sudo systemctl start headscale
sudo chmod 600 /root/headscale-*.tgz

Перенесіть обидва файли за межі сервера. Вони містять приватні ключі та всі реєстраційні дані, тому потребують такого самого захисту, як і сам сервер. У розділі резервне копіювання з VPS за допомогою restic описано, як виконувати це за розкладом і в зашифрованому вигляді.

Оновлення повторює процедуру встановлення: завантажте нові .deb, sudo apt install ./headscale.deb, потім перезапустіть сервіс і повторно виконайте перевірки is-active та /health. Починаючи з версії 0.29, шлях оновлення суворо обмежений. Пропуск проміжної версії заблокований, як і повернення до старішої проміжної версії. Оновлюйтеся на одну проміжну версію за раз, перед кожним кроком створюйте резервну копію та спочатку ознайомтеся з примітками до випуску цієї версії, оскільки в ній змінено поведінку політики ACL і переміщено кілька ключів конфігурації.

FAQ

Чи headscale уже запущений одразу після встановлення .deb?

Так. Пакет вмикає та запускає сервіс одразу після встановлення, з параметрами /etc/headscale/config.yaml, які постачаються за замовчуванням. Сервіс прослуховує лише loopback і встановлює server_url у значення http://127.0.0.1:8080. Ця адреса непридатна для клієнтів, тому відредагуйте server_url, listen_addr і base_domain, потім виконайте sudo systemctl restart headscale і перевірте результат командою sudo systemctl is-active headscale. Значення має бути restart: headscale читає файл лише під час запуску, а виконання enable --now для вже запущеного сервісу нічого не змінює. Якщо після перезапуску сервіс не запускається, sudo journalctl -u headscale -n 50 --no-pager покаже причину. На цьому етапі це майже завжди помилка YAML, оскільки headscale аналізує весь файл до початку прослуховування порту.

Чи потрібно все одно встановлювати звичайний клієнт Tailscale на моїх машинах?

Так. Headscale замінює лише control server. На кожному вузлі працює офіційний клієнт Tailscale, якому ви вказуєте свій сервер за допомогою sudo tailscale up --login-server https://headscale.example.com. Цей прапорець є у стандартному клієнті, тому нічого не потрібно виправляти або збирати повторно.

Чи проходить мій трафік через сервер headscale?

Зазвичай ні. Headscale координує мережу та видає ключі й адреси, а передавання даних відбувається безпосередньо між вузлами через WireGuard. Трафік обходить прямий маршрут лише тоді, коли два вузли не можуть з’єднатися безпосередньо й переходять на DERP relay. У конфігурації, що постачається за замовчуванням, це публічні relay-сервери Tailscale. Виконайте tailscale status на вузлі, щоб перевірити, чи певний peer має стан direct або використовує relay.

Чому мій вузол залишається offline після реєстрації?

Вузол, який відображається в headscale nodes list, але ніколи не переходить у стан online, зазвичай втратив control connection на reverse proxy. Це з’єднання є HTTP upgrade-запитом, надісланим методом POST із заголовком Upgrade: tailscale-control-protocol. nginx відкидає його, якщо не додати блок map $http_upgrade $connection_upgrade і відповідні рядки proxy_set_header. Caddy пересилає такий запит без додаткової конфігурації. Тому його зручно використовувати, щоб перевірити, чи є причиною проблеми reverse proxy.

Чи потрібні для headscale доменне ім’я та TLS?

На практиці так. Клієнти підключаються до рядка, який ви вказуєте в server_url. Сертифікати видаються для імен, а не для звичайних IP-адрес. Файл конфігурації також вказує, що DERP потребує TLS. Домен разом із Caddy налаштовується приблизно за п’ять хвилин і надає HTTPS endpoint з автоматичним поновленням. Якщо запускати control server через звичайний HTTP, кожен обмін даними клієнта з ним проходить через інтернет у відкритому вигляді.