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

Получить VPS arrow_forward
eco Начальный Туториал

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

calendar_month Oct 13, 2026 schedule 18 мин. чтения visibility 37 просмотров
Развёртывание 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. Troubleshooting и 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 с ограниченным доступом.

Был ли этот гайд полезен?

Ваш отзыв помогает нам улучшать гайды.

Поделиться записью:

Отправьте гайд тому, кому он может пригодиться.

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.