Три независимых VPN-компонента под одним скриптом. Каждый устанавливается, управляется и мигрирует отдельно — или все вместе.
ssh root@YOUR_SERVER_IP
curl -fsSL https://raw.githubusercontent.com/stump3/server-manager/main/server-manager.sh | bash
Скрипт покажет статусы и предложит список действий. Каждый компонент устанавливается независимо.
VPN-панель управления. Архитектура eGames: nginx в network_mode: host, Xray (remnanode) принимает Reality-трафик на порту 443. Доступ в панель защищён cookie-ключом.
Подменю
2) ⚙️ Управление
3) 🌐 WARP Native
4) 🎨 Страница подписки
5) 🖼️ Selfsteal шаблон
6) 📦 Миграция на другой сервер
7) 🗑️ Удалить панель
0) ◀️ Назад
Параметры установки
| Параметр | Пример | Описание |
|---|---|---|
| Режим | 1 / 2 | 1 — панель + нода, 2 — только панель |
| Домен панели | panel.example.com | Основной домен |
| Домен подписок | sub.example.com | Для клиентских конфигов |
| Домен selfsteal | node.example.com | Для Reality |
| Метод SSL | 1 / 2 / 3 | Cloudflare / ACME standalone / Gcore |
| Логин суперадмина | авто | Случайные 8 букв [a-zA-Z]{8}, генерируется автоматически |
| Пароль суперадмина | авто | Случайный, генерируется автоматически |
https://panel.example.com/auth/login?KEY=VALКак работает cookie-защита
При установке генерируются два случайных слова — они вшиваются в nginx.conf как имя и значение cookie:
KEY и VAL — это [a-zA-Z]{8}, два независимых случайных слова. Nginx проверяет или наличие cookie, или наличие query-параметра — достаточно одного.
# nginx.conf — как это выглядит внутри map $http_cookie $auth_cookie { default 0; "~*xKtBpWnR=mQaYjZvL" 1; # авторизован по cookie } map $arg_xKtBpWnR $auth_query { default 0; "mQaYjZvL" 1; # авторизован по query } map "$auth_cookie$auth_query" $authorized { "~1" 1; # хотя бы одно совпало default 0; }
rp open_port, затем восстановите KEY и VAL из конфига:grep -A2 "map \$arg_" /opt/remnawave/nginx.conf | head -4Команды управления rp
Telegram MTProto прокси на Rust. Поддерживает ограничения на пользователей, hot reload без разрыва соединений.
Подменю
2) Добавить пользователя
3) Пользователи и ссылки
4) Статус и логи
5) Обновить
6) Остановить
7) Мигрировать на новый сервер (только systemd)
8) Сменить режим (systemd ↔ Docker)
0) Назад
Режимы запуска
| Режим | Плюсы | Когда выбирать |
|---|---|---|
| systemd рекомендуется | Hot reload, меньше RAM, миграция | По умолчанию |
| Docker | Изоляция, простое обновление | Если уже используете Docker-стек |
Параметры установки
| Параметр | По умолчанию | Описание |
|---|---|---|
| Порт | 8443 | Telegram: 443, 8443, 2053, 2083, 2087, 2096 |
| Домен-маскировка | petrovich.ru | Любой крупный HTTPS-сайт |
| Имя пользователя | — | Минимум один при установке |
| Секрет | авто | 32 hex-символа |
Ограничения на пользователей
| Параметр | Описание |
|---|---|
| max_tcp_conns | Максимум одновременных подключений |
| max_unique_ips | Максимум уникальных IP-адресов |
| data_quota_bytes | Квота трафика в ГБ |
| expiration_rfc3339 | Срок действия в днях |
Высокоскоростной VPN поверх QUIC/UDP. Эффективен на нестабильных каналах и при высоких потерях пакетов.
cdn.example.com).Порт 80/tcp — скрипт открывает автоматически перед получением сертификата и закрывает после.
Совместимость
| Компонент | Конфликт | Решение |
|---|---|---|
| Xray/Reality UDP 443 | ⚠ есть | Выбрать другой порт — скрипт проверит автоматически |
| nginx TCP 80/443 | частично | Порт 80 открывается временно только для ACME |
| MTProxy 2053/8443 | частично | Скрипт показывает занятые порты в меню |
| certbot / Let's Encrypt | нет | Hysteria использует собственный ACME-клиент |
Параметры установки
| Параметр | По умолчанию | Описание |
|---|---|---|
| Домен | — | FQDN вида cdn.example.com с A-записью на сервер |
| — | Для ACME-уведомлений, необязателен | |
| CA | Let's Encrypt | Центр сертификации |
| Порт | 8443 | UDP — скрипт проверяет занятость в реальном времени |
| Логин | — | Имя пользователя (вводится вручную) |
| Пароль | авто | Генерируется если не указан |
| Название | Hysteria2 | Отображается в URI и клиенте (например: 🇩🇪 Germany Hysteria2) |
| Masquerade | bing.com | Режим маскировки трафика |
| Алгоритм | BBR | BBR или Brutal |
Выбор CA
| CA | Срок | Когда использовать |
|---|---|---|
| Let's Encrypt рекомендуется | 90 дней | По умолчанию |
| ZeroSSL | 90 дней | Если Let's Encrypt заблокирован провайдером |
| Buypass | 180 дней | Если нужен более долгий срок |
Режимы маскировки
| Вариант | Тип | Описание |
|---|---|---|
| bing.com рекомендуется | proxy | Поддерживает HTTP/3 |
| yahoo.com | proxy | Стабильный |
| cdn.apple.com | proxy | Нейтральный |
| speed.hetzner.de | proxy | Нейтральный |
| /var/www/html | file | Локальная заглушка Remnawave |
| Свой URL | proxy | Любой HTTP/3 сайт |
Алгоритмы скорости
| Алгоритм | Когда использовать |
|---|---|
| BBR рекомендуется | Стабильный канал — по умолчанию |
| Brutal | Нестабильный канал, потери >5%, мобильный интернет. Создаёт до 1.4× нагрузки — следите за трафиком на VPS с лимитом |
Результат установки
hy2://Login:[email protected]:8443?sni=cdn.example.com&alpn=h3&insecure=0#Germany Hysteria2
Файлы сохраняются в /root/:
| Файл | Содержимое |
|---|---|
| hysteria-{домен}.txt | URI подключения |
| hysteria-{домен}.yaml | Конфиг Clash/Mihomo |
| hysteria-{домен}.png | QR-код PNG |
| hysteria-{домен}-users.txt | URI всех добавленных пользователей |
Подменю
2) ⚙️ Управление
3) 👥 Пользователи
4) 🔗 Подписка
5) 📦 Миграция на другой сервер
0) ◀️ Назад
Меню управления (пункт 2)
2) 📋 Логи
3) 🔄 Перезапустить
4) 📶 Bandwidth
5) 🎭 Маскировка
0) ◀️ Назад
Bandwidth
| Режим | Когда использовать | Как задать |
|---|---|---|
| BBR рекомендуется | Стабильный канал. Автоподбор скорости — ничего настраивать не нужно | Не задавать bandwidth блок |
| Brutal | Нестабильный канал, потери >5%, мобильный интернет. Агрессивно держит заданную скорость | Задать bandwidth.up и bandwidth.down |
Указывайте скорость серверного канала, не клиентского. Завышение → перегрузка сети.
| Скорость сервера | Значение |
|---|---|
| 1 Гбит/с | 1 gbps |
| 500 Мбит/с | 500 mbps |
| 100 Мбит/с | 100 mbps |
tx: N вместо tx: 0. tx: 0 — признак того что bandwidth не задан или клиент его не передал.Интеграция с Remnawave (пункт 4 → 3)
Webhook-сервис синхронизирует пользователей Hysteria2 с Remnawave в реальном времени. Форк subscription-page автоматически добавляет hy2:// URI в ответ подписки.
2) 🔗 Объединить с подпиской Remnawave (merger)
3) 🪝 Интеграция с Remnawave (webhook + sub-page)
0) ◀️ Назад
Трафик Hysteria2 агрегируется в БД Remnawave каждые 60 секунд. Панель показывает суммарное потребление — VLESS + Hysteria2 вместе. При отключении пользователя активные сессии разрываются мгновенно через /kick.
Схема работы
Hysteria2 trafficStats API (:9999) ↓ GET /traffic?clear=1 каждые 60с ↓ {username: {tx, rx}} → delta = tx + rx hy-webhook traffic_poller (фоновый поток) ↓ _save_pending() — буфер на случай краша ↓ psycopg2 → PostgreSQL :6767 ↓ UPDATE user_traffic SET used_traffic_bytes += delta ↓ _delete_pending() Remnawave Panel — показывает обновлённый трафик Remnawave user.deleted / disabled / expired ↓ handle_user_deleted() + POST /kick ↓ Hysteria2 разрывает сессии немедленно
Переменные окружения /etc/hy-webhook.env
| Переменная | Дефолт | Описание |
|---|---|---|
HY_TRAFFIC_PORT | 9999 | Порт trafficStats API |
HY_TRAFFIC_SECRET | — | Секрет для Authorization заголовка |
DATABASE_URL | — | DSN PostgreSQL. Пустой = поллер отключён |
TRAFFIC_POLL_INTERVAL | 60 | Интервал опроса в секундах |
DATABASE_URL не задан — поллер завершается сразу: DATABASE_URL не задан — учёт трафика отключён. На основную работу не влияет.Конфиг Hysteria2 — секция trafficStats
trafficStats: listen: 127.0.0.1:9999 secret: <секрет>
Добавляется автоматически при установке Hysteria2. Секрет генерируется как openssl rand -hex 16 и сохраняется в /etc/hy-webhook.env.
Ручное включение
# 1. Добавить секцию в config.yaml и перезапустить printf '\ntrafficStats:\n listen: 127.0.0.1:9999\n secret: my-secret\n' \ >> /etc/hysteria/config.yaml systemctl restart hysteria-server # 2. Установить psycopg2 pip3 install psycopg2-binary --break-system-packages # 3. Добавить переменные и перезапустить hy-webhook sed -i '/^HY_TRAFFIC_SECRET=/d;/^DATABASE_URL=/d;/^TRAFFIC_POLL_INTERVAL=/d' /etc/hy-webhook.env printf 'HY_TRAFFIC_SECRET=my-secret\nHY_TRAFFIC_PORT=9999\nDATABASE_URL=postgresql://postgres:postgres@127.0.0.1:6767/postgres\nTRAFFIC_POLL_INTERVAL=60\n' >> /etc/hy-webhook.env systemctl restart hy-webhook # 4. Проверить логи journalctl -u hy-webhook -f # Ожидаемые строки: # Traffic poller запущен, интервал 60с # Traffic записан в БД: N пользователей
Полезные SQL-запросы
-- Пользователи с трафиком SELECT u.username, u.status, round(t.used_traffic_bytes / 1073741824.0, 2) AS used_gb, round(t.lifetime_used_traffic_bytes / 1073741824.0, 2) AS lifetime_gb FROM users u JOIN user_traffic t ON u.t_id = t.t_id ORDER BY t.used_traffic_bytes DESC; -- Сбросить трафик пользователя UPDATE user_traffic SET used_traffic_bytes = 0 WHERE t_id = (SELECT t_id FROM users WHERE username = 'admin');
Перенос сервисов на новый сервер через SSH + пароль. sshpass устанавливается автоматически.
2) Перенести MTProxy (telemt, только systemd)
3) Перенести Hysteria2
4) Перенести всё (Panel + MTProxy + Hysteria2)
0) Назад
Мониторинг DNS после переноса Hysteria2
После переноса скрипт предлагает дождаться обновления DNS и автоматически перезапустить:
[ 2] cdn.example.com → 5.6.7.8 — ожидание......
[ 3] cdn.example.com → 1.2.3.4
✅ DNS обновлён
✅ Сервис перезапущен — ACME переиздаст сертификат автоматически
Таймаут ожидания — 60 минут.
После переноса — остановить старые сервисы
systemctl stop hysteria-server systemctl stop telemt cd /opt/remnawave && docker compose down
Схема трафика
Порядок запуска
nginx стартует и создаёт /dev/shm/nginx.sock
Нода (remnanode) подключается к панели на 172.30.0.1:2222
rw-core занимает :443, начинает Reality handshake и запись в nginx.sock
Диагностика
# Кто слушает 443? Должен быть rw-core, НЕ nginx ss -tlnp | grep :443 # Сокет существует? ls -la /dev/shm/nginx.sock # Xray запустился? docker logs remnanode --tail=10 | grep -E "Xray started|SPAWN_ERROR" # Конфиг содержит inbounds? docker logs remnanode --tail=5 | grep "inbounds" # OK: inbounds:[{"tag":"Steal",...}] # ПРОБЛЕМА: inbounds:[] ← нода не получила конфиг
Частые ошибки
| Симптом | Причина | Решение |
|---|---|---|
| SPAWN_ERROR: xray | nginx занимает порт 443 | Убедиться что в nginx.conf нет listen 443 для selfsteal |
| inbounds:[] | Нода без activeInbounds | В панели: Nodes → ред. → убедиться что inbound отмечен активным |
| nginx.sock отсутствует | nginx не стартовал | docker restart remnawave-nginx |
Добавляет hy2:// URI в подписку Remnawave автоматически — при создании, удалении или изменении пользователя.
Схема работы
Remnawave (user.created / deleted / disabled) ↓ POST http://172.30.0.1:8766/webhook ↓ X-Remnawave-Signature: HMAC-SHA256 hy-webhook (systemd, :8766, 0.0.0.0) ↓ verify → sanitize → gen_password → update users.json ↓ update /etc/hysteria/config.yaml ↓ systemctl reload-or-restart hysteria-server subscription-page (remnawave-sub-hy:local) ↓ при запросе подписки читает users.json ↓ добавляет hy2:// URI в base64 ответ
Требования
| Условие | Проверка |
|---|---|
| /opt/remnawave/ | Remnawave установлена через server-manager |
| /etc/hysteria/config.yaml | Hysteria2 установлена через server-manager |
| UFW: 172.16.0.0/12 → 8766 | Добавляется автоматически при установке |
| REMNAWAVE_API_TOKEN | Создать в панели: Settings → API Tokens |
Настройка webhook в .env
WEBHOOK_ENABLED=true WEBHOOK_URL=http://172.30.0.1:8766/webhook # НЕ 127.0.0.1! WEBHOOK_SECRET_HEADER=<hex64>
http://172.30.0.1:8766 — gateway Docker сети. Адрес 127.0.0.1 из контейнера указывает на сам контейнер, не на хост.Диагностика интеграции
# Статус webhook curl -s http://127.0.0.1:8766/health # Доступность из контейнера docker exec remnawave curl -s http://172.30.0.1:8766/health # Логи событий journalctl -u hy-webhook -n 20 # Проверить hy2:// в подписке curl -s "https://sub.domain/TOKEN" | base64 -d | grep "hy2://"
Hysteria2
| Путь | Назначение |
|---|---|
| /etc/hysteria/config.yaml | Конфигурация сервера |
| /usr/local/bin/hysteria | Бинарник |
| /root/hysteria-{домен}.txt | URI подключения |
| /root/hysteria-{домен}.yaml | Конфиг Clash/Mihomo |
| /root/hysteria-{домен}.png | QR-код PNG |
| /root/hysteria-{домен}-users.txt | URI всех пользователей |
Remnawave Panel
| Путь | Назначение |
|---|---|
| /opt/remnawave/.env | Конфигурация панели |
| /opt/remnawave/docker-compose.yml | Docker Compose |
| /opt/remnawave/nginx.conf | Nginx (cookie-ключ здесь) |
| /opt/remnawave/backups/ | Бэкапы |
| /usr/local/bin/remnawave_panel | Скрипт управления rp |
MTProxy (systemd)
| Путь | Назначение |
|---|---|
| /usr/local/bin/telemt | Бинарник |
| /etc/telemt/telemt.toml | Конфиг |
| /etc/systemd/system/telemt.service | Systemd-сервис |
Hysteria2
systemctl status hysteria-server journalctl -u hysteria-server -f systemctl restart hysteria-server nano /etc/hysteria/config.yaml
MTProxy
systemctl status telemt
journalctl -u telemt -f
systemctl reload telemt # hot reload без разрыва соединений
systemctl restart telemt
Remnawave Panel
cd /opt/remnawave docker compose ps docker compose logs -f remnawave docker compose restart
Hysteria2 не запускается
journalctl -u hysteria-server -n 50 --no-pager
Частые причины: порт занят, домен не резолвится, ACME не получил сертификат.
ACME не выдаёт сертификат
# Открыть порт 80 и перезапустить ufw allow 80/tcp systemctl restart hysteria-server # Проверить что порт 80 свободен ss -tulpn | grep :80 # Проверить DNS (должны совпадать) curl -4 ifconfig.me dig +short cdn.example.com
Клиент не подключается
systemctl status hysteria-server ss -tulpn | grep PORT ufw status | grep PORT
Сменить CA после установки
nano /etc/hysteria/config.yaml
# Изменить строку: ca: zerossl
systemctl restart hysteria-server
DNS не обновился после переноса
dig +short A cdn.example.com ssh root@NEW_IP systemctl restart hysteria-server
Известные проблемы и решения
| Проблема | Симптом | Решение |
|---|---|---|
| SPAWN_ERROR: xray | Xray не стартует, порт занят | Проверить что nginx.conf не содержит listen 443 ssl в selfsteal режиме. ss -tlnp | grep :443 |
| inbounds:[] | Нода получает пустой конфиг | Панель → Nodes → редактировать → убедиться что inbound Steal (443) отмечен активным |
| Webhook ECONNREFUSED | Remnawave не достучивается до hy-webhook | Проверить WEBHOOK_URL=http://172.30.0.1:8766 (не 127.0.0.1). Проверить UFW: ufw status | grep 8766 |
| hy-webhook слушает 127.0.0.1 | Docker не видит webhook | Добавить LISTEN_HOST=0.0.0.0 в /etc/hy-webhook.env, перезапустить |
| 502 Bad Gateway на панели | nginx не может достучаться до Remnawave | Проверить что proxy_protocol соответствует источнику: unix-сокет требует proxy_protocol, прямой 443 — нет |
| Invalid subscription content | Подписка не парсится клиентом | Проверить логи: docker logs remnawave-subscription-page --tail=20. Убедиться что REMNAWAVE_PANEL_URL и REMNAWAVE_API_TOKEN заданы |
Вариант 1 — через меню рекомендуется
Если скрипт уже установлен и запущен:
Главное меню → 5) Обновить скрипт
Скачивает архив с GitHub и обновляет все модули lib/*.sh.
Вариант 2 — полная переустановка с нуля
Если скрипта ещё нет или нужна чистая установка:
mkdir -p /root/lib
for mod in common panel telemt hysteria migrate; do
curl -fsSL "https://raw.githubusercontent.com/stump3/server-manager/main/lib/${mod}.sh" \
-o "/root/lib/${mod}.sh"
done
curl -fsSL "https://raw.githubusercontent.com/stump3/server-manager/main/server-manager.sh" \
-o /root/server-manager.sh && chmod +x /root/server-manager.sh
bash /root/server-manager.sh
Вариант 3 — обновить один модуль
Если нужно обновить только конкретный компонент:
# Один модуль curl -fsSL "https://raw.githubusercontent.com/stump3/server-manager/main/lib/panel.sh" \ -o /root/lib/panel.sh # Или все модули через архив curl -fsSL https://github.com/stump3/server-manager/archive/refs/heads/main.tar.gz \ | tar -xz --strip-components=2 -C /root/lib server-manager-main/lib
rp хранится отдельно в /usr/local/bin/remnawave_panel и не обновляется автоматически.После обновления модулей применить изменения:
Главное меню → Remnawave Panel → Управление → 12) Переустановить скрипт (rp)
Система и порты
| Параметр | Значение |
|---|---|
| ОС | Ubuntu 20.04+ / Debian 11+ |
| Права | root |
| 443 TCP | Xray (selfsteal) + nginx — должен быть свободен до установки |
| 8443 UDP | Hysteria2 |
| 80 TCP | certbot — открывается на время SSL, потом закрывается |
| 2222 TCP | remnanode — только из Docker сети 172.30.0.0/16 |
| Зависимости | Docker, docker-compose, jq, certbot, openssl |
RAM
| Компонент | RAM (факт) | Peak | Тип |
|---|---|---|---|
| remnawave (NestJS) | ~395 MB | ~400 MB | Docker |
| remnawave-db (Postgres) | ~50 MB + ~195 MB workers | ~260 MB | Docker |
| remnanode (Xray rw-core) | ~88 MB | ~90 MB | Docker |
| subscription-page | ~76 MB | ~80 MB | Docker |
| telemt | ~18 MB | ~84 MB | systemd |
| hysteria2 | ~17 MB | ~53 MB | systemd |
| nginx + redis | ~10 MB | ~15 MB | Docker |
| hy-webhook | ~1 MB | ~12 MB | systemd |
| Итого | ~850 MB | ~1 GB | — |
remnawave/backend:2 (включая eGames) показывают те же цифры — конфиги идентичны.Swap
Рекомендуется минимум 1 GB swap. При 2 GB RAM telemt и hysteria2 в пике уходят в swap (47–83 MB). Без достаточного swap OOM killer может убивать контейнеры.
fallocate -l 2G /swapfile chmod 600 /swapfile mkswap /swapfile swapon /swapfile echo '/swapfile none swap sw 0 0' >> /etc/fstab
Сравнение с eGames
| Аспект | eGames | server-manager |
|---|---|---|
| Обработка ошибок API | Подробные сообщения | || warn без деталей |
| Идемпотентность | Повторный запуск ломает | IS_INSTALLED + компоненты |
| Модульность | Монолит 5000+ строк | 5 модулей, curl|bash |
| SSH рефакторинг | Дублирование кода | ask_ssh_target/helpers |
| Валидация UUID | Проверка перед API | Частичная |
| API Client-Type header | X-Remnawave-Client-Type | X-Remnawave-Client-Type |