bolt Valebyte VPS від $4/міс — NVMe, запуск за 60 секунд.

Отримати VPS arrow_forward
eco Початковий Туторіал

Розгортання Headscale на VPS: приватна mesh VPN, ACL, DNS і підключення клієнтів

calendar_month Oct 13, 2026 schedule 18 хв. читання visibility 66 переглядів
Развёртывание Headscale на VPS: приватная mesh VPN, ACL, DNS и подключение клиентов
info

Потрібен сервер для цього гайду? Ми пропонуємо виділені сервери та VPS у 50+ країнах з миттєвим налаштуванням.

Потрібен сервер для цього гайду?

Розгорніть VPS або виділений сервер за хвилини.

Розгортання Headscale на VPS: приватна mesh VPN, ACL, DNS і підключення клієнтів

TL;DR

Headscale — self-hosted control server для клієнтів Tailscale. Він дає змогу створити власну mesh VPN-мережу на VPS, керувати вузлами, ACL-правилами та MagicDNS без залежності від хмарного control plane. У цьому посібнику Headscale встановлюється на Ubuntu 24.04 LTS, захищається через Caddy і HTTPS, а клієнти підключаються за допомогою Tailscale.

  • Headscale працює на невеликому VPS: зазвичай достатньо 1 vCPU, 1 ГБ RAM і 10–20 ГБ SSD.
  • Для зовнішніх підключень відкриваються лише TCP 80 і 443; SSH краще обмежити своєю IP-адресою.
  • Клієнти об'єднуються в mesh VPN і можуть з'єднуватися напряму, коли це можливо.
  • ACL-файл обмежує доступ між групами та серверами.
  • MagicDNS дає змогу звертатися до вузлів за іменами замість IP-адрес.
  • Резервувати потрібно базу Headscale, конфігурацію, TLS-дані та ACL-політику.

1. TL;DR

Headscale не є VPN-клієнтом або звичайним VPN-шлюзом. Це сервер керування для Tailscale-сумісних клієнтів: він видає адреси, поширює налаштування, координує з'єднання та застосовує політики доступу. Сам користувацький трафік за можливості йде напряму між вузлами, а VPS потрібен насамперед для керування, NAT traversal і DERP-реле за неможливості прямого з'єднання.

Нижче використовується окремий домен headscale.example.com, Ubuntu 24.04 LTS, Headscale 0.27.x, Caddy 2.x і SQLite. Для production-середовища замініть домен, SSH-адреси та секрети власними значеннями.

2. Зміст

Послідовність розрахована на новий VPS із публічною IPv4-адресою. IPv6 бажаний, але не обов'язковий. Якщо сервер уже використовується для інших вебсервісів, перевірте, що порти 80 і 443 вільні, або налаштуйте reverse proxy в наявній інфраструктурі.

3. Що ми налаштовуємо і навіщо

Схема: 3. Що ми налаштовуємо і навіщо
Схема: 3. Що ми налаштовуємо і навіщо

Роль Headscale у VPN-інфраструктурі

Tailscale побудований поверх WireGuard, але приховує складність ручного налаштування ключів, маршрутів і NAT. У класичному WireGuard адміністратор створює конфігурацію кожного peer вручну. У Headscale вузли реєструються в центральній системі, отримують адреси з приватного діапазону та автоматично дізнаються про інших учасників мережі.

Headscale — відкрита реалізація control server, сумісна з Tailscale-клієнтами. Сервер не розшифровує VPN-трафік: криптографічні ключі залишаються на кінцевих пристроях. Однак control server зберігає метадані мережі, список вузлів, їхні публічні ключі та параметри маршрутизації, тому його також потрібно захищати й регулярно резервувати.

Що буде отримано в результаті

  • Приватна мережа з адресами з діапазону 100.64.0.0/10.
  • Підключення ноутбуків, серверів, телефонів і віртуальних машин через Tailscale.
  • Доступ до SSH, HTTP, баз даних і внутрішніх сервісів через VPN.
  • ACL-політика, що дозволяє доступ лише потрібним групам.
  • MagicDNS для імен на кшталт git-1.mesh.example.com.
  • HTTPS на control server і автоматичне поновлення сертифіката.

Headscale, Tailscale Cloud або звичайний WireGuard

Варіант Переваги Обмеження
Звичайний WireGuard Мінімум компонентів, повний контроль, висока передбачуваність Peer-конфігурації та маршрути доводиться підтримувати вручну
Tailscale Cloud Просте встановлення, готова адмін-панель, керований control plane Залежність від зовнішнього сервісу та його політики
Headscale на VPS Самостійне керування control plane, сумісність із Tailscale-клієнтами Оновлення, бекапи, TLS і моніторинг стають обов'язком власника

Self-hosted варіант виправданий, коли потрібно контролювати домен, метадані та правила реєстрації пристроїв, а також коли команда готова самостійно обслуговувати Linux-сервер. Для кількох особистих пристроїв хмарний варіант зазвичай простіший. Для десятків або сотень корпоративних пристроїв Headscale потребує заздалегідь продуманої моделі користувачів, ACL і резервного відновлення.

Як працює з'єднання

Після реєстрації клієнт отримує ключі та інформацію про інші вузли. Клієнти намагаються встановити прямий UDP-сеанс через NAT traversal. Якщо пряме з'єднання неможливе, використовується DERP-реле. VPS із Headscale не обов'язково передає весь трафік через себе, тому навантаження зазвичай значно нижче, ніж у традиційного VPN-шлюзу.

Якщо потрібно примусово пропускати інтернет-трафік через один сервер, це вже окреме завдання exit node. Воно потребуватиме ввімкнення IP forwarding, налаштування NAT і явного дозволу маршрутів. У базовій конфігурації цієї статті вузли отримують доступ один до одного, але інтернет-клієнтський трафік через VPS не маршрутизується.

4. Яка VPS-конфігурація потрібна для цього завдання

Схема: 4. Яка VPS-конфігурація потрібна для цього завдання
Схема: 4. Яка VPS-конфігурація потрібна для цього завдання

Мінімальні вимоги

Ресурс Мінімум Практичний варіант
CPU 1 vCPU 2 vCPU
RAM 512 МБ, без великого DERP-трафіку 1–2 ГБ
Диск 10 ГБ SSD 20–40 ГБ SSD
Мережа Публічний IPv4, 100 Мбіт/с Публічні IPv4 і IPv6, 1 Гбіт/с
ОС Ubuntu 24.04 LTS або Debian 12 Ubuntu 24.04 LTS

Для control server, що обслуговує 10–50 пристроїв, достатньо 1–2 vCPU і 1 ГБ RAM. SQLite підходить для невеликої та середньої інсталяції, якщо база зберігається на локальному SSD і регулярно копіюється. Основна змінна — не кількість зареєстрованих вузлів, а обсяг трафіку через DERP-реле.

Як практичний варіант можна взяти VPS із такими характеристиками: 2 vCPU, 2 ГБ RAM, 30 ГБ NVMe, публічний IPv4, IPv6 і порт 1 Гбіт/с. Такий запас дає змогу одночасно розмістити Headscale, Caddy, моніторинг і невеликий приватний DNS-сервіс.

Коли потрібен dedicated

Виділений сервер для самого Headscale зазвичай надлишковий. Він стає виправданим, якщо VPS має одночасно працювати як потужне DERP-реле, exit node для великої кількості користувачів, маршрутизатор кількох мереж або хост для інших ресурсоємних сервісів. Dedicated також може знадобитися через вимоги до ізоляції, передбачуваної продуктивності або великого обсягу шифрованого трафіку.

Вибір локації

Локація впливає на затримку control plane і DERP-реле. За прямого з'єднання географія Headscale майже не важлива після обміну станом. У разі використання DERP затримка між вузлами залежатиме від відстані до реле, тому для міжнародної команди розумно розміщувати сервер ближче до більшості користувачів або розгортати кілька DERP-реле в різних регіонах.

Перевірте наявність статичного IPv4, зверніть увагу на обмеження UDP і політику провайдера щодо VPN. Навіть якщо Headscale використовує HTTPS для control plane, клієнти WireGuard надають перевагу UDP, тому мережеві обмеження можуть погіршити прямі з'єднання.

5. Підготовка сервера

Схема: 5. Підготовка сервера
Схема: 5. Підготовка сервера

DNS і базові параметри

Перед установленням створіть DNS-запис A для headscale.example.com, що вказує на IPv4-адресу VPS. Якщо використовується IPv6, додайте запис AAAA. Перевірка виконується з локального комп’ютера:

# Перевіряємо, що домен вказує на потрібну адресу
dig +short A headscale.example.com
dig +short AAAA headscale.example.com

У прикладах нижче передбачається, що ви входите під тимчасовим користувачем із правами sudo. Не працюйте постійно під root.

Створення адміністратора та SSH-ключа

На робочій станції створіть ключ Ed25519, якщо його ще немає:

# Створюємо сучасну пару SSH-ключів
ssh-keygen -t ed25519 -a 100 -f ~/.ssh/headscale_ed25519

# Копіюємо публічний ключ на сервер
ssh-copy-id -i ~/.ssh/headscale_ed25519.pub root@SERVER_IP

Підключіться до сервера та створіть окремого адміністратора:

# Створюємо користувача для адміністрування
adduser deploy
usermod -aG sudo deploy

# Створюємо каталог SSH і встановлюємо права
install -d -m 700 -o deploy -g deploy /home/deploy/.ssh
install -m 600 -o deploy -g deploy /root/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys

У новому вікні термінала перевірте вхід:

# Перевіряємо вхід без пароля
ssh -i ~/.ssh/headscale_ed25519 deploy@SERVER_IP

Оновлення та базові пакети

# Оновлюємо індекси та встановлюємо виправлення безпеки
sudo apt update
sudo DEBIAN_FRONTEND=noninteractive apt full-upgrade -y

# Встановлюємо інструменти адміністрування та діагностики
sudo apt install -y ca-certificates curl wget jq unzip vim-tiny \
  ufw fail2ban sqlite3 dnsutils chrony unattended-upgrades

Увімкніть синхронізацію часу. Коректний час потрібен для TLS і правильної роботи тимчасових ключів реєстрації:

# Увімкніть службу синхронізації часу
sudo systemctl enable --now chrony

# Перевіряємо стан годинника
timedatectl status

SSH, firewall і fail2ban

Перед увімкненням firewall переконайтеся, що поточна SSH-сесія відкрита та новий вхід під користувачем deploy працює. Якщо SSH доступний лише з фіксованої мережі, обмежте порт 22 її адресою.

# Дозволяємо SSH, HTTP і HTTPS
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Увімкніть firewall
sudo ufw --force enable
sudo ufw status verbose

Для додаткового обмеження SSH замініть загальне правило правилом із вашою публічною адресою:

# Приклад: дозволяємо SSH лише з адміністративної мережі
sudo ufw delete allow 22/tcp
sudo ufw allow from ADMIN_PUBLIC_IP to any port 22 proto tcp

Вимикати парольну автентифікацію варто лише після перевірки входу за ключем:

# Створюємо окреме налаштування SSH
sudo tee /etc/ssh/sshd_config.d/ hardening.conf >/dev/null <<'EOF'
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
EOF

# Перевіряємо синтаксис і застосовуємо налаштування
sudo sshd -t
sudo systemctl reload ssh

В останній команді вище в реальному shell між каталогом і назвою файлу не має бути пробілу. Використовуйте правильний варіант:

# Записуємо SSH-налаштування до правильного шляху
sudo tee /etc/ssh/sshd_config.d/hardening.conf >/dev/null <<'EOF'
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
EOF

sudo sshd -t
sudo systemctl reload ssh
# Запускаємо захист від перебору SSH-паролів
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd

6. Установлення ПЗ — покроково

Крок 1. Визначаємо версію та архітектуру

Станом на 2026 рік для нового встановлення використовуйте актуальний стабільний реліз Headscale гілки 0.27.x. Перед production-розгортанням перевірте сторінку релізів проєкту та замініть змінну на останню стабільну версію. Не використовуйте автоматично плаваючий URL у критичній інфраструктурі без попереднього тестування.

# Перевіряємо архітектуру сервера
uname -m

# Фіксуємо версію Headscale для відтворюваного встановлення
export HEADSCALE_VERSION="0.27.1"
export HEADSCALE_ARCH="amd64"

Крок 2. Завантажуємо офіційний пакет

Пакет релізу береться з офіційного GitHub-репозиторію Headscale. Для ARM64 замініть значення архітектури на arm64, якщо відповідний пакет присутній у вибраному релізі.

# Завантажуємо Debian-пакет з офіційного релізу
cd /tmp
curl -fLO "https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_${HEADSCALE_ARCH}.deb"

# Установлюємо Headscale та його systemd unit
sudo apt install -y "./headscale_${HEADSCALE_VERSION}_linux_${HEADSCALE_ARCH}.deb"

# Перевіряємо встановлену версію
headscale version

Крок 3. Створюємо каталоги та системного користувача

# Створюємо каталог даних і конфігурації
sudo install -d -m 750 -o headscale -g headscale /var/lib/headscale
sudo install -d -m 750 -o headscale -g headscale /etc/headscale

# Перевіряємо, що пакет створив системного користувача
getent passwd headscale

Крок 4. Створюємо конфігурацію Headscale

Згенеруйте випадковий ключ Noise. Він використовується control server для встановлення захищених сесій із клієнтами та не повинен потрапляти до публічного репозиторію.

# Генеруємо приватний ключ Noise
sudo sh -c 'umask 077; openssl rand -hex 32 > /var/lib/headscale/noise_private.key'

# Призначаємо власника ключа
sudo chown headscale:headscale /var/lib/headscale/noise_private.key

Створіть конфігураційний файл. Значення домену та підмереж мають бути узгоджені з ACL і вашою наявною інфраструктурою.

# /etc/headscale/config.yaml
server_url: https://headscale.example.com
listen_addr: 127.0.0.1:8080
metrics_listen_addr: 127.0.0.1:9090

noise:
  private_key_path: /var/lib/headscale/noise_private.key

prefixes:
  v4: 100.64.0.0/10
  v6: fd7a:115c:a1e0::/48

derp:
  server:
    enabled: false
  urls:
    - https://controlplane.tailscale.com/derpmap/default

database:
  type: sqlite
  sqlite:
    path: /var/lib/headscale/db.sqlite

dns:
  magic_dns: true
  base_domain: mesh.example.com
  nameservers:
    global:
      - 1.1.1.1
      - 9.9.9.9
  search_domains: []
  extra_records_path: /etc/headscale/dns-records.json

policy:
  mode: file
  path: /etc/headscale/acl.hujson

log:
  level: info
# Захищаємо конфігурацію та перевіряємо власника
sudo chown root:headscale /etc/headscale/config.yaml
sudo chmod 640 /etc/headscale/config.yaml

Крок 5. Створюємо DNS-записи для MagicDNS

Файл додаткових записів дає змогу спрямовувати внутрішні імена на конкретні адреси. На першому етапі можна залишити порожній масив.

[
  {
    "name": "git.mesh.example.com",
    "type": "A",
    "value": "100.64.0.20"
  }
]
# Створюємо файл додаткових DNS-записів
sudo tee /etc/headscale/dns-records.json >/dev/null <<'EOF'
[]
EOF

sudo chown root:headscale /etc/headscale/dns-records.json
sudo chmod 640 /etc/headscale/dns-records.json

Крок 6. Налаштовуємо systemd

Якщо пакет не встановив готовий unit або ви хочете явно закріпити параметри запуску, створіть власний systemd-файл:

# /etc/systemd/system/headscale.service
[Unit]
Description=Headscale coordination server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=headscale
Group=headscale
ExecStart=/usr/bin/headscale serve
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/headscale
ReadOnlyPaths=/etc/headscale

[Install]
WantedBy=multi-user.target
# Перечитуємо unit-файли та запускаємо Headscale
sudo systemctl daemon-reload
sudo systemctl enable --now headscale

# Перевіряємо статус і останні повідомлення журналу
sudo systemctl status headscale --no-pager
sudo journalctl -u headscale -n 50 --no-pager

Крок 7. Установлюємо Caddy

Caddy прийматиме HTTPS-запити, отримуватиме сертифікат Let’s Encrypt і проксуватиме запити до Headscale на localhost. Використовуйте офіційний репозиторій Caddy.

# Установлюємо ключ і репозиторій Caddy
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | \
  sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | \
  sudo tee /etc/apt/sources.list.d/caddy-stable.list

# Установлюємо Caddy 2.x з офіційного репозиторію
sudo apt update
sudo apt install -y caddy

Крок 8. Підключаємо HTTPS reverse proxy

# /etc/caddy/Caddyfile
headscale.example.com {
    reverse_proxy 127.0.0.1:8080

    header {
        Strict-Transport-Security "max-age=31536000"
        X-Content-Type-Options "nosniff"
        Referrer-Policy "no-referrer"
    }

    log {
        output file /var/log/caddy/headscale-access.log
        format json
    }
}
# Перевіряємо синтаксис Caddyfile
sudo caddy validate --config /etc/caddy/Caddyfile

# Запускаємо Caddy і застосовуємо конфігурацію
sudo systemctl enable --now caddy
sudo systemctl reload caddy

# Перевіряємо HTTPS-відповідь
curl -I https://headscale.example.com/

Відповідь на кореневий URL може не бути HTML-сторінкою. Важливі успішне TLS-з’єднання та відсутність помилки reverse proxy. Якщо сертифікат не випускається, перевірте DNS, доступність TCP 80 і відсутність іншого процесу, що займає порт.

7. Конфігурація

Схема: 7. Конфігурація
Схема: 7. Конфігурація

Створення першого користувача

У Headscale користувач є логічним власником вузлів. Не плутайте його з Linux-користувачем. Створіть окремі простори для особистих пристроїв, серверів або різних команд.

# Создаём пользователя для администрирования устройств
sudo headscale users create admin

# Показываем список пользователей и запоминаем имя
sudo headscale users list

ACL-політика

ACL краще зберігати в Git, але не включати до репозиторію секрети. Нижче наведено приклад політики для двох груп: адміністратори можуть підключатися до серверів, а розробники — лише до Git-сервера та SSH-порту застосунків. Синтаксис використовує HUJSON: коментарі дозволені, лапки навколо ключів можна залишати для ясності.

{
  "groups": {
    "group:admins": [
      "admin@",
    ],
    "group:developers": [
      "dev@",
    ],
  },

  "hosts": {
    "git": "100.64.0.20",
    "app": "100.64.0.21",
    "db": "100.64.0.22",
  },

  "acls": [
    {
      "action": "accept",
      "src": ["group:admins"],
      "dst": [":"],
    },
    {
      "action": "accept",
      "src": ["group:developers"],
      "dst": ["git:22", "git:443", "app:22"],
    },
  ],

  "ssh": [
    {
      "action": "check",
      "src": ["group:admins"],
      "dst": ["autogroup:tagged"],
      "users": ["autogroup:nonroot", "root"],
    },
    {
      "action": "check",
      "src": ["group:developers"],
      "dst": ["git"],
      "users": ["autogroup:nonroot"],
    },
  ],
}

Адреси в полі hosts мають відповідати адресам вузлів у Headscale. У реальному середовищі корисно використовувати теги для серверів і окремі групи для людей. Не додавайте правило : без потреби: воно перетворює ACL на дозвіл для всієї mesh-мережі.

# Проверяем, что файл существует и читается
sudo stat /etc/headscale/acl.hujson

# Перезапускаем Headscale после изменения политики
sudo systemctl restart headscale

# Проверяем журнал на ошибки разбора конфигурации
sudo journalctl -u headscale -n 80 --no-pager

Реєстрація клієнта через pre-auth key

Для серверів зручніше використовувати одноразовий або короткочасний pre-auth key. Не передавайте його через публічні чати та не зберігайте в shell history без потреби.

# Создаём ключ регистрации на 24 часа для пользователя admin
sudo headscale preauthkeys create \
  --user admin \
  --expiration 24h

Скопіюйте виведений ключ і виконайте на клієнтському Linux-сервері:

# Устанавливаем Tailscale-клиент на Debian/Ubuntu
curl -fsSL https://tailscale.com/install.sh | sh

# Подключаем узел к собственному Headscale
sudo tailscale up \
  --login-server=https://headscale.example.com \
  --authkey=PASTE_PREAUTH_KEY \
  --accept-dns=true

# Проверяем локальное состояние клиента
sudo tailscale status

Для інтерактивної реєстрації без pre-auth key виконайте tailscale up --login-server=https://headscale.example.com. Клієнт покаже URL. Відкрийте його в браузері, потім зареєструйте вузол, що з’явився, на сервері:

# Показываем ожидающие регистрации узлы
sudo headscale nodes list

# Привязываем узел к пользователю по его идентификатору
sudo headscale nodes register --user admin --key nodekey:PASTE_NODE_KEY

# Снова выводим список зарегистрированных узлов
sudo headscale nodes list

Імена вузлів і перевірка з’єднання

# Переименовываем узел для понятного отображения
sudo headscale nodes rename --identifier NODE_ID --new-name git-1

# Проверяем узлы на сервере Headscale
sudo headscale nodes list

# На клиенте проверяем адрес и состояние mesh VPN
tailscale ip
tailscale status
tailscale netcheck

Перевірте з’єднання з іншого вузла:

# Проверяем доступ по VPN-адресу
ping -c 3 100.64.0.20

# Проверяем SSH через mesh-сеть
ssh [email protected]

# Проверяем MagicDNS-имя
getent hosts git-1.mesh.example.com
ping -c 3 git-1.mesh.example.com

DNS і MagicDNS

MagicDNS додає імена зареєстрованих пристроїв до домену, вказаного в base_domain. Клієнти Tailscale мають приймати DNS-налаштування, тому параметр --accept-dns=true важливий. Якщо на робочій станції використовується локальний менеджер DNS, наприклад systemd-resolved, NetworkManager або корпоративний агент, перевірте, хто фактично керує DNS.

Не використовуйте домен mesh-мережі як публічну DNS-зону з конфліктними записами. Практично виділити окремий піддомен, наприклад mesh.example.com, і не публікувати його в публічній зоні або залишати там лише необхідні записи.

Маршрути до приватної мережі

Для доступу до LAN через вузол-сабнет-роутер потрібно увімкнути forwarding на цьому вузлі, оголосити маршрут і дозволити його на сервері Headscale. Це окрема зона ризику: помилка в маршрутах може відкрити внутрішні мережі зайвим користувачам.

# На Linux-сабнет-роутере включаем IPv4 forwarding
echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-tailscale-forwarding.conf
sudo sysctl --system

# Объявляем внутреннюю подсеть через Tailscale
sudo tailscale up \
  --advertise-routes=192.168.10.0/24 \
  --accept-dns=true

Після цього маршрут потрібно схвалити засобами Headscale та обмежити ACL. Не рекламуйте маршрут 0.0.0.0/0, якщо навмисно не створюєте exit node.

8. Бекапи та обслуговування

Схема: 8. Бекапи та обслуговування
Схема: 8. Бекапи та обслуговування

Що необхідно резервувати

  • /var/lib/headscale/db.sqlite — зареєстровані вузли, користувачі та стан сервера.
  • /etc/headscale/config.yaml — основна конфігурація.
  • /etc/headscale/acl.hujson — політика доступу.
  • /etc/headscale/dns-records.json — додаткові DNS-записи.
  • /var/lib/headscale/noise_private.key — критично важливий приватний ключ Headscale.
  • /etc/caddy/Caddyfile і каталог сертифікатів Caddy.

Компрометація бекапу Noise-ключа та бази може надати зловмиснику цінну інформацію про control plane. Зберігайте архіви в окремому сховищі з шифруванням, обмежте доступ і не розміщуйте єдину копію на тому ж VPS.

Простий бекап через restic

Нижче наведено приклад із S3-сумісним сховищем. Значення підключення передаються через окремий файл середовища з правами 600. Для production використовуйте bucket із versioning та окремим ключем доступу, якому дозволено лише потрібний кошик.

# Устанавливаем restic из репозитория Ubuntu
sudo apt install -y restic

# Создаём файл с параметрами репозитория
sudo install -m 600 /dev/null /root/.restic-headscale.env
sudo tee /root/.restic-headscale.env >/dev/null <<'EOF'
export RESTIC_REPOSITORY="s3:https://s3.example.net/headscale-backups"
export AWS_ACCESS_KEY_ID="CHANGE_ME"
export AWS_SECRET_ACCESS_KEY="CHANGE_ME"
export RESTIC_PASSWORD="CHANGE_ME_LONG_RANDOM_PASSWORD"
EOF

# Инициализируем репозиторий один раз
sudo bash -c 'source /root/.restic-headscale.env && restic init'

Скрипт спочатку створює SQLite-дамп, потім архівує файли конфігурації та сертифікати:

# /usr/local/sbin/backup-headscale
#!/usr/bin/env bash
set -euo pipefail

source /root/.restic-headscale.env

backup_dir="$(mktemp -d)"
trap 'rm -rf "$backup_dir"' EXIT

# Делаем согласованный снимок SQLite
sqlite3 /var/lib/headscale/db.sqlite ".backup '$backup_dir/headscale.sqlite'"

# Копируем конфигурацию, ключи и настройки TLS
install -d "$backup_dir/etc-headscale" "$backup_dir/caddy"
cp -a /etc/headscale/. "$backup_dir/etc-headscale/"
cp -a /etc/caddy/Caddyfile "$backup_dir/caddy/"

# Загружаем архив в удалённое хранилище
restic backup "$backup_dir" --tag headscale

# Удаляем старые точки восстановления
restic forget --tag headscale --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
# Устанавливаем права на скрипт
sudo chmod 700 /usr/local/sbin/backup-headscale

# Запускаем тестовый бэкап
sudo /usr/local/sbin/backup-headscale

# Добавляем ежедневный запуск в cron
echo '15 03   * root /usr/local/sbin/backup-headscale >> /var/log/headscale-backup.log 2>&1' | \
  sudo tee /etc/cron.d/headscale-backup

Періодично перевіряйте відновлення:

# Просматриваем доступные точки восстановления
sudo bash -c 'source /root/.restic-headscale.env && restic snapshots'

# Проверяем целостность репозитория
sudo bash -c 'source /root/.restic-headscale.env && restic check'

Оновлення

Headscale — stateful-сервіс, тому оновлюйте його в maintenance window. Перед оновленням збережіть бекап, прочитайте release notes і перевірте сумісність формату конфігурації. Не оновлюйте одночасно Headscale, ОС, Caddy і клієнтські застосунки без можливості визначити джерело проблеми.

# Останавливаем сервис перед заменой бинарного пакета
sudo systemctl stop headscale

# Устанавливаем новый пакет, скачанный и проверенный заранее
sudo apt install -y /tmp/headscale_NEW_VERSION_linux_amd64.deb

# Запускаем сервис и проверяем журнал
sudo systemctl start headscale
sudo systemctl status headscale --no-pager
sudo journalctl -u headscale -n 100 --no-pager

Для одного VPS rolling update неможливий: на час перезапуску control plane реєстрація та оновлення стану клієнтів можуть бути недоступними, хоча вже встановлені VPN-з’єднання іноді продовжують працювати. Якщо потрібна висока доступність, проєктуйте окрему архітектуру із зовнішньою базою, кількома control server і перевіреною підтримкою такої схеми конкретною версією Headscale.

Спостережуваність

Мінімально контролюйте systemd-статус, термін дії TLS-сертифіката, заповнення диска, доступність HTTPS і вік останнього бекапу.

# Быстрая проверка состояния сервисов и диска
systemctl is-active headscale caddy
df -h /
curl -fsS https://headscale.example.com/ >/dev/null
sudo journalctl -u headscale --since "1 hour ago" --no-pager

9. Усунення несправностей і FAQ

Headscale не запускається після зміни config.yaml

Спочатку перевірте журнал: sudo journalctl -u headscale -n 100 --no-pager. Часті причини — помилка YAML, неправильний шлях до бази або ключа Noise, відсутність прав у користувача headscale і параметри з іншої версії. Переконайтеся, що файл читається сервісом, а каталог /var/lib/headscale доступний для запису. Після виправлення виконайте sudo systemctl restart headscale і повторно перевірте статус.

Клієнт повідомляє про проблему з login server або TLS

Перевірте DNS-запис командою dig, доступність https://headscale.example.com через curl -Iv і журнали Caddy. Порти TCP 80 і 443 мають бути дозволені в UFW і зовнішньому firewall провайдера. Якщо сертифікат не видано, переконайтеся, що домен вказує саме на цей VPS і що інший вебсервіс не зайняв порт 80. У клієнтській команді має використовуватися повний URL з HTTPS.

Вузол з’явився, але не видно в mesh-мережі

На сервері виконайте headscale nodes list, а на клієнті — tailscale status і tailscale netcheck. Перевірте, що вузол дійсно зареєстрований у потрібного користувача, а його локальний firewall дозволяє інтерфейс Tailscale. Помилка ACL також виглядає як відсутність доступу, хоча реєстрація пройшла успішно. Для діагностики тимчасово перевірте дозвіл конкретної адреси й порту, не відкриваючи всю мережу.

MagicDNS не розв’язує імена

Переконайтеся, що в конфігурації увімкнено dns.magic_dns: true, задано base_domain, а клієнт запущено з --accept-dns=true. Команди tailscale ip і tailscale status допоможуть підтвердити стан клієнта. На Linux перевірте resolvectl status; сторонній VPN, NetworkManager або корпоративний DNS-агент може перезаписувати налаштування. Якщо потрібен лише доступ за IP, DNS можна тимчасово вимкнути, щоб відокремити мережеву проблему від проблеми розв’язання імен.

З’єднання працює через DERP, але повільне

Виконайте tailscale netcheck на обох вузлах і подивіться, чи доступний UDP та який DERP-регіон вибрано. Симптом зазвичай означає, що NAT або firewall блокує прямий UDP-сеанс. Дозвольте вихідний UDP і перевірте, чи немає симетричного NAT у провайдера. Для віддалених користувачів можна розмістити додаткові DERP-реле ближче до їхніх регіонів, але спочатку переконайтеся, що проблема справді впливає на продуктивність, а не пов’язана з перевантаженням кінцевого сервера.

Яка VPS-конфігурація мінімально підійде?

Для самого Headscale достатньо 1 vCPU, 512 МБ RAM, 10 ГБ SSD і публічного IPv4. Практичніше вибрати 2 vCPU, 1–2 ГБ RAM і 20–40 ГБ SSD: це залишає запас для Caddy, журналів, моніторингу та невеликого DERP-трафіку. Важливіша за продуктивність наявність відкритих TCP 80/443, стабільної мережі та резервного копіювання. Якщо через сервер проходитиме великий обсяг релейного або exit-node-трафіку, орієнтуйтеся насамперед на канал і CPU.

Що вибрати — VPS чи dedicated для цього завдання?

Для control server майже завжди достатньо VPS. Dedicated потрібен не через сам Headscale, а через супутнє навантаження: велике DERP-реле, exit node, маршрутизацію кількох офісних мереж або суворі вимоги до ізоляції. Почніть із VPS і виміряйте CPU, RAM, мережевий трафік і затримку. Перехід на dedicated має сенс, коли обмеження віртуалізації або мережевого каналу підтверджені метриками, а не припускаються заздалегідь.

Чи можна використовувати SQLite у production?

Для одного Headscale на невелику кількість вузлів SQLite підходить, якщо база розміщена на локальному SSD і виконуються регулярні узгоджені бекапи. Не розміщуйте файл бази на мережевій файловій системі та не копіюйте його простим cp під час активного запису. Для великої інсталяції заздалегідь вивчіть підтримувані версією Headscale варіанти зовнішньої бази даних і протестуйте міграції на копії.

Чи безпечно відкривати Headscale в інтернет?

Публічним має бути лише HTTPS endpoint control server; сама база й адміністративний SSH не повинні бути доступними для всіх. Використовуйте актуальні версії, HTTPS, обмежений SSH, fail2ban, firewall і короткоживучі pre-auth keys. ACL не замінює оновлення та захист кінцевих пристроїв. Пам’ятайте, що Headscale керує реєстрацією вузлів: компрометація VPS або його приватних ключів потребує негайного аналізу, ротації секретів і, можливо, перевипуску довіреної інфраструктури.

10. Висновки та наступні кроки

Схема: 10. Висновки та наступні кроки
Схема: 10. Висновки та наступні кроки

У результаті Headscale працює на VPS за Caddy і HTTPS, клієнти Tailscale об’єднані в приватну mesh VPN, а доступ між вузлами контролюється ACL і MagicDNS. Конфігурація не потребує пропускати весь користувацький трафік через VPS, тому невеликого сервера зазвичай достатньо.

Далі варто перевірити відновлення з бекапа, додати моніторинг терміну дії сертифіката й диска, а потім формалізувати групи, теги та правила ACL. У разі зростання навантаження можна винести DERP-реле в додаткові регіони, додати сабнет-маршрути або окремо спроєктувати exit node з обмеженим доступом.

Чи був цей гайд корисним?

Ваш відгук допомагає нам покращувати гайди.

Share this post:

Надішліть гайд тому, кому він може стати в пригоді.

Telegram VKVK WhatsApp Facebook LinkedIn XX

розгортання headscale на VPS: приватна mesh VPN, ACL, DNS і підключення клієнтів
support_agent
Valebyte Support
Usually replies within minutes
Hi there!
Send us a message and we'll reply as soon as possible.