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

Headscale: власний control server для Tailscale

Запустіть власний control server 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 реалізація control server Tailscale. Тому сервером, який координує вашу приватну мережу, є VPS у вашому володінні. Це community-проєкт, яким не керує Tailscale Inc. На кожній машині все одно працює офіційний клієнт tailscale. Щоб указати ваш сервер, клієнту потрібен один прапорець — --login-server.

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

Один екземпляр headscale обслуговує один tailnet (одну мережу Tailscale). Проєкт описує таку конфігурацію як придатну для особистого використання або невеликої організації. Якщо у вас три або чотири машини, звичайний WireGuard VPN на власному VPS потребує менше програмного забезпечення та створює менше можливостей для помилок. Headscale стає виправданим, коли ви більше не хочете вручну створювати блок [Peer] для кожного нового ноутбука. Якщо вам потрібен self-hosted control plane, але ви віддаєте перевагу власному клієнту та вебінтерфейсу для керування пірами, а не 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. Він не запускає сервіс, і це правильний порядок дій. Конфігурація з пакета спрямовує server_url на http://127.0.0.1:8080. Жоден ваш клієнт не може підключитися до цієї адреси, тому сервіс, запущений на цьому етапі, працював би неправильно, навіть якби запустився. Виконання sudo systemctl is-active headscale на цьому етапі виведе inactive. Це очікувана поведінка, а не помилка.

Налаштуйте 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, у каталозі, який створив пакет і власником якого він є. Для tailnet такого розміру SQLite достатньо.

Запустіть headscale і перевірте, що він працює

sudo systemctl enable --now 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. enable --now виконує обидві дії: запускає сервіс і налаштовує його запуск після перезавантаження.

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

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

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, а не GET, а значення заголовка Upgrade дорівнює tailscale-control-protocol. Caddy передає це без додаткової конфігурації. nginx цього не робить, тому для front end на nginx потрібна map для 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-виклику ACME (середовища автоматичного керування сертифікатами) і перенаправлення на HTTPS. Caddy також потребує цей порт, щоб отримати сертифікат.

Порт 8080 залишається закритим. listen_addr — це 127.0.0.1:8080, тому проксі підключається до headscale через loopback-інтерфейс, і правило брандмауера не потрібне. Відкриття порту 8080 для інтернету надає клієнтам незашифрований канал керування й не дає жодних переваг. Пам’ятайте, що більшість провайдерів використовує другий брандмауер у панелі керування, окремо від UFW. Тому порт може бути відкритим на сервері, але закритим на межі мережі. У розділі Основи брандмауера 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. З цього випливають два наслідки. Команда не працює, коли service зупинено. Це ще одна причина дотримуватися порядку дій у цьому посібнику. Також потрібен 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 показує вузол із його ідентифікатором, користувачем і станом підключення.

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

Машина, на якій раніше виконували вхід до розміщеного сервісу Tailscale, зберігає цей обліковий запис. Спочатку виконайте на ній 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, а не в адміністративній консолі розміщеного сервісу.

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 виводить затримку до кожного відомого регіону relay, а tailscale status позначає кожен peer як direct з адресою або relay з кодом регіону. Якщо peer завис у стані relay, це проблема NAT, а не headscale. Якщо peer має стан direct, але з’єднання все одно повільне, це вже інше питання: зазвичай проблема пов’язана з MTU, а не з самим тунелем.

Чому вузол відображається як офлайн?

Проксі відкидає upgrade-запит. Це найпоширеніша причина. Її ознака в тому, що все інше працює нормально: /health повертає 200, headscale nodes list показує вузол, але вузол не переходить у стан онлайн. Керувальне з’єднання — це 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.

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

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

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

Ключі вузлів — це довготривала частина. Розділ 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?

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

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

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

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

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

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

Вузол, який відображається в headscale nodes list, але не переходить у стан онлайн, зазвичай втратив керувальне з’єднання на 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 з автоматичним поновленням сертифіката. Якщо запускати сервер керування через звичайний HTTP, кожен обмін даними клієнта з ним проходить через інтернет у відкритому вигляді.