Установщик одной командой для VPS-ноды на Remnawave: TLS-фронт на nginx для XHTTP-профиля Xray, сайт-заглушка и переустановка Remnawave Node в Docker.
One command to turn a fresh Debian/Ubuntu VPS into a Remnawave node behind nginx + Let's Encrypt, with an XHTTP Xray profile and a static site on every other path.
Текущая версия: 1.6.0 — публичный HTTPS-порт теперь свой (случайный свободный, не 443; --https-port N), вместе с установкой ставится Psiphon (egress для Gemini, --no-psiphon — пропустить), в правило Gemini профиля добавлены 5 доменов. Установки до 1.6.0 остаются на 443. Подробности — в CHANGELOG.md.
На VPS, от root (нужны Debian или Ubuntu):
bash <(curl -fsSL https://raw.githubusercontent.com/goji-app/goji-node-setup/main/xhttp-tls/install.sh)Скрипт сам спросит всё нужное:
| Вопрос | Что ввести |
|---|---|
| Домен сервера | домен, A-запись которого уже указывает на этот VPS |
SECRET_KEY ноды |
ключ из Remnawave: Nodes → ваша нода (при переустановке Enter оставит текущий) |
NODE_PORT |
порт API ноды для панели, по умолчанию 2222 |
| IP панели | с него будет разрешён доступ к NODE_PORT (Enter — не создавать правило файрвола) |
| для Let's Encrypt (можно пропустить) |
Все ответы можно передать флагами, тогда вопросов не будет:
bash install.sh node.example.com --secret-key '...' --node-port 2222 --panel-ip 203.0.113.10 --email me@example.comПеред запуском: нода добавлена в панели, домен указывает на сервер, порт 80 свободен для nginx. Порт 443 не нужен: HTTPS-порт скрипт выбирает сам (случайный свободный), его можно задать --https-port N.
flowchart LR
C[Клиент] -- "TLS 1.2/1.3, h2, :HTTPS-порт" --> N[nginx]
N -- "/случайный/путь/ (h2c)" --> X["Xray XHTTP<br/>127.0.0.1:10443"]
N -- "любой другой путь" --> S[Статический сайт-заглушка]
N -. "чужой SNI / голый IP" .-> D[обрыв рукопожатия]
P[Панель Remnawave] -- "NODE_PORT, только с IP панели" --> R[Remnawave Node]
R --- X
- nginx держит публичный HTTPS-порт (случайный свободный в диапазоне 20000–32000, не 443; хранится в
/etc/goji-node/install.conf) и сертификат Let's Encrypt, а Xray слушает только127.0.0.1. Порт 80 остаётся для проверки Let's Encrypt и редиректа (наhttps://домен:порт). - Путь XHTTP проксируется в Xray, все остальные пути отдают статический сайт. На каждой новой установке путь случайный (вида
/assets/sync/3f9a1c0b7d2e/), при переустановке сохраняется прежний; задать свой —--path, сменить на новый случайный —--path random(после этого обновите профиль и хост в Remnawave). Общий путь/api/v2/telemetry/из старых версийgoji-node checkпомечает предупреждением: по нему ноду можно найти одним запросом. - Запрос с неизвестным именем сервера или по голому IP получает обрыв TLS-рукопожатия.
- Remnawave Node запускается в Docker с
network_mode: host, чтобы Xray и nginx видели друг друга на127.0.0.1.
| Шаг | Действие |
|---|---|
| 1. Вопросы | домен, SECRET_KEY, порт API, IP панели, e-mail, выбор сайта-заглушки из меню — самый первый вопрос (если не задан --template). Работает и через curl … | bash: ввод читается с терминала |
| 2. Пакеты | nginx, certbot, curl и зависимости из репозиториев дистрибутива |
| 3. Проверка DNS | предупреждает, если домен указывает не на этот сервер, иначе сертификат не выпустится |
| 4. Заглушка | разворачивает выбранный шаблон сайта в /var/www/decoy; все шаблоны остаются в /usr/share/goji-node/templates для смены заглушки без переустановки |
| 5. nginx :80 | отвечает на проверку Let's Encrypt, всё остальное перенаправляет на HTTPS (с номером порта); при активном ufw открывает 80 и HTTPS-порт |
| 6. Сертификат | выпуск через webroot и автопродление с перезагрузкой nginx, после чего выполняется проверка certbot renew --dry-run. Если папка сертификата осталась без конфигурации продления, её старые файлы переносятся в /root/letsencrypt-backup-… |
| 7. Remnawave Node | ставит Docker при необходимости, сохраняет старый docker-compose.yml в .bak-<дата>, пишет новый, скачивает образ remnawave/node:latest, закрепляет его по digest (image: remnawave/node@sha256:…) и запускает |
| 8. Ожидание профиля | ждёт, пока в панели профиль ноды переключат на XHTTP (Xray слушает 127.0.0.1:10443, HTTPS-порт свободен). До этого nginx на HTTPS-порту не включается, а старый профиль продолжает работать |
| 9. nginx, HTTPS-порт | включает TLS-фронт |
| 10. Самопроверка | заглушка отвечает кодом 200, Xray слушает внутренний порт |
| 11. Профиль для Remnawave | выводит готовый JSON профиля (с вашими портом и путём) для копирования в Remnawave → Config Profiles; он же сохраняется в /etc/goji-node/remnawave-profile.json. Если профиль ещё не применён, JSON выводится до ожидания, чтобы его можно было вставить сразу |
Скрипт можно запускать повторно: уже выпущенный и автопродляемый сертификат он не трогает, установленная заглушка остаётся прежней.
| Флаг | По умолчанию | Назначение |
|---|---|---|
<домен> |
спросит | домен ноды (первый аргумент) |
--email |
— | e-mail для Let's Encrypt |
--secret-key |
спросит | SECRET_KEY ноды |
--node-port |
2222 |
порт API ноды для панели |
--panel-ip |
— | IP панели; для NODE_PORT в ufw создаётся правило только для него, а оставшееся от прежней установки правило «всем» (ALLOW Anywhere) снимается |
--xray-port |
10443 |
внутренний порт Xray на 127.0.0.1 |
--https-port |
случайный свободный (20000–32000) | публичный HTTPS-порт nginx. Не 80, не порты Xray/ноды/SSH. При переустановке остаётся прежний (у установок до 1.6.0 — 443); смена порта требует обновить host в Remnawave. --https-port 443 — вернуть стандартный |
--psiphon / --no-psiphon |
ставит на новой установке | vps-psiphon: Psiphon-туннель в Docker как egress для доменов Gemini (SOCKS5 на адресе docker0, порт случайный, HTTP-прокси не публикуется). Уже установленный vps-psiphon скрипт не трогает; --psiphon переустановит. Сбой Psiphon установку не прерывает |
--psiphon-args |
— | дополнительные параметры для vps-psiphon в кавычках, например --psiphon-args "--region DE --bulk-test-mb 0" |
--path |
случайный | путь XHTTP, начинается и заканчивается на /; random — новый случайный; без флага при переустановке остаётся прежний |
--template |
меню / random |
заглушка: random, analytics, blog, docs, saas, freelancer, resume, creative, grayscale, new-age, agency, livecams. Без флага на терминале показывается меню выбора, без терминала берётся случайная |
--wait |
900 |
сколько секунд ждать переключения профиля в панели |
--skip-node |
— | не трогать Remnawave Node, настроить только nginx и сертификат |
--skip-hardening |
— | не применять тюнинг и защиту (BBR/fq, tc, ZRAM, UFW, Fail2ban, ICMP) |
--icmp-drop |
— | полностью блокировать входящий ping (по умолчанию лимит 5/сек) |
--traffic-control / --no-traffic-control |
спросить | блокировка сетей сканеров по публичным спискам (nftables), по желанию |
--admin-ip |
IP SSH-сессии | IP администратора, исключённый из блокировок Traffic Control, флаг можно повторять |
--upgrade-os / --no-upgrade-os |
спросить | обновить пакеты текущего релиза ОС (apt upgrade) перед установкой; план с удалениями отклоняется |
--check |
— | отчёт «компонент — статус» (то же: goji-node check) |
--fix |
— | найти ошибки и исправить безопасные автоматически (то же: goji-node fix [--dry-run]) |
--uninstall |
— | меню удаления компонентов установки (сайт, сертификат, нода, защита, тюнинг, SSH, Fail2ban, правила UFW, goji-node); подробности в INSTALL.md |
--settings |
— | показать настройки, с которыми сервер был установлен (то же: goji-node settings). На уже настроенном сервере запуск без параметров сначала предлагает меню: проверка, настройки, goji-node, переустановка |
--resume |
— | повторить установку с сохранёнными параметрами |
--version |
— | версия установщика |
--update / --check-update |
— | скачать новую версию с GitHub / только проверить (то же: goji-node update) |
--no-update-check |
— | отключить проверку и уведомления об обновлениях |
--ssh-port |
определяется | порт SSH для UFW и Fail2ban |
--ssh-keys-only / --no-ssh-keys-only |
спросить | вход по SSH только по ключу: отключает пароль и keyboard-interactive, root по паролю → только по ключу. Включается, только если у пользователя сессии есть ключи в authorized_keys и сама сессия вошла не по паролю |
--allow-port |
— | дополнительный порт для UFW (8443, 8443/udp), флаг можно повторять |
xhttp-tls/xray-node-profile.json — универсальный профиль, его достаточно один раз создать в панели и назначить нодам:
- inbound
XHTTP-TLS:127.0.0.1:10443, VLESS, транспортxhttp,security: none(TLS заканчивается на nginx); - блокируются: QUIC (UDP 443), исходящий SMTP на порт 25, BitTorrent, частные адреса и домены, реклама и трекеры;
- outbound
psiphon-out(SOCKS5 Psiphon) и balancer для доменов Gemini и связанных сервисов Google AI (в том числеgeminiweb-pa,waa-pa,appsgenaiserver-pa,signaler-pa,ogads-paнаclients6.google.com) с автоматическим переключением наDIRECT, если прокси не отвечает; адрес и порт Psiphon установщик подставляет сам при показе профиля (goji-node profile); - журнал доступа выключен.
Если Psiphon не установлен (--no-psiphon), трафик для этих доменов просто пойдёт напрямую. Psiphon не принимает UDP; QUIC (UDP 443) в профиле блокируется, поэтому до Psiphon доходит только TCP.
| Поле | Значение |
|---|---|
| Адрес / порт | <домен> / HTTPS-порт из вывода установщика (goji-node profile; у установок до 1.6.0 — 443) |
| Network | xhttp |
| Path | путь из вывода установщика / goji-node profile (без пробела в конце) |
| Mode | auto |
| Security | tls |
| SNI | <домен> |
| ALPN | h2 |
| Fingerprint | firefox или chrome |
| Flow | пусто (Vision с XHTTP не используется) |
Путь должен совпадать на сервере и в хосте символ в символ. Лишний пробел в конце поля Path в панели не виден, но клиент после этого получает «недоступен». Лучше набирать путь вручную, а не копировать.
Скрипт разворачивает один из шаблонов из xhttp-tls/templates/:
| Имя | Что это | Источник и лицензия |
|---|---|---|
analytics |
Quillmetric — сервис веб-аналитики: график, источники, тарифы | написан для этого репозитория; фото: THIRD-PARTY-NOTICES (MIT) |
blog |
Lantern & Road — тревел-блог: 6 статей с фото, страница «О блоге» | написан для этого репозитория; фото: THIRD-PARTY-NOTICES (MIT) |
docs |
Postline — документация API: боковое меню, поиск, примеры кода | написан для этого репозитория; фото: THIRD-PARTY-NOTICES (MIT) |
saas |
Stackpulse — лендинг сервиса мониторинга: панель, тарифы, отзывы | написан для этого репозитория; фото: THIRD-PARTY-NOTICES (MIT) |
freelancer |
Maren Holt — портфолио фотографа: 12 фото, лайтбокс | написан для этого репозитория; фото: THIRD-PARTY-NOTICES (MIT) |
resume |
Daniel Okafor — резюме backend-разработчика | написан для этого репозитория; фото: THIRD-PARTY-NOTICES (MIT) |
creative |
Oak & Ledger — магазин изделий ручной работы с корзиной | написан для этого репозитория; фото: THIRD-PARTY-NOTICES (MIT) |
grayscale |
Fjellro — горные домики в Норвегии, запрос брони | написан для этого репозитория; фото: THIRD-PARTY-NOTICES (MIT) |
new-age |
Habitual — лендинг приложения-трекера привычек | написан для этого репозитория; фото: THIRD-PARTY-NOTICES (MIT) |
agency |
Fieldstone — студия брендинга и веб-дизайна: услуги, работы, команда | написан для этого репозитория; фото: THIRD-PARTY-NOTICES (MIT) |
livecams |
сайт «Open Window»: 24 YouTube-трансляции (камеры городов, МКС и NASA, зоопарки, аквариумы, дикая природа, новости); плеер на странице, при недоступном эфире переключается на следующий | написан для этого репозитория |
Заглушку можно выбрать в меню при установке или флагом --template; без терминала выбирается случайная. При повторном запуске сохраняется выбранный шаблон, сменить его позже: goji-node decoy (меню) или goji-node decoy <имя>. Шаблоны Start Bootstrap взяты из dist/ репозиториев, изображения пересжаты, рекламные тексты и ссылки на сайт-источник заменены, скрипт стороннего сервиса форм удалён. Часть шаблонов подгружает Bootstrap-скрипт, шрифты и иконки с публичных CDN (jsDelivr, Google Fonts, Font Awesome), как и оригиналы. Лицензии сторонних шаблонов лежат рядом с ними и отдаются как LICENSE.txt.
Свой шаблон: положите папку в xhttp-tls/templates/<имя>/ и выполните python xhttp-tls/build.py. Он заново встроит шаблоны в install.sh, чтобы скрипт оставался одним самодостаточным файлом.
goji-node decoy # меню выбора среди установленных шаблонов
goji-node decoy blog # поставить конкретную (или random)
goji-node decoy --list # список шаблонов, текущая отмечена
goji-node decoy --pull # скачать свежие шаблоны из репозитория (codeload.github.com)
goji-node decoy --from ~/mysite # свой сайт: каталог, .tar.gz/.zip или https-ссылка на архив
goji-node decoy --undo # вернуть предыдущий сайтСвой сайт должен содержать index.html (в корне или в единственной вложенной папке). Он сохраняется как custom-<имя>, переживает install.sh --resume и не затрагивается --pull. Архивы с путями ../абсолютными отклоняются, ссылки удаляются, максимум 200 МБ. Перед каждой заменой делается резервная копия в /var/backups/goji-node/ (5 последних). --pull обновляет только шаблоны сайтов, не сам скрипт и не настройки сервера. После install.sh --resume версии стандартных шаблонов возвращаются к встроенным в скрипт.
Скрипт сам проверяет, вышла ли новая версия в этом репозитории (файл xhttp-tls/VERSION), и сообщает об этом:
в меню goji-node, в конце goji-node check, при запуске install.sh и при входе root по SSH.
Проверка идёт не чаще раза в сутки (раз в сутки обновляет кэш cron), запрос только к raw.githubusercontent.com.
goji-node update # показать, что изменилось, скачать новый install.sh и предложить применить
goji-node update --check # только проверить (код выхода 10, если есть обновление)
goji-node update --notes # только список изменений
goji-node update --yes # без вопросов
goji-node update --no-apply # скачать, но не применять
bash install.sh --update # то же, если goji-node ещё не установлен
goji-node update --off # отключить проверки и уведомления (--on — включить снова)Скачанный файл проверяется (размер, bash -n, версия внутри, sha256 из install.sh.sha256) и сохраняется как
/usr/share/goji-node/install.sh; предыдущая версия — install.sh.prev. «Применить» — это повторный проход
install.sh --resume --wait 0 с сохранёнными настройками. Контрольная сумма лежит в том же репозитории,
поэтому защищает от повреждённой загрузки, но не от подмены самого репозитория. Версии до 1.4.0 команды update
не имеют: один раз скачайте новый install.sh вручную и запустите bash install.sh --resume.
CHANGELOG.md история версий
xhttp-tls/
├── install.sh готовый установщик (шаблоны встроены, один файл)
├── install.template.sh исходник установщика
├── VERSION, install.sh.sha256 версия и контрольная сумма (для goji-node update; создаёт build.py)
├── build.py встраивает templates/ в install.template.sh -> install.sh
├── templates/ сайты-заглушки (+ THIRD-PARTY-NOTICES.md)
├── psiphon/ vps-psiphon (MIT) без изменений: вшивается в install.sh, README с источником и SHA-256
├── nginx-template.conf эталон конфига nginx, который собирает скрипт
├── xray-node-profile.json универсальный профиль Xray для Remnawave
└── INSTALL.md краткая памятка
- сервер с Debian или Ubuntu (используется
apt), root-доступ; - домен с A-записью на сервер, порт 80 свободен (443 не нужен);
- установленная панель Remnawave и добавленная в ней нода (нужен
SECRET_KEY); - на ноде ничего не должно слушать порт 80 и выбранный HTTPS-порт; 443 можно оставить старому профилю.
- Psiphon нужен Docker (ставится вместе с нодой) и зарубежный сервер: на сервере внутри страны, чьи ограничения вы обходите, его ставить нельзя. Образ
swarupsengupta2007/psiphon:latestсторонний и не закреплён по версии; примерно 35 ГБ трафика в месяц на проверки (--psiphon-args "--bulk-test-mb 0"отключает).
| Симптом | Причина и решение |
|---|---|
live directory exists for … от certbot |
старая папка сертификата без конфигурации продления: скрипт переносит её в /root/letsencrypt-backup-… и выпускает сертификат заново, запустите его ещё раз |
Xray падает с address already in use на HTTPS-порту |
nginx занял порт, который нужен профилю (бывает при --https-port 443, пока работает старый профиль): скрипт включает nginx на HTTPS-порту только после появления Xray на 127.0.0.1:10443. Если это уже случилось, уберите /etc/nginx/conf.d/xhttp-tls.conf и выполните systemctl reload nginx |
| Клиенты не подключаются после смены порта | в host Remnawave остался старый порт: возьмите новый из goji-node profile |
| Клиент пишет «недоступен» | проверьте путь в хосте (пробел в конце), ALPN h2, пустой Flow |
| Сертификат не выпускается | домен указывает не на этот сервер, либо порт 80 закрыт файрволом |
- Скрипт переустанавливает контейнер Remnawave Node на этой машине: нода будет недоступна около минуты. Старый compose-файл сохраняется как
/opt/remnanode/docker-compose.yml.bak-<дата>, но подключённые папки и соседние сервисы из него в новый не переносятся, сверьте их. SECRET_KEYзаписывается в/opt/remnanode/docker-compose.ymlс правами только для root.- Установщик настраивает только описанное выше. Остальная конфигурация сервера (SSH, файрвол, сеть) остаётся на вас.
- Это установщик инфраструктуры. Его не запускали на всех поддерживаемых версиях ОС; прежде чем раскатывать на все ноды, проверьте на одной.
- Репозиторий не содержит ключей, UUID пользователей и адресов реальных нод. Не добавляйте их в публичный репозиторий.
MIT, © 2026 goji-app. Сторонние шаблоны в xhttp-tls/templates/ распространяются под своими лицензиями (тоже MIT), их тексты лежат в папках шаблонов.