Ядро XanMod на серверах Akonit

Обновлено 25 августа 2026~9 мин чтенияСтатус: раскатан на флот

Скорость VPN упирается не только в канал провайдера. Между вашим устройством и сайтом, который вы открываете, стоит ядро Linux на нашем сервере — оно решает, как быстро разгонять поток, когда притормозить и что делать с потерянными пакетами. Мы заменили стоковое ядро Ubuntu на XanMod и здесь честно рассказываем, что это дало, а что нет.

Потери пакетов ↓ 30–55% на боевых серверах Скорость +7…12% (на грани шума) Hysteria2 не затрагивает Потолок канала не поднимает

Что такое XanMod

Это то же ядро Linux, что и в обычной Ubuntu, но пересобранное с набором доработок, которые в основную ветку ядра ещё не приняты или выключены по умолчанию. Проект ведёт Alexandre Frade с 2015 года и публикует готовые пакеты в собственный репозиторий.

Мы используем ветку LTS — самую консервативную из четырёх: она получает только исправления и живёт до декабря 2028 года. Версия на наших серверах — 6.18.45-x64v3-xanmod1 против 6.8.0-111, который стоял раньше.

ВеткаЧто этоБерём ли
LTS 6.18.xДолгая поддержка, только исправленияда
MAIN 7.1.xСтабильная свежая ветканет
EDGE 7.2.xВсё новое сразу, включая экспериментынет
RTЯдро реального времени, для звука и станковнет

Как сервер решает, с какой скоростью вам слать

Сеть не сообщает отправителю, какой у неё запас. Ширину канала между нашим сервером и вашим провайдером никто не объявляет — её приходится угадывать, причём заново для каждого соединения и непрерывно, потому что она меняется каждую секунду. Алгоритм, который этим занят, называется управлением перегрузкой, и именно его версия — главное, что мы получили вместе с новым ядром.

Два способа угадывать

CUBIC — то, что стоит по умолчанию почти везде, — наращивает темп, пока пакеты не начнут теряться. Потеря для него и есть сигнал «перебор»: значит, упёрлись, надо сбросить скорость и наращивать снова. Схема рабочая, но у неё встроенный побочный эффект: чтобы узнать границу, CUBIC обязан регулярно её переходить — то есть намеренно переполнять буфер ближайшего маршрутизатора. Пока буфер полон, все пакеты в нём стоят в очереди, и задержка растёт у всех, кто через этот маршрутизатор идёт.

BBR вместо этого измеряет. Он постоянно оценивает две величины:

Их произведение — объём «трубы»: сколько байт помещается в пути одновременно. Цель BBR — держать в полёте примерно столько и отправлять ровно со скоростью BtlBw. Труба заполнена, очередь пуста, потери не нужны как сигнал.

Деталь, без которой BBR не работает

BBR не выбрасывает пачку пакетов и не ждёт ответа — он раскладывает их во времени равномерно, с рассчитанным интервалом. Это называется pacing, и делает его планировщик очередей в ядре. На наших серверах для этого стоит fq, а на исходящем интерфейсе поверх него работает шейпер CAKE. Запустить BBR без пейсинга технически можно, но тогда он превращается в обычный залповый отправитель и половина смысла теряется.

Чем плоха первая версия BBR

Именно она стоит в обычном ядре Ubuntu, и именно с неё мы ушли. Четыре её свойства объясняют наши замеры лучше любых слов:

Что чинит третья версия

BBRv3 — это переработанная вторая версия плюс исправления, которые Google внёс после эксплуатации у себя. Сейчас это алгоритм по умолчанию для всего публичного трафика google.com и YouTube, то есть обкатан на планетарном масштабе.

ЧтоBBR v1 (стоковое ядро)BBRv3 (XanMod)
Реакция на потеринетесть, целевой потолок 2%
Реакция на ECNнетпо-DCTCP, с мелким порогом
Предел данных в полётедо 2× объёма трубыграницы inflight_hi / inflight_lo и запас 15% сверху не занимается
Замер чистой задержки4 пакета на 200 мс, раз в 10 соколо половины трубы, раз в 5 с
Выход из разгонакогда скорость перестала растито же плюс по потерям и ECN
Какой RTT считается сигналомвесь путь туда-обратнотолько сетевая часть, без задержки на стороне получателя
Цикл прощупывания канала8 фаз с фиксированными коэффициентамисостояния DOWN → CRUISE → REFILL → UP; сброс мягче (0.75 → 0.9), рывок вверх сильнее (2.0 → 2.25)

Читать таблицу стоит через одну мысль: третья версия перестала быть «слепой, но вежливой». Она по-прежнему строит модель канала вместо того, чтобы искать предел столкновением, но теперь у неё есть и аварийные сигналы — потери и ECN, — и явно оставленный запас, и щадящий способ перемерить задержку. Отсюда наш главный результат: данных проходит столько же или больше, а повторных отправок вдвое меньше.

Почему это особенно заметно именно на VPN

Внутри туннеля потеря стоит дороже, чем в обычном соединении, и на то две причины.

Не всё так однозначно

Независимые исследования BBRv3 показывают и обратную сторону: в некоторых конфигурациях он всё ещё забирает больше канала, чем соседние соединения на CUBIC, а потоки с разной задержкой делят канал неравномерно. Мы выбирали алгоритм по своим замерам на своих маршрутах, а не по обещаниям — и на них выигрыш есть.

Что ещё внутри этого ядра

BBRv3 — причина, по которой мы вообще пошли менять ядро. Но вместе с ним приезжает и остальное.

ЧтоЧто это значит
Сборка с LTO Компилятор оптимизирует ядро как единое целое, а не по файлам: видит вызовы между модулями и может их встроить. Тот же исходный код, более плотный машинный.
Сборка под x86-64-v3 Отдельные пакеты под поколения процессоров. Наши серверы на современных Ryzen, поэтому берём вариант, где компилятору разрешены AVX2, BMI2 и FMA — инструкции, которых нет в универсальной сборке для машин 2003 года.
MGLRU Переработанное вытеснение памяти: страницы разложены по «поколениям» вместо двух списков, и ядро реже перебирает их целиком, решая, что выгрузить. В основном ядре это есть, но по умолчанию выключено.
Патч Cloudflare Ограничивает время, которое ядро тратит на уплотнение переполненной очереди приёма. Именно этот участок кода даёт всплески загрузки процессора на серверах с тысячами одновременных соединений — то есть на наших.
PREEMPT_DYNAMIC Режим вытеснения задач выбирается на загрузке, а не намертво при сборке. Ядро может прервать само себя ради более срочной работы — для сервера, который обрабатывает пакеты, это про равномерность задержки.
sched_ext Возможность подключать планировщики задач без пересборки ядра. Мы этим не пользуемся, но именно из-за таких вещей ветку LTS мы предпочитаем экспериментальной.
Полтора года стека Между 6.8 и 6.18 — исправления в TCP, драйверах виртуальных сетевых карт и обработке прерываний. Это не «фича», но именно из таких мелочей складывается разница, которую мы измеряем.

Что мы измерили

Сначала на резервном сервере, которым никто не пользуется, — чтобы поломка ничего не стоила. Потом на боевых, под живой нагрузкой. Ниже оба замера, начиная с того, который важнее.

На боевых серверах

Считаем долю сегментов, которые пришлось отправлять повторно, — этот счётчик ведётся непрерывно на каждом сервере. Сравниваем со средним за сутки до переключения: брать ближайшие два часа нельзя, они попадают то на пик, то на затишье и врут в обе стороны.

СерверЧто на нём у людейПовторных отправок доПослеРазница
Хармоник 🇳🇱VLESS, ~100 человек1.610%0.727%−55%
Хёллентэлер 🇩🇪VLESS, ~195 человек1.826%1.282%−30%
Оркан 🇳🇱Игровой, Hysteria20.125%0.116%−7%
Яук 🇩🇪Игровой, Hysteria22.079%2.054%−1%

Проценты легко подогнать, поэтому вот те же данные в штуках. Хёллентэлер — самая загруженная машина из четырёх:

до переключения:  24 020 сегментов/с   510 повторных отправок/с   92.4 Мбит/с   200 человек
после:            24 777 сегментов/с   318 повторных отправок/с   89.8 Мбит/с   195 человек

Сегментов сервер отправляет на 3% больше, а переотправляет на 192 штуки в секунду меньше. Отговорка «просто стало тише» здесь не работает: нагрузка после перезагрузки вернулась к прежней, а у Хёллентэлера даже выросла.

Почему у игровых серверов ничего не изменилось

Это не осечка замера, а ожидаемый результат. Их пользователи работают по Hysteria2 поверх UDP, а счётчик считает TCP — до этого трафика новый алгоритм просто не достаёт. Подробнее об этом в разделе «Чего XanMod не делает».

На стенде

Отдельный резервный сервер, никакой посторонней нагрузки. Одна и та же машина, одна цель, один скрипт, восемь прогонов по 10 секунд на каждое ядро, плечо 33 мс. После замера сервер возвращали на старое ядро и повторяли — чтобы отделить эффект ядра от состояния канала.

ПоказательСтоковое 6.8 · BBR v1XanMod 6.18 · BBRv3Разница
Скорость, медиана514 Мбит/с550–575 Мбит/с+7…12%
Потери на гигабит0.02030.0091–0.0111−45…−55%
Задержка под нагрузкой41.9 мс40.3–40.9 мсбез изменений
Прогонов вообще без потерь1 из 86 из 16×3

Стенд и боевые серверы сошлись: −45…−55% в лаборатории и −30…−55% под живой нагрузкой. Совпадение лабораторного замера с боевым — редкость, и здесь оно есть.

А вот прирост скорости надёжным назвать нельзя. Он в ту же сторону, но слабый: вероятность, что случайный прогон на XanMod быстрее случайного прогона на стоковом ядре, — 0.79. Направление есть, утверждения «на 12% быстрее» нет.

Почему потери важнее скорости в цифре

Потерянный пакет — это не «чуть медленнее». Это пауза, пока отправитель поймёт, что пакет пропал, и пришлёт заново. Именно из таких пауз складываются рывки в видеозвонке, подвисания в игре и «сайт думает». Пиковая скорость в мегабитах на это влияет куда меньше.

Задержка не выросла — и это часть результата

Ускориться можно нечестно: набить очередь на маршрутизаторе и получить хорошую цифру в тесте скорости ценой выросшего пинга. Мы это проверяли отдельно: задержка под полной нагрузкой осталась прежней. Значит выигрыш не куплен раздуванием очереди.

Чего XanMod не делает

Это самая важная часть статьи. Ядро — не универсальное ускорение, и вот три вещи, на которые оно не влияет вообще.

Не ускоряет игровые серверы на Hysteria2

Hysteria2 работает поверх QUIC, а он управляет перегрузкой сам, внутри программы, минуя ядро. На таких серверах BBRv3 не задействован — им достаётся только общее оздоровление стека.

Не поднимает потолок, поставленный хостером

Если провайдер ограничил порт сервера 20 Мбит/с, никакое ядро не сделает из него 200. Такие машины лечатся сменой тарифа или переносом людей, а не пересборкой ядра.

Не отменяет предел по процессору

Шифрование и обработка пакетов упираются в ядра процессора. Сервер на 2 ядрах отдаёт примерно 250 Мбит/с и начинает деградировать при загрузке 75–80% — с XanMod эта граница сдвигается едва заметно.

Чем мы за это платим

МинусЧто это значит на практике
Сторонний репозиторий Обновления ядра приходят не от Ubuntu, а от проекта XanMod. Исправления безопасности там выходят быстро, но ответственность за них уже не на дистрибутиве.
Риск не загрузиться Новое ядро может не подняться на конкретном железе. Лечится заранее — см. ниже.
Пропадает CUBIC В XanMod список доступных алгоритмов — reno bbr. Софт, который явно просит cubic, получит отказ.
Дольше загрузка Машина возвращается за 3–4 минуты вместо одной, и в это время SSH принимает подключение, но не отвечает. Выглядит как зависшая — на деле сервер уже работает.

Как мы это ставим

Порядок один и тот же для каждой машины, и он построен так, чтобы неудача не требовала физического доступа к серверу.

  1. Проверка совместимости. Ubuntu 24.04, виртуализация KVM (в контейнере ядро не меняется вовсе), поддержка набора инструкций x86-64-v3.
  2. Установка пакета из репозитория XanMod. Старые ядра остаются на диске.
  3. Проверка набора модулей нового ядра: фильтрация трафика, WireGuard, шейпер CAKE, BBR. Если хоть одного нет — установка останавливается, ребута не будет.
  4. Одноразовая загрузка. В загрузчике по умолчанию остаётся старое ядро, а новое ставится «на один раз».
  5. Перезагрузка и приёмка по списку: все службы, туннели, порты, правила фильтрации, шейпер, возврат пользователей.
Почему одноразовая загрузка — ключевой шаг

Если новое ядро не поднимется, следующая перезагрузка вернёт сервер на старое автоматически, без консоли и без обращения к хостеру. Пока машина не отстоялась, по умолчанию грузится проверенное ядро — новое включается только на текущую загрузку.

Закрепление: ядро не слетает при перезагрузке

Одноразовая загрузка хороша ровно один раз: после неё сервер вернулся бы на старое ядро при первой же перезагрузке. Поэтому после проверки — вернулась ли машина, поднялись ли Xray, агент и туннель — новое ядро закрепляется загрузочным по умолчанию. Порядок именно такой: сначала доказательство, что ядро рабочее, потом закрепление. Не вернулась или служба не встала — закрепление не делается, и сервер остаётся на старом ядре.

Новые серверы получают ядро сразу

Установка встроена в сценарий развёртывания ноды и идёт до остальной настройки — чтобы перезагрузка пришлась на пустую машину, а не на сервер с людьми. Сценарий сам определяет поколение процессора, ставит нужную сборку, проверяет набор модулей и закрепляет ядро. Отдельного действия руками не требуется, и новая нода изначально работает на BBRv3.

Грабля, на которой мы один раз обожглись

Пункт меню загрузчика, лежащий внутри подменю, задаётся полным путём <подменю>><пункт>. С коротким именем загрузчик молча берёт первый пункт меню — то есть самое новое ядро. Страховка при этом выглядит настроенной, но не работает. Проверять надо не конфигурацию, а фактическую версию после перезагрузки.

Откат

Одной перезагрузки достаточно: сервер вернётся на стоковое ядро, потому что оно и остаётся выбранным по умолчанию. Полное удаление — снятие пакета и репозитория, тоже без переустановки системы.

Коротко для тех, кто просто пользуется VPN

Замеры выполнены на собственной инфраструктуре Akonit: iperf3 между нашими серверами, восемь прогонов по 10 секунд на каждое ядро, и наблюдение за долей повторных отправок на реальном пользовательском трафике. Методику и сырые цифры отдаём по запросу в поддержку.