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

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

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

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

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

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

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

  • Координационный сервер Tailscale по-прежнему знает все ваши устройства, их публичные ключи, IP-адреса и сам список DERP-серверов. Именно он раздаёт узлам вашу derpMap. Если не устраивает эта зависимость, это задача для собственного координационного сервера Headscale, а не для DERP.
  • Релей видит адреса, с которых подключаются узлы, их ключи, объём и время передачи. Содержимое пакетов он расшифровать не может, потому что ключи WireGuard есть только у конечных устройств.
  • Без флага --verify-clients к вашему DERP может подключиться любой узел Tailscale из любой чужой сети, и его трафик пойдёт за счёт вашего канала. С флагом derper спрашивает у локального tailscaled, известен ли ему этот узел, и отбрасывает незнакомые.
  • Через DERP проходит только трафик между узлами вашей сети. Он не выполняет роль exit node и сам по себе не меняет ваш внешний 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, и разработчики требуют собирать оба бинарника из одной ревизии, поскольку тестируют их вместе. Само различие номеров версий проверку не ломает: в derper 1.102.4 ошибка tailscaled version is too old (out of sync with derper binary) возвращается только при ошибке LocalAPI с текстом invalid 'addr' parameter, указывающей на несовместимый API. Поэтому тег для сборки берётся из вывода tailscale version, а не @latest.

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

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

go version должен ответить go version go1.27.3 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

Для этой установки откройте 80/tcp для HTTP, 443/tcp для HTTPS (по нему ходят и клиенты, и проверка при выдаче сертификата), 3478/udp для STUN и ICMP в обе стороны. HTTP-порт задаётся флагом --http-port, значение по умолчанию 80; значение -1 отключает HTTP, а проверка HTTP-01 требует именно публичного порта 80. Порты 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-01 на порту 80 или по TLS-ALPN-01 на порту 443; для TLS-ALPN-01 публичный доступ к порту 80 не требуется, а 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

Ответ содержит HTML с текстом This is a <a href="https://tailscale.com/">Tailscale</a> 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 использовать только регионы DERP из вашего блока. Именно он нужен, когда серверы Tailscale для вас медленные: без него каждый узел выберет ближайший по задержке регион, и это снова может оказаться чужой релей. Обратная сторона: пока VPS недоступен, релея DERP у сети нет вообще, и узлы без прямого пути или доступного peer relay друг друга не увидят. Прямые соединения при этом продолжают работать. Более мягкий вариант: оставить регионы по умолчанию, добавить свой и выключить только отдельные.

"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

У пира, соединённого через DERP, 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), порт 443 в панели провайдера, порт 80 при проверке HTTP-01, значение --hostname в юните. Имя в --hostname должно совпадать с доменом, который вы направили на VPS: 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). В derper 1.102.4 эта ошибка означает, что вызов WhoIsNodeKey получил от LocalAPI ошибку с текстом invalid 'addr' parameter: API несовместим с запросом. Само различие номеров версий её не вызывает. Обновите tailscaled и пересоберите derper под тег из tailscale version, затем перезапустите юнит. Разработчики требуют собирать оба бинарника из одной ревизии и тестируют их вместе.

Регион не появляется в 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 и сам не выполняет роль exit node; без отдельно настроенного exit node ваш внешний IP-адрес не меняется, и на сайты вы по-прежнему выходите со своего обычного адреса. Чтобы весь интернет-трафик устройства шёл через VPS, на этом VPS настраивают exit node Tailscale, и это можно сделать на том же сервере параллельно с derper.

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

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

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

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

Была ли эта статья полезной?