SSD Nodes Learn Hosting plans →
Руководства Matt ConnorАвтор: Matt Connor

Свой DERP-сервер Tailscale на VPS: сборка и настройка

Свой DERP-релей вместо чужого: derper той же версии, что tailscaled, флаг --verify-clients, порты 80, 443 и 3478 и регион 900 в derpMap. Пошагово, с проверками.

Что даёт свой DERP-сервер Tailscale

Свой DERP-сервер Tailscale на VPS заменяет чужой ретранслятор вашим. Когда прямое соединение между двумя устройствами не поднимается, Tailscale отправляет их трафик через релей, и по умолчанию это сервер Tailscale где-то в мире. После этой инструкции такой сервер будет стоять на вашем VPS, отвечать на вашем домене со своим TLS-сертификатом и обслуживать только устройства вашей сети. Скорость и стабильность канала между вашими устройствами будут зависеть от вашего VPS, а не от загрузки чужого релея.

Сразу о границах. Свой DERP меняет только то, кто ретранслирует пакеты и по какому имени хоста. Он ничего не скрывает от координационного сервера Tailscale, и он не выходной узел: интернет-трафик через него не пойдёт. Если задача именно «выходить в интернет через VPS», вам нужен exit node Tailscale на VPS, а не релей.

Как работает DERP и почему трафик уже идёт через релей

DERP (Designated Encrypted Relay for Packets) это протокол ретрансляции, которым Tailscale пользуется, когда два узла не могут связаться напрямую. Каждый узел держит постоянное соединение с «домашним» DERP-сервером по HTTPS на порту 443. Если прямой путь по UDP не устанавливается, пакеты WireGuard уходят на DERP отправителя, оттуда на DERP получателя и только потом на само устройство. Шифрование остаётся сквозным: релей видит зашифрованные пакеты WireGuard и публичные ключи узлов, но не содержимое.

Прямой путь не поднимается по двум причинам. Либо NAT (трансляция сетевых адресов) с обеих сторон не даёт пробить UDP-соединение через NAT (hole punching), либо UDP на пути режется или сильно ограничен по скорости. В обоих случаях tailscale status показывает у пира relay "..." вместо direct, а скорость упирается в чужой сервер. Как отличить одно от другого и что можно сделать без своего сервера, разобрано в статье про медленный Tailscale: прямое соединение против релея. Здесь решается другая задача: если релей всё равно неизбежен, пусть он будет вашим.

Модель угроз: что меняется, а что остаётся

Полезно проговорить это до первой команды, потому что «свой сервер» звучит как «приватность», а на деле картина точнее.

  • Координационный сервер Tailscale по-прежнему знает все ваши устройства, их публичные ключи, IP-адреса и сам список DERP-серверов. Именно он раздаёт узлам вашу derpMap. Если не устраивает эта зависимость, это задача для собственного координационного сервера Headscale, а не для DERP.
  • Релей видит адреса, с которых подключаются узлы, их ключи, объём и время передачи. Содержимое пакетов он расшифровать не может, потому что ключи WireGuard есть только у конечных устройств.
  • Без флага --verify-clients к вашему DERP может подключиться любой узел Tailscale из любой чужой сети, и его трафик пойдёт за счёт вашего канала. С флагом derper спрашивает у локального tailscaled, известен ли ему этот узел, и отбрасывает незнакомые.
  • Через DERP проходит только трафик между узлами вашей сети. Он не маршрутизирует интернет и не меняет ваш внешний IP.

Что понадобится

VPS с публичным IPv4-адресом и Ubuntu 24.04. Доменное имя, для которого вы можете создать A-запись, в примерах это derp.example.com. Свободные порты 80/tcp и 443/tcp: если на этом же VPS уже стоит nginx или Caddy, derper не сможет занять 443, а перенос на другой порт тянет за собой ручной сертификат. Проще выделить под DERP отдельный небольшой VPS.

Расположение VPS выбирайте по задержке. Пакет между двумя вашими устройствами проходит путь «устройство A, VPS, устройство B», поэтому сервер посередине между устройствами даёт меньшую задержку, чем сервер далеко от обоих. И считайте трафик: каждый байт, прошедший через релей, проходит через интерфейс VPS дважды, на входе и на выходе, так что лимит трафика тарифа расходуется вдвое быстрее, чем кажется.

Шаг 1. Установить tailscaled на VPS и узнать его версию

Флаг --verify-clients работает только когда на том же хосте запущен tailscaled и этот узел состоит в вашем tailnet (так Tailscale называет вашу приватную сеть). Поэтому VPS сначала становится обычным узлом сети.

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
tailscale version

tailscale up напечатает ссылку для входа; откройте её в браузере и подтвердите устройство. tailscale version первой строкой выводит номер версии, например 1.102.4. Запишите его: под эту же версию будет собран derper.

Сразу зайдите в админ-консоль Tailscale, найдите этот VPS в списке машин и отключите у него истечение ключа (Disable key expiry). По умолчанию ключ узла живёт 180 дней. Когда он истечёт, tailscaled на VPS выпадет из сети, derper с --verify-clients перестанет подтверждать клиентов, и релей молча откажет всем.

Шаг 2. Собрать derper той же версии, что tailscaled

Готовых пакетов derper нет, его собирают из исходников на Go. Версия важна: derper проверяет клиентов через локальный API tailscaled, и когда бинарники расходятся, проверка падает с ошибкой tailscaled version is too old (out of sync with derper binary). Поэтому тег для сборки берётся из вывода tailscale version, а не @latest.

Модуль Tailscale версии 1.102.4 требует Go не ниже 1.26.6, а в репозитории Ubuntu 24.04 лежит более старый компилятор. Поставьте Go с официального сайта. На сентябрь 2026 года актуальная стабильная версия Go 1.27.1.

curl -fsSLO https://go.dev/dl/go1.27.1.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.27.1.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
go version

go version должен ответить go version go1.27.1 linux/amd64. Теперь сборка. Подставьте свой тег: буква v плюс номер из tailscale version.

go install tailscale.com/cmd/derper@v1.102.4
sudo install -m 0755 ~/go/bin/derper /usr/local/bin/derper
derper --version

Сборка скачивает весь модуль Tailscale и занимает несколько минут. Если она обрывается строкой signal: killed, компилятору не хватило памяти: добавьте swap или соберите на другой машине с Linux той же архитектуры и скопируйте один файл. derper --version печатает версию бинарника, и число в ней должно совпадать с тем, что показал tailscale version.

Шаг 3. Открыть порты 80, 443, 3478 и ICMP

Снаружи DERP-серверу нужны четыре вещи: 80/tcp для HTTP, 443/tcp для HTTPS (по нему ходят и клиенты, и проверка при выдаче сертификата), 3478/udp для STUN и ICMP в обе стороны. Порт HTTP переопределить нельзя. Порты HTTPS и STUN можно поменять в derpMap полями DERPPort и STUNPort, но derper отдаёт TLS на нестандартном порту только с ручным сертификатом (--certmode=manual), так что для первой установки оставьте 443.

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 3478/udp
sudo ufw status

ICMP echo ufw пропускает по умолчанию правилами из /etc/ufw/before.rules, отдельной команды не нужно. Если ufw на этом VPS ещё не включён, начните с основ настройки ufw на VPS, иначе первый же ufw enable без правила для SSH закроет вам вход. И проверьте второй файрвол, сетевой, в панели провайдера: там те же порты часто закрыты отдельно.

Шаг 4. Запустить derper через systemd

derper работает от root. Причин две: он слушает привилегированные порты 80 и 443, и без явного -c он ищет свой ключ в /var/lib/derper/derper.key только от root, а от обычного пользователя завершается с derper: -c <config path> not specified. Каталог для сертификатов положим рядом.

sudo install -d -m 0700 /var/lib/derper/certs
sudo tee /etc/systemd/system/derper.service > /dev/null <<'EOF'
[Unit]
Description=Tailscale DERP relay
After=network-online.target tailscaled.service
Wants=network-online.target

[Service]
ExecStart=/usr/local/bin/derper \
  --hostname=derp.example.com \
  --certdir=/var/lib/derper/certs \
  --verify-clients
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now derper
sudo journalctl -u derper -n 20 --no-pager

О флагах. --hostname обязателен: по умолчанию там стоит derp.tailscale.com, и derper попытается получить сертификат на чужое имя. --certmode не трогаем: у derper версии 1.102.4 его значение по умолчанию letsencrypt, и в этом режиме он получает сертификат сам по протоколу ACME (библиотека autocert), только для имени из --hostname, и хранит его в --certdir. Проверку владения доменом Let's Encrypt проводит по HTTP на порту 80 или по TLS на порту 443, поэтому оба должны быть открыты, а A-запись уже должна указывать на VPS. STUN на 3478/udp включён по умолчанию (--stun=true).

В журнале должна появиться строка derper: serving on :443 with TLS, а перед ней no config path specified; using /var/lib/derper/derper.key. Теперь проверка снаружи, с любой машины:

curl -sS https://derp.example.com/ | grep -i derp

Ответ содержит текст This is a Tailscale DERP server. Первый запрос может занять несколько секунд или оборваться на TLS: derper в этот момент как раз получает сертификат. Повторите через полминуты. Если ошибка держится, проверьте dig +short derp.example.com: он должен вернуть адрес VPS.

Шаг 5. Добавить регион в derpMap файла политики

Клиенты узнают о вашем сервере из derpMap, которую координационный сервер раздаёт всем узлам tailnet. Она задаётся в файле политики: админ-консоль Tailscale, раздел Access controls. Добавьте на верхний уровень JSON, рядом с acls или grants, блок derpMap. Идентификаторы регионов от 900 до 999 зарезервированы под пользовательские серверы.

"derpMap": {
  "OmitDefaultRegions": true,
  "Regions": {
    "900": {
      "RegionID": 900,
      "RegionCode": "myderp",
      "RegionName": "My VPS DERP",
      "Nodes": [
        {
          "Name": "900a",
          "RegionID": 900,
          "HostName": "derp.example.com",
          "IPv4": "203.0.113.15"
        }
      ]
    }
  }
}

HostName должен совпадать с --hostname: по этому имени клиент проверяет TLS-сертификат. IPv4 необязателен; если он указан, клиент подключается сразу по адресу и не зависит от DNS, что полезно, когда резолвер на устройстве работает ненадёжно. RegionCode это короткое имя, которое вы увидите в tailscale status и tailscale netcheck, а RegionName попадает в строку Nearest DERP.

Переключатель "OmitDefaultRegions": true заставляет весь tailnet использовать только регионы из вашего блока. Именно он нужен, когда серверы Tailscale для вас медленные: без него каждый узел выберет ближайший по задержке регион, и это снова может оказаться чужой релей. Обратная сторона: пока VPS недоступен, релея у сети нет вообще, и узлы без прямого пути друг друга не увидят. Прямые соединения при этом продолжают работать. Более мягкий вариант: оставить регионы по умолчанию, добавить свой и выключить только отдельные.

"derpMap": {
  "Regions": {
    "1": null,
    "900": { "RegionID": 900, "RegionCode": "myderp", "RegionName": "My VPS DERP", "Nodes": [ { "Name": "900a", "RegionID": 900, "HostName": "derp.example.com" } ] }
  }
}

Здесь 1 это RegionID региона Tailscale, который вы исключаете; текущую карту с номерами и кодами печатает tailscale debug derp-map на любом узле. Сохраните файл политики; узлы получают новую derpMap автоматически, обычно в течение нескольких секунд.

Шаг 6. Проверить, что устройства ходят через ваш релей

На любом устройстве tailnet:

tailscale netcheck

Ищите в отчёте строку вида * Nearest DERP: My VPS DERP и в блоке * DERP latency: строку с кодом myderp и задержкой до вашего VPS. Если регион в списке есть, узел получил новую derpMap и дотянулся до сервера. Дальше:

tailscale status
tailscale ping other-device

У пира без прямого пути tailscale status показывает active; relay "myderp", а tailscale ping отвечает строкой вида pong from other-device (100.101.102.103) via DERP(myderp) in 48ms. Если там по-прежнему direct, у этих двух узлов прямой путь работает, и релей им не нужен; это хороший результат, а не ошибка.

На VPS в это время sudo journalctl -u derper -f показывает, что происходит с подключениями. Строка client <ключ> rejected: peer <ключ> not authorized (not found in local tailscaled) означает, что --verify-clients отбросил узел, которого tailscaled на VPS не знает.

Что ломается и как это выглядит

derper не стартует, в журнале bind: address already in use. Порт 443 или 80 занят другим сервисом. sudo ss -ltnp 'sport = :443' покажет, каким. Остановите его или вынесите DERP на отдельный VPS.

Сертификат не выдаётся, curl обрывается на TLS. В журнале derper при каждом подключении появляется http: TLS handshake error from ... с причиной. Проверьте по порядку: A-запись (dig +short derp.example.com должен вернуть адрес VPS), порты 80 и 443 в панели провайдера, значение --hostname в юните. Опечатка в --hostname означает, что derper просит сертификат на другое имя, и Let's Encrypt его не выдаст.

Клиенты отбрасываются с not found in local tailscaled. derper спросил tailscaled на VPS, и тот ответил, что такого узла не знает. Проверьте tailscale status там же. Если он показывает Logged out., ключ узла истёк или узел вышел из сети: выполните sudo tailscale up заново и отключите истечение ключа. Если список машин есть, но нужного устройства в нём нет, ACL вашего tailnet не разрешает VPS видеть этот узел. tailscaled знает только те узлы, с которыми ему разрешено общаться, поэтому устройство, скрытое от VPS правилами доступа, не пройдёт проверку. Разрешите VPS видеть все узлы, которым нужен релей.

tailscaled version is too old (out of sync with derper binary). Версии разошлись. Чаще всего это случается после apt upgrade, который обновил tailscale, а derper остался прежним. Пересоберите derper под новый тег из tailscale version и перезапустите юнит. Обновление Tailscale на этом VPS теперь всегда состоит из двух действий.

Регион не появляется в tailscale netcheck. Узел не получил новую derpMap или не может дотянуться до VPS. Выполните tailscale debug derp-map на этом узле: если региона 900 там нет, файл политики не сохранился или содержит ошибку, и проблема не в сети. Если регион есть, проверьте с этого же устройства curl -sS https://derp.example.com/ и порт 3478/udp в обоих файрволах.

FAQ

Скрывает ли свой DERP-сервер мой трафик от Tailscale?

Нет. Свой DERP меняет только то, через какой сервер и по какому имени хоста идут ретранслируемые пакеты. Координационный сервер Tailscale по-прежнему видит список устройств, их ключи и адреса, и именно он раздаёт узлам derpMap с вашим регионом. Содержимое трафика не видит ни он, ни релей: пакеты WireGuard зашифрованы сквозным образом между устройствами. Если нужен контроль и над координацией, это отдельный проект, Headscale.

Можно ли выходить в интернет через свой DERP-сервер?

Нет. DERP ретранслирует только трафик между узлами вашего tailnet, ваш внешний IP-адрес не меняется, и на сайты вы по-прежнему выходите со своего обычного адреса. Чтобы весь интернет-трафик устройства шёл через VPS, на этом VPS настраивают exit node Tailscale, и это можно сделать на том же сервере параллельно с derper.

Почему derper нужно собирать той же версии, что tailscaled?

С флагом --verify-clients derper на каждое подключение спрашивает локальный tailscaled через его API, известен ли ему узел с таким ключом. Когда версии расходятся, вызов заканчивается ошибкой tailscaled version is too old (out of sync with derper binary), и derper отбрасывает клиентов. Тег для go install tailscale.com/cmd/derper@vX.Y.Z берите из вывода tailscale version на VPS, а после каждого обновления пакета tailscale пересобирайте derper.

Что случится с сетью, если VPS с DERP-сервером упадёт?

С "OmitDefaultRegions": true у tailnet не останется ни одного релея: узлы с рабочим прямым путём продолжат общаться, а пары без прямого пути потеряют связь до возвращения VPS. Если такой риск неприемлем, уберите OmitDefaultRegions и исключите только медленные регионы через "<RegionID>": null, или поднимите второй derper на другом VPS как регион 901.