Marzban: установка панели и нод с VLESS Reality
Установка Marzban на VPS: панель в Docker, ноды marzban-node, инбаунд VLESS + Reality + Vision, подписки, бэкапы и разбор типичных сбоев. Актуально на сентябрь 2026.
Разбираем схему, которую чаще всего держат наши клиенты: панель на отдельном небольшом VPS и по ноде на каждую локацию, например в Нидерландах и Германии. Версии на момент проверки: панель v0.8.4, marzban-node v0.5.2, стабильный Xray-core v26.3.27.
Что понадобится
- VPS под панель. Хватает 1 vCPU и 1 ГБ RAM, с MariaDB или MySQL — 2 ГБ: скрипт отдаёт базе буфер 256 МБ. Ubuntu 22.04/24.04 или Debian 12.
- По VPS на каждую локацию. В пакет ForestsNet Cloud идёт только исходящий трафик, но у VPN-ноды исходящим оказывается почти всё: скачанное клиентом уходит к нему, отправленное им — в интернет.
- Свой домен: поддомен для подписок с A-записью на IP панели и по поддомену на каждую ноду, см. «Как привязать домен к серверу».
- SSH под root или пользователем с sudo. Вход по паролю закройте заранее: «Усиление SSH».
Marzban в 2026 году: стоит ли начинать с него
Работает, но не развивается.
- Последний релиз — v0.8.4 от 9 января 2025 года, master с тех пор не менялся. marzban-node v0.5.2 вышла в марте 2025-го, а в августе один из разработчиков написал, что она больше не поддерживается.
- Версию 1.0 довели до beta-образов летом 2025-го, после чего её разработчики ушли в отдельный проект PasarGuard: по их словам, владельцы отвечали раз в несколько месяцев. PasarGuard уже на v5.x, со своей нодой на Go.
- Серия коммитов в dev в июне 2026-го релизом не закончилась, вопрос «проект умер?» в issues остался без ответа.
- С сентября 2025-го открыта несовместимость с новыми ядрами Xray, обход — в шаге 3.
Начинаете с нуля, тем более коммерческий сервис на несколько нод, — берите Remnawave: он активно развивается, его ноды работают без панели, каждой можно назначить свой профиль Xray, см. «Установка Remnawave». Marzban разумен, если уже работает или нужна небольшая установка, которую вы готовы чинить сами. Для переезда есть утилита миграции Remnawave (старые ссылки подписок Marzban там продолжают работать) и инструкция PasarGuard.
Как устроен Marzban
- Панель — приложение на Python (FastAPI) в Docker с
network_mode: host. Хранит пользователей в базе, собирает конфиг Xray изxray_config.json, сама подставляя их вclients, и запускает собственное ядро Xray. Поэтому сервер панели тоже слушает все инбаунды. - Ноды — агент marzban-node. Панель подключается к нему по REST с взаимной TLS-аутентификацией на
SERVICE_PORT(62050), передаёт конфиг, а через gRPC API Xray наXRAY_API_PORT(62051) добавляет пользователей без перезапуска и забирает статистику. - Подписки — адрес
/sub/<токен>/на той же панели. Серверы в них берутся из «Настроек хоста» (Host Settings): один хост — одна строка в подписке.
Следствия:
- Все ноды получают один конфиг и запускают все инбаунды панели; сузить набор на ноде можно переменной
INBOUNDS. - При
marzban down,restartиupdateпанель отключает ноды, и Xray на них стоит до её возвращения; сохранение «Основных настроек» (Core Settings) перезапускает ядра везде. Работы на панели — обрыв у всех клиентов. - Если панель пропала аварийно, ноды работают с последним конфигом, но перезагруженная нода Xray не поднимет: конфиг ей приносит панель.
- Сертификат для связи с нодами и секрет, которым подписаны ссылки подписок, лежат в базе. Потеряли базу — переподключаете все ноды и раздаёте новые ссылки.
Шаг 1. Установите панель
SQLite, MariaDB или MySQL
- SQLite — по умолчанию: один файл
/var/lib/marzban/db.sqlite3, обслуживать нечего. Хватает для личного VPN и нескольких десятков пользователей. - MariaDB или MySQL — отдельный контейнер, данные в
/var/lib/marzban/mysql, порт 3306 только на127.0.0.1. Берите, если пользователей сотни или нод несколько: каждые 10 секунд панель пишет в базу трафик каждого пользователя на каждой ноде, и SQLite начинает отвечатьdatabase is locked.
Перенос с SQLite на MySQL — ручная работа с простоем по рецепту из документации, так что выбирайте сразу.
Запустите установочный скрипт
# SQLite
sudo bash -c "$(curl -sL https://github.com/Gozargah/Marzban-scripts/raw/master/marzban.sh)" @ install
# MariaDB (для MySQL укажите --database mysql)
sudo bash -c "$(curl -sL https://github.com/Gozargah/Marzban-scripts/raw/master/marzban.sh)" @ install --database mariadbСкрипт поставит Docker, для MariaDB и MySQL спросит пароль базы (Enter — сгенерировать), поднимет контейнеры и откроет логи; Ctrl+C закрывает только логи. docker-compose.yml и .env лежат в /opt/marzban, конфиг Xray, база и сертификаты — в /var/lib/marzban, управление — командой marzban.
Ключевые переменные .env
Открыть — marzban edit-env, применить — marzban restart.
UVICORN_HOST,UVICORN_PORT— адрес и порт приложения, по умолчанию0.0.0.0и8000.UVICORN_SSL_CERTFILE,UVICORN_SSL_KEYFILE— сертификат, если панель сама отдаёт HTTPS. Файлы должны лежать в/var/lib/marzban: другие каталоги в контейнер не смонтированы. Самоподписанный сертификат панель отвергнет безUVICORN_SSL_CA_TYPE = "private".DASHBOARD_PATH— путь к админке, по умолчанию/dashboard/.XRAY_SUBSCRIPTION_URL_PREFIX— внешний адрес подписок, напримерhttps://sub.example.com:8443. Пустое значение — самая частая ошибка: ссылки получаются относительными. Путь задаётXRAY_SUBSCRIPTION_PATH, по умолчаниюsub.SUB_UPDATE_INTERVAL,SUB_PROFILE_TITLE,SUB_SUPPORT_URL— интервал автообновления (часы, по умолчанию 12), название профиля и ссылка на поддержку.TELEGRAM_API_TOKEN,TELEGRAM_ADMIN_ID— встроенный бот и уведомления о входах в панель.
Шаг 2. Откройте доступ к панели и подпискам
Без сертификата Marzban слушает только 127.0.0.1:8000, даже если в .env стоит UVICORN_HOST = "0.0.0.0": так устроен код запуска. Создайте первого администратора (команда спросит логин и пароль) и откройте туннель со своего компьютера:
marzban cli admin create --sudo
ssh -L 8000:127.0.0.1:8000 root@PANEL_IPПанель откроется на http://127.0.0.1:8000/dashboard/. Подписки же должны быть доступны клиентам всегда, а отдаёт их то же приложение, поэтому наружу публикуем только путь /sub/. Порт 443 на сервере панели занимает Reality — ядро панели слушает те же инбаунды, что и ноды, — поэтому подписки вешаем на 8443: выпустите сертификат по статье «Бесплатный SSL с Let's Encrypt» и добавьте сайт nginx.
server {
listen 8443 ssl;
server_name sub.example.com;
ssl_certificate /etc/letsencrypt/live/sub.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/sub.example.com/privkey.pem;
location /sub/ {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
}
location / {
return 404;
}
}В .env пропишите XRAY_SUBSCRIPTION_URL_PREFIX = "https://sub.example.com:8443" и выполните marzban restart.
Путь из документации проще — сертификат в /var/lib/marzban/certs/ и переменные UVICORN_SSL_*, — но наружу тогда открыты админка и API. Если выбрали его, смените DASHBOARD_PATH, включите уведомления о входах и не ставьте UVICORN_PORT = 443: этот порт нужен Reality.
Шаг 3. Настройте инбаунд VLESS + Reality + Vision
Скрипт кладёт в /var/lib/marzban/xray_config.json пример с одним инбаундом Shadowsocks на порту 1080, его заменяем целиком. Сгенерируйте ключи и shortId:
docker exec marzban-marzban-1 xray x25519
openssl rand -hex 8До v25.8.3 ядро печатает Private key и Public key, в более новых — PrivateKey и Password: это и есть публичный ключ, в ссылке он станет параметром pbk.
{
"log": {
"loglevel": "warning"
},
"routing": {
"rules": [
{ "type": "field", "ip": ["geoip:private"], "outboundTag": "BLOCK" },
{ "type": "field", "protocol": ["bittorrent"], "outboundTag": "BLOCK" }
]
},
"inbounds": [
{
"tag": "VLESS TCP REALITY",
"port": 443,
"protocol": "vless",
"settings": { "clients": [], "decryption": "none" },
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"dest": "127.0.0.1:8443",
"serverNames": ["nl.example.com", "de.example.com"],
"privateKey": "ВАШ_PRIVATE_KEY",
"publicKey": "ВАШ_PUBLIC_KEY",
"shortIds": ["6ba85179e30d4fc2"]
}
},
"sniffing": { "enabled": true, "destOverride": ["http", "tls", "quic"] }
}
],
"outbounds": [
{ "protocol": "freedom", "tag": "DIRECT" },
{ "protocol": "blackhole", "tag": "BLOCK" }
]
}Главное:
dest— ваш сайт на этой же ноде, nginx на127.0.0.1:8443(настройка ниже). В новой документации Xray поле называетсяtarget, ноdestпонимают и старые ядра.serverNames— домены ваших нод, без пустой строки. Marzban выбирает SNI для ссылки случайно из этого списка, поэтому SNI каждой ноды задаётся явно в «Настройках хоста».shortIds— хотя бы одно значение, иначе панель не запустится: hex чётной длины до 16 символов.publicKeyсервер Xray игнорирует, а Marzban v0.8.4 берёт ключ отсюда. Без этого поля панель разбирает выводxray x25519по старому формату и с ядром v25.8.29 и новее падает при запуске.networkоставьтеtcp: Marzban копирует его в ссылку, а нового имениrawстандарт ссылок не знает.- Vision (
xtls-rprx-vision) включается не в инбаунде, а у пользователя — в шаге 5.
Конфиг вставьте в «Основные настройки» (Core Settings): панель проверит JSON, сохранит файл и перезапустит ядро на всех нодах. Либо правьте файл и выполняйте marzban restart. В логах должна появиться строка Xray core ... started.
Свой сайт для Reality на каждой ноде
На серверах ForestsNet Cloud Reality настраивается только на своём домене и своём сайте. Чужой домен (google.com, microsoft.com и т. п.) в SNI, dest или сертификате ведёт к блокировкам адресов, которые бьют по всем клиентам сети, поэтому он запрещён Условиями использования, а сертификаты на публичных портах проверяются автоматически.
Схема: у каждой ноды свой поддомен, например nl.example.com и de.example.com, с A-записью на её IP без проксирования Cloudflare. На ноде nginx с сертификатом Let's Encrypt слушает 127.0.0.1:8443, dest указывает на него, и любой, кто откроет 443 ноды без вашего ключа, увидит ваш сайт с настоящим сертификатом. Один инбаунд подходит всем нодам: dest у всех локальный, а в serverNames перечислены домены всех нод. На каждой ноде выполните:
sudo apt install -y nginx certbot
sudo certbot certonly --webroot -w /var/www/html -d nl.example.com --deploy-hook "systemctl reload nginx"Certbot подтвердит домен через стандартный сайт nginx на порту 80 и дальше будет сам продлевать сертификат, поэтому порт 80 на ноде держите открытым. Положите свою страницу в /var/www/html/index.html и создайте /etc/nginx/conf.d/reality.conf:
server {
listen 127.0.0.1:8443 ssl http2;
server_name nl.example.com;
ssl_certificate /etc/letsencrypt/live/nl.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/nl.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
root /var/www/html;
}Примените sudo nginx -t && sudo systemctl reload nginx и проверьте: curl -sSI --http2 --resolve nl.example.com:8443:127.0.0.1 https://nl.example.com:8443 | head -1 должен ответить HTTP/2 200.
Когда подключите ноду (шаг 4), впишите её домен в поле SNI её хоста в «Настройках хоста»: иначе Marzban подставит в ссылку случайное имя из serverNames. На сервере панели dest ведёт в nginx подписок из шага 2; если панель тоже обслуживает клиентов, добавьте домен подписок в serverNames и в SNI её хоста.
Шаг 4. Подключите ноды
В панели откройте «Настройки узлов» (Node Settings) → «Добавить новый узел Marzban» → «Показать сертификат». Скопируйте его целиком, от BEGIN до END: сертификат один на панель и подходит для всех нод. На сервере ноды запустите:
sudo bash -c "$(curl -sL https://github.com/Gozargah/Marzban-scripts/raw/master/marzban-node.sh)" @ installСкрипт попросит вставить сертификат (ввод завершает пустая строка) и спросит протокол и порты. На вопрос о REST ответьте «да»: так нода без сертификата панели не стартует, а в старом rpyc к ней без сертификата подключится кто угодно. Порты оставьте 62050 и 62051. Сертификат ляжет в /var/lib/marzban-node/cert.pem, управление — командой marzban-node.
Порты управления открывайте только для IP панели. API ядра на 62051 от чужих закрыто лишь правилом маршрутизации по IP, а открытые наружу порты панелей видны сканерам.
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow from PANEL_IP to any port 62050,62051 proto tcp
sudo ufw enableНода работает в network_mode: host, так что ufw действует как обычно, см. «Файрвол ufw: порты».
Вернитесь в панель и заполните форму: имя (например, NL-1), IPv4 ноды, порты 62050 и 62051. «Коэффициент использования» (Usage Ratio) умножает трафик ноды при учёте лимитов, например 1.5 для дорогой локации. Флажок «Добавить этот узел, как новый хост для каждого inbound» создаст хосты с IP ноды во всех инбаундах.
У инбаунда уже есть хост по умолчанию {SERVER_IP}, то есть IP самой панели. Если панель не должна обслуживать клиентов, удалите этот хост и закройте 443 на её файрволе.
Ядро обновляют marzban core-update на панели и marzban-node core-update на нодах; держите везде одну стабильную версию. Меню показывает последние релизы вместе с pre-release (10 на панели, 5 на ноде), и стабильного v26.3.27 в нём уже нет — введите его вручную через пункт M. Pre-release v26.9.8 и новее отклоняют клиентов без ключа X25519MLKEM768 в рукопожатии, а это на сентябрь 2026 все клиенты на sing-box и mihomo.
Шаг 5. Заведите пользователей и раздайте подписки
В панели нажмите «Создать нового пользователя»:
- Протокол — VLESS, в его настройках Flow —
xtls-rprx-vision. Без этого ссылка работает, но без Vision. - «Лимит трафика» и «Период сброса трафика»: без сброса, день, неделя, месяц или год.
- «Дата истечения срока» или режим «В режиме ожидания» (On Hold): срок пойдёт с первого подключения, и купленный заранее ключ не сгорает.
Если ссылка утекла, нажмите «Отозвать подписку»: старая перестанет действовать.
Ссылка подписки — https://sub.example.com:8443/sub/<токен>/. Клиенты на mihomo (Clash Verge, FlClash) получают YAML, sing-box, Hiddify и Karing — JSON sing-box, остальные — ссылки в base64, браузер — страницу с QR-кодами. Формат можно задать явно, дописав к ссылке v2ray, clash-meta или sing-box.
Клиенты с поддержкой Reality и Vision:
- Android: v2rayNG, Happ, v2RayTun, Hiddify.
- iOS и macOS: Happ, Streisand, V2Box, OneXray (бывший FoXray), Karing, Shadowrocket.
- Windows и Linux: v2rayN, Hiddify, Happ, Clash Verge Rev, Throne (продолжение NekoRay).
Проверяйте клиенты в российском App Store: на сентябрь 2026 официальных Happ, Streisand и V2Box там нет, зато есть приложения с похожими названиями от других разработчиков.
Шаг 6. Бэкапы, обновления и логи
marzban backup собирает /opt/marzban/backup/backup_<дата>.tar.gz: база (копия SQLite или дамп MariaDB/MySQL), .env, docker-compose.yml и /var/lib/marzban без сменных ядер и сырых файлов MySQL. marzban backup-service ставит задачу в cron и шлёт архивы Telegram-боту. Нюансы:
- Каждый запуск стирает предыдущий архив: на сервере лежит только последний, копию уносите наружу.
- SQLite копируется на ходу обычным
cp. Целостную копию дастsqlite3 /var/lib/marzban/db.sqlite3 ".backup '/root/marzban.sqlite3'"(пакетsqlite3). - Схемы хранения — в статье «Бэкапы баз данных».
marzban update и marzban-node update скачивают свежий образ и пересоздают контейнеры; у v0.8.4 обновлять нечего, пока не выйдет релиз. Флаг --dev в прод не ставьте: образ dev собран в июне 2025-го, сборки 2026-го не прошли.
marzban logs и marzban-node logs показывают логи, с -n — без слежения; логи Xray панели и каждой ноды видны в окне «Основных настроек». Docker по умолчанию не ротирует логи контейнеров: задайте max-size и max-file в /etc/docker/daemon.json и пересоздайте контейнеры.
Журнал подключений для разбора абьюз-жалоб включается строкой "access": "/var/lib/marzban/access.log" в секции log; путь работает и на нодах. Без logrotate журнал быстро забьёт диск.
Диагностика
Нода в статусе connecting или error
- Висит connecting, или Xray на ноде перезапускается каждые 10 секунд — панель не достучалась до 62050 или 62051. Проверьте с панели:
nc -vz NODE_IP 62050, затем 62051. - Ошибки TLS. В логах панели —
EOF occurred in violation of protocol, вmarzban-node logs— ошибки сертификата: он вставлен не целиком или не от этой панели. Скопируйте его заново в/var/lib/marzban-node/cert.pemи выполнитеmarzban-node restart. После переустановки панели без восстановления базы сертификат новый, менять его придётся на всех нодах. - Порт занят. Статус error с текстом
address already in use: порт инбаунда на ноде занят, обычно nginx на 443, проверьтеss -ltnp | grep ':443'.
Reality не подключается
- Причину покажет лог. С
"loglevel": "info"ядра с v25.10.15 пишутREALITY: processed invalid connectionс причиной:server name mismatch— SNI нет вserverNames,authentication failed— не тот ключ или shortId,target sent incorrect server hello— проблема сdest. - Клиент не обновил подписку после смены ключей,
shortIdsили flow. При несовпадении flow сервер отклоняет подключение. - Не отвечает локальный сайт. Если nginx не слушает
127.0.0.1:8443, Reality не подключит никого. Проверьте с ноды той же командойcurl: отказ соединения — nginx не слушает порт (ss -ltnp | grep 8443); ошибка сертификата — он истёк и не продлился, проверьтеcertbot renew --dry-runи порт 80;HTTP/1.1вместоHTTP/2— вlistenнетhttp2, а README REALITY требует HTTP/2. - Адрес хоста — домен за Cloudflare. Reality работает только напрямую до IP ноды.
- Pre-release ядро v26.9.x, а клиент на sing-box или mihomo — откатите ядро до v26.3.27.
- Подключение есть, но через 15–20 КБ всё замирает. Так ТСПУ с июня 2025 года ограничивает соединения с подсетями ряда хостингов, инбаунд тут ни при чём. Проверьте сеть клиента чекером TCP 16-20; помогает смена IP или локации либо промежуточный сервер в России.
- Не работает только в мобильной сети. Операторы вводят белые списки, при которых недоступны любые зарубежные IP. IP ForestsNet Cloud ежедневно проверяются на блокировки ТСПУ, так что начинайте с сети клиента.
Панель не запускается после смены ядра
TypeError: 'NoneType' object is not subscriptable в marzban logs — это новый формат вывода xray x25519. Добавьте publicKey, как в шаге 3, или вернитесь на встроенное ядро: удалите XRAY_EXECUTABLE_PATH из .env и выполните marzban restart.
Подписка не обновляется
- Ссылки ведут на локальный адрес — не задан
XRAY_SUBSCRIPTION_URL_PREFIX, и панель подставляет адрес, с которого вы открыли админку, например127.0.0.1. - Клиент получает 404 —
locationв nginx не совпадает сXRAY_SUBSCRIPTION_PATH, подписку отозвали, пользователя пересоздали или подняли новую базу. - Ошибка TLS в клиенте — сертификат продлён, а nginx его не перечитал: добавьте
systemctl reload nginxв deploy-hook certbot. - Пустой список серверов — панель не узнала User-Agent: допишите формат к ссылке.
Высокая нагрузка
- Панель грузит CPU и базу — поднимите
JOB_RECORD_USER_USAGES_INTERVALиJOB_RECORD_NODE_USAGES_INTERVALдо 30–60 секунд. Если статистика по нодам не нужна, включитеDISABLE_RECORDING_NODE_USAGE = True: таблица почасового трафика по нодам растёт быстрее всех. - Блокировки SQLite (
database is locked) — переезжайте на MariaDB по рецепту из документации. - Воркеры uvicorn не добавить: Marzban однопроцессный, число воркеров зашито в код с пометкой «не менять». Упёрлись в это на тысячах пользователей — смотрите на Remnawave или PasarGuard.
Что дальше
- Пройдите «Чеклист базовой защиты VPS», поставьте fail2ban и настройте блокировку торрентов.
- Для сравнения: 3x-ui с VLESS Reality, Remnawave, Hysteria 2 и обзор протоколов для своего VPN. Если нагружена сама нода — «Высокая нагрузка на сервер».
Источники
- Gozargah/Marzban, Marzban-node, Marzban-scripts
- Документация Marzban на русском
- Issue #1938 — падение панели на новых ядрах Xray
- Issue #1956 и Marzban-node #44 — разработчики об уходе в PasarGuard
- Релизы XTLS/Xray-core, XTLS/REALITY, документация Xray по REALITY
- sing-box #4520 — несовместимость с Xray v26.9.8+
- Блог Cloudflare и ntc.party — ограничение 16 КБ
- «Не дайте РКН заблокировать ваш VPS» — признаки, за которые банят IP
- «Marzban vs 3X-UI» и «Как мы делали VPN под Китай»
- PasarGuard: миграция с Marzban, Remnawave: миграция с Marzban и сравнение с Marzban