Tailscale + Headscale: свой control-сервер на Debian 12
Tailscale строит mesh-сеть поверх WireGuard, но координацию узлов — обмен ключами, ACL, DNS — по умолчанию делает облачный control-сервер вендора. Если вы не хотите, чтобы метаданные вашей сети (список узлов, их IP, теги, политика доступа) хранились на чужой инфраструктуре, поднимите Headscale — открытую реализацию control-сервера, совместимую с обычным клиентом tailscale. Ниже — пошаговая установка на Debian 12: от голого сервера до рабочей сети с ACL и маршрутами подсетей.
Содержание
- Зачем свой control-сервер вместо облака Tailscale
- Подготовка сервера: домен, DNS, firewall
- Установка Headscale на Debian 12
- Конфигурация config.yaml и TLS через реверс-прокси
- Пользователи, preauth-ключи и подключение клиентов
- ACL, маршруты подсетей, exit node и MagicDNS
- Мониторинг, бэкапы, типичные проблемы
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Зачем свой control-сервер вместо облака Tailscale
Tailscale как продукт состоит из двух частей: клиент (демон tailscaled + WireGuard-туннели между узлами) и control plane — сервис, который раздаёт узлам ключи друг друга, DERP-релеи для NAT-траверсала, ACL-политику и DNS-записи. Сам трафик между узлами идёт напрямую по WireGuard и control-сервер его не видит, но он знает топологию сети: кто с кем в одной tailnet, какие у узлов имена и теги, какая версия клиента.
Headscale — open-source сервер, который реализует тот же протокол координации, что и tailscale.com, и понимается штатным клиентом без патчей: достаточно указать --login-server при первом запуске. Свой Headscale имеет смысл, если:
- вы не хотите зависеть от аккаунта и лимитов бесплатного тарифа Tailscale (там ограничение на число устройств);
- нужен полный контроль над ACL и хранением метаданных сети;
- есть требования по размещению данных на своей инфраструктуре, а не у стороннего облачного провайдера.
Обратная сторона: Headscale не реализует 1:1 все функции коммерческого Tailscale (например, Tailscale Funnel и часть SSO-интеграций поддержаны частично или отсутствуют), и поддержку придётся оказывать себе самому — апдейты, бэкапы, мониторинг ложатся на вас.
Подготовка сервера: домен, DNS, firewall
Понадобится VPS с публичным IPv4, доменное имя (или поддомен) с A-записью на этот IP, и открытые порты для TLS и, при желании, для встроенного DERP-релея.
Минимальные требования: 1 vCPU, 512 МБ–1 ГБ RAM для сети из десятков-сотен узлов на SQLite — Headscale написан на Go и ест ресурсы экономно. Для сотен-тысяч узлов и высокой нагрузки лучше сразу смотреть в сторону PostgreSQL вместо SQLite.
Заведите поддомен, например headscale.example.com, и направьте A-запись на IP сервера — как настроить DNS для домена на Debian 12, подробно разбирали в статье про настройку домена и DNS.
Базовые пакеты и обновление системы:
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget ca-certificates gnupg
Firewall: если ещё не настраивали ufw на этом сервере, сначала пройдите базовую настройку — как открыть только нужные порты, описано в статье про настройку файрвола на Debian 12. Для Headscale за реверс-прокси достаточно:
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
Порт 80 нужен для выпуска Let's Encrypt-сертификата (HTTP-01 challenge), 443 — для самого control-сервера. Если планируете свой DERP-релей на этом же хосте, дополнительно откройте UDP 3478 и диапазон, который укажете в конфиге DERP.
Арендуйте сервер под свои задачи!
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверУстановка Headscale на Debian 12
Официальный способ — скачать .deb-пакет с GitHub Releases проекта и поставить через dpkg. Точную версию лучше брать динамически, а не хардкодить в команде, — так вы всегда получите актуальный релиз:
HEADSCALE_VERSION=$(curl -s https://api.github.com/repos/juanfont/headscale/releases/latest \
| grep '"tag_name"' | cut -d '"' -f4)
wget "https://github.com/juanfont/headscale/releases/download/${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION#v}_linux_amd64.deb" \
-O headscale.deb
sudo dpkg -i headscale.deb
Пакет создаёт системного пользователя headscale, каталоги /etc/headscale (конфиг) и /var/lib/headscale (данные, включая SQLite-базу), а также unit systemd headscale.service, изначально выключенный — сначала нужно донастроить конфиг.
Проверьте, что бинарник встал:
headscale version
Если вместо GitHub Releases предпочитаете собирать из исходников (например, на архитектуре, для которой нет готового .deb), потребуется Go нужной версии и git clone репозитория проекта с последующим go build, но для типового VPS на amd64/arm64 готовый пакет закрывает вопрос быстрее и без лишних зависимостей на сервере.
Конфигурация config.yaml и TLS через реверс-прокси
Откройте /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
grpc_listen_addr: 127.0.0.1:50443
grpc_allow_insecure: false
database:
type: sqlite
sqlite:
path: /var/lib/headscale/db.sqlite
dns:
magic_dns: true
base_domain: ts.example.com
nameservers:
global:
- 1.1.1.1
- 8.8.8.8
derp:
server:
enabled: false
urls:
- https://controlplane.tailscale.com/derpmap/default
Headscale слушает только на loopback (127.0.0.1:8080) — наружу его выпускает реверс-прокси с TLS. Это стандартная и рекомендованная схема: прокси снимает TLS и держит HTTP/2, который нужен gRPC-эндпоинтам.
Пример конфига Caddy (проще всего для автоматического Let's Encrypt) в /etc/caddy/Caddyfile:
headscale.example.com {
reverse_proxy 127.0.0.1:8080
}
Caddy сам выпустит и обновит сертификат при перезапуске сервиса — вручную ничего дёргать не придётся. Если у вас уже настроен nginx, тоже подойдёт, но нужно явно прописать proxy_http_version 1.1 и заголовки Upgrade/Connection, а для gRPC-порта — отдельный location с grpc_pass, иначе CLI-клиент headscale не сможет подключаться удалённо.
После правки конфига включите и запустите сервис:
sudo systemctl enable --now headscale
sudo systemctl status headscale
Строка base_domain в блоке dns формирует MagicDNS-имена узлов вида hostname.ts.example.com — она должна отличаться от server_url, иначе будет конфликт зон.
Пользователи, preauth-ключи и подключение клиентов
В терминологии Headscale узлы группируются в "пользователей" (users, в старых версиях — namespaces). Создайте пользователя для своей сети:
sudo headscale users create home
Дальше — pre-auth ключ, чтобы подключать устройства без интерактивного логина в браузере:
sudo headscale preauthkeys create --user home --expiration 24h --reusable
Флаг --reusable делает ключ многоразовым (удобно для нескольких устройств), --expiration ограничивает срок действия — для продакшена лучше выпускать одноразовые ключи под конкретное устройство и не хранить их дольше необходимого.
На клиентской машине (Linux, macOS, Windows, роутер с поддержкой tailscale) устанавливаете обычный клиент tailscale и указываете свой control-сервер:
sudo tailscale up --login-server https://headscale.example.com --authkey <ваш-preauth-ключ>
Если ключ не передан, клиент выведет ссылку на страницу авторизации — её нужно будет подтвердить через headscale nodes register на сервере (для варианта без веб-UI Headscale это делается вручную командой из вывода).
Проверить список подключённых узлов:
sudo headscale nodes list
Здесь видно IP из диапазона 100.64.0.0/10 (тот же CGNAT-диапазон, что использует Tailscale), имя узла, последний онлайн и то, какие маршруты он анонсирует.
ACL, маршруты подсетей, exit node и MagicDNS
По умолчанию Headscale работает в "открытом" режиме — все узлы одного пользователя видят друг друга. Для реальной сети стоит завести файл политики доступа, например /etc/headscale/acl.hujson:
{
"acls": [
{
"action": "accept",
"src": ["home"],
"dst": ["home:*"]
}
],
"tagOwners": {
"tag:server": ["home"]
}
}
Путь к файлу указывается в config.yaml через policy.path, применение политики происходит при рестарте сервиса или через headscale policy reload в новых версиях (команда может отличаться в зависимости от релиза — сверяйтесь с headscale policy --help).
Маршруты подсетей позволяют узлу отдавать в tailnet доступ к своей локальной сети (например, домашней LAN за NAT):
sudo tailscale up --login-server https://headscale.example.com \
--authkey <ключ> --advertise-routes=192.168.1.0/24
Маршрут не активируется автоматически — его нужно одобрить на control-сервере:
sudo headscale nodes list-routes
sudo headscale routes enable -r <route-id>
Exit node (маршрутизация всего трафика клиента через один узел) настраивается похожим образом — флаг --advertise-exit-node на сервере-шлюзе и одобрение маршрута на Headscale, после чего на клиентских устройствах включается tailscale up --exit-node=<имя-узла>.
MagicDNS при включённом dns.magic_dns: true даёт каждому узлу имя hostname.ts.example.com, резолвящееся в его tailnet-IP без ручного редактирования /etc/hosts — это удобно, если вы уже проходили установку WireGuard на VPS руками и знаете, сколько времени уходит на ручное управление IP-адресами и конфигами пиров при росте сети.
Мониторинг, бэкапы, типичные проблемы
Логи сервиса — через journalctl:
sudo journalctl -u headscale -f
Метрики Prometheus отдаются на metrics_listen_addr (в примере выше — 127.0.0.1:9090/metrics), их можно подключить к уже настроенному стеку мониторинга, если вы разворачивали его по той же схеме, что в статье про мониторинг Debian 12 из коробки.
Бэкап при SQLite — это просто копия файла базы (лучше через sqlite3 .backup, а не голый cp, чтобы не словить незавершённую транзакцию на активной базе):
sudo sqlite3 /var/lib/headscale/db.sqlite ".backup /root/headscale-backup-$(date +%F).sqlite"
При PostgreSQL — стандартный pg_dump по расписанию cron. Сохраняйте вместе с базой и config.yaml, и acl.hujson — без них восстановленная база бесполезна.
Частые проблемы на практике:
| Симптом | Вероятная причина |
|---|---|
| Клиент не может подключиться, TLS-ошибка | реверс-прокси не настроен на HTTP/2, либо сертификат не выпустился (проверьте порт 80 открыт) |
tailscale up зависает на "Logging in..." | узел не зарегистрирован через headscale nodes register, если использовался интерактивный логин без preauth-ключа |
| Узлы не видят друг друга | не настроена или не применена ACL-политика, либо пользователи (users) разные |
| Маршрут подсети не работает у клиентов | маршрут анонсирован, но не одобрен командой headscale routes enable |
| Медленное соединение между узлами за NAT | нет доступного DERP-релея — по умолчанию используются публичные relay Tailscale, при их недоступности из России может понадобиться свой DERP на VPS с прямым выходом |
Если публичные DERP-релеи Tailscale недоступны или дают большую задержку из вашей локации, в config.yaml можно включить встроенный DERP-сервер (derp.server.enabled: true) прямо на вашем Headscale-хосте — тогда узлы будут резервно переключаться на него при невозможности установить прямой WireGuard-туннель.
Арендуйте сервер под свои задачи!
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Headscale — это форк Tailscale?
Нет, это независимая реализация протокола control-сервера с открытым исходным кодом. Клиент используется официальный, от Tailscale, — меняется только адрес сервера координации.
Можно ли одновременно пользоваться и облаком Tailscale, и своим Headscale?
Да, но не в одной tailnet одновременно с одного клиента — устройство подключено либо к одному control-серверу, либо к другому. Переключение — это tailscale up --login-server ... --force-reauth.
Нужен ли отдельный сервер под DERP-релей?
Нет, если публичные relay Tailscale доступны и задержка устраивает. Свой DERP имеет смысл при плохой связности или желании держать весь трафик координации на своей инфраструктуре.
Что будет, если Headscale-сервер упадёт?
Уже установленные WireGuard-туннели между узлами продолжат работать (координация не участвует в передаче данных), но новые узлы не смогут подключиться, а изменения ACL/маршрутов не применятся до восстановления сервиса.
SQLite или PostgreSQL?
Для сети в пределах нескольких десятков-сотен узлов SQLite достаточно и проще в бэкапе. PostgreSQL стоит brать заранее, если планируете рост до тысяч узлов или несколько параллельных читателей базы.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →