Что вы ставите и что нужно от VPS
3x-ui — веб-панель поверх Xray-core, а не отдельный протокол и не замена Xray. Панель управляет инбаундами, клиентами, лимитами трафика и сроками, отдаёт подписку, считает статистику, умеет телеграм-бота. Трафик гоняет Xray, его бинарь лежит внутри установки — в /usr/local/x-ui/bin/.
3x-ui GitHub: какой репозиторий брать. Единственный источник скрипта и образов — MHSanaei/3x-ui. Легаси-панель vaxilu/x-ui и форк FranzKafkaYu/x-ui — другие проекты: своя схема БД, свой набор транспортов, развитие давно остановилось. Ставить «x-ui» по гайдам 2022 года в 2026-м смысла нет — свежих транспортов (XHTTP и его режимов) там просто не будет.
| Ресурс | Минимум | Комфорт (100–300 активных) | Комментарий |
|---|---|---|---|
| RAM | 512 MB | 1–2 GB | панель и Xray нетребовательны к памяти |
| CPU | 1 vCPU | 2 vCPU | упор в шифрование на пиках, не в панель |
| Диск | 8–10 GB | 20 GB SSD | БД крошечная, место ест access.log |
| ОС | Debian 12, Ubuntu 22.04/24.04 | те же | RHEL-семейство и Arch поддержаны, но грабель при установке заметно больше |
| Архитектура | amd64 / arm64 | — | сборки под armv7/386/s390x в релизах встречаются, но набор ассетов меняется — сверяйтесь с Releases |
Что подготовить до того, как установить 3x-ui:
- root и systemd. Скрипт ставит системный юнит. В OpenVZ/LXC-контейнерах без systemd установка не заработает — это самая частая причина «скрипт отработал, сервис не стартует».
- Домен и A-запись — для TLS на панели и на странице подписки. На Cloudflare для домена, к которому подключаются клиенты, держите DNS only (серое облако): проксирующий режим ломает Reality и прямой TLS.
- Свободные 80/443, если планируете выпускать сертификат встроенным acme в standalone-режиме. Системный nginx/apache с этих портов уберите заранее.
- Проверенный IP. Что подсеть не в чёрных списках и что хостер не режет исходящий трафик, дешевле выяснить до установки, чем после переноса клиентов.
Установка 3x-ui скриптом с GitHub
Штатный способ — установочный скрипт из репозитория. На чистом Debian/Ubuntu:
apt update && apt install -y curl socat tzdata sqlite3
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
Скрипт определяет ОС и архитектуру, тянет архив последнего релиза (не код из master), разворачивает панель, ставит systemd-юнит и CLI. В прод полезнее ставить конкретную версию — тогда обновление становится осознанным действием, а не побочным эффектом переустановки:
VERSION=v2.6.6 && bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
Актуальный тег смотрите в Releases на GitHub: версии здесь меняются часто, и копировать номер из статьи (в том числе из этой) не надо — он приведён как формат команды, а не как рекомендация.
Что оказалось на диске:
| Путь | Что это |
|---|---|
/usr/local/x-ui/ | бинарь панели; в bin/ — Xray и geo-файлы |
/etc/x-ui/x-ui.db | SQLite: инбаунды, клиенты, настройки панели. Главный объект бэкапа |
/usr/bin/x-ui | CLI-меню управления |
/etc/systemd/system/x-ui.service | юнит автозапуска |
/usr/local/x-ui/bin/config.json | конфиг Xray, который генерирует панель; править руками бессмысленно — перезапишется при следующем сохранении инбаунда |
Критичный момент первого запуска. На чистой установке скрипт генерирует случайные логин, пароль, порт панели и webBasePath и печатает их один раз в конце вывода. Скопируйте в менеджер паролей сразу. Если ваша версия повела себя иначе и пустила по admin/admin — меняйте креды немедленно: такие панели находят сканерами за часы.
Проверка после установки:
systemctl status x-ui --no-pager
ss -tlnp | grep -E 'x-ui|xray'
x-ui settings # текущий порт, webBasePath, логин
/usr/local/x-ui/bin/xray-linux-amd64 -version # для arm64 — xray-linux-arm64
Версию Xray стоит посмотреть сразу: именно она, а не версия панели, определяет, какие транспорты и поля вам доступны. Меню управления открывается командой x-ui (старт/стоп, обновление, сброс учётных данных, сертификаты, geo-файлы). Нумерация пунктов между релизами меняется — ориентируйтесь на названия, а не на номер.
Установка 3x-ui в Docker
Docker выбирают ради изоляции от системы и предсказуемого отката по тегу образа. Функционально панель та же.
mkdir -p /opt/3x-ui && cd /opt/3x-ui
docker-compose.yml:
services:
3x-ui:
image: ghcr.io/mhsanaei/3x-ui:latest
container_name: 3x-ui
hostname: node1
volumes:
- $PWD/db/:/etc/x-ui/
- $PWD/cert/:/root/cert/
environment:
XRAY_VMESS_AEAD_FORCED: "false"
XUI_ENABLE_FAIL2BAN: "true"
tty: true
network_mode: host
restart: unless-stopped
docker compose up -d && docker compose logs -f
Ловушка №1 — сеть. network_mode: host здесь не прихоть: инбаунды в 3x-ui создаются динамически, и при bridge-сети каждый новый порт пришлось бы дописывать в ports: и пересоздавать контейнер. Симптом типовой: панель открывается, инбаунд создан, клиенты не подключаются. Если host-режим недоступен, порты публикуются явно и диапазон лучше зарезервировать заранее:
ports:
- "2053:2053" # панель
- "443:443" # инбаунд
- "25454:25454" # запасной инбаунд
Ловушка №2 — файрвол. С network_mode: host контейнер живёт в сетевом стеке хоста, и правила ufw работают как обычно. А вот при публикации через ports: Docker пишет свои правила в цепочку DOCKER до пользовательских, и «закрытый» в ufw порт остаётся доступен из интернета. Ограничивать такие порты нужно через DOCKER-USER:
iptables -I DOCKER-USER -p tcp --dport 2053 '!' -s 203.0.113.7 -j DROP
Правило не переживёт перезагрузку без iptables-persistent — либо ставьте пакет, либо возвращайтесь к host-режиму.
Отличия в эксплуатации:
- CLI живёт внутри контейнера:
docker exec -it 3x-ui x-ui; - обновление —
docker compose pull && docker compose up -d --force-recreate(данные в./db/не трогаются), откат — фиксацией тега вместоlatest; - бэкап — файл
./db/x-ui.dbна хосте; XUI_ENABLE_FAIL2BAN: "true"поднимает fail2ban внутри контейнера; он нужен для IP-лимитов на клиента, а те, в свою очередь, требуют включённогоaccess.logу Xray.
Первый вход и минимальная настройка
Адрес панели: http://<IP>:<PORT>/<webBasePath>/. Без базового пути будет 404 — это не поломка, а штатное поведение: панель намеренно не отвечает на корень, чтобы не отдаваться массовым сканерам.
- Panel Settings. Нестандартный порт, свой длинный
webBasePath. После сохранения панель перезапускается — заходить уже по новому адресу. - Первый инбаунд. Inbounds → Add Inbound: VLESS, транспорт по вашей стратегии — Reality для прямых подключений, XHTTP, если вход будет за CDN. Порт инбаунда откройте и в файрволе ОС, и в security group хостера: это два независимых слоя.
- Подписка. Panel Settings → Subscription: включить, задать порт (по умолчанию
2096), путь и домен. Клиент получает ссылку видаhttps://sub.example.com:2096/sub/<subId>— это удобнее раздачи сырыхvless://: при смене хоста конфиг обновится у всех разом. - Логи Xray.
access.logнужен для IP-лимитов и разбора инцидентов, но растёт быстро. Включаете — сразу ставьте ротацию (путь берите тот, что указан в Xray Settings → Log):
cat >/etc/logrotate.d/xray <<'EOF'
/usr/local/x-ui/bin/access.log {
daily
rotate 3
compress
copytruncate
missingok
notifempty
}
EOF
copytruncate здесь обязателен: Xray не переоткрывает файл по сигналу, и после обычного rotate продолжит писать в удалённый inode — место не освободится.
- Проверка на живом клиенте. Создайте тестового пользователя, импортируйте подписку в Happ / v2rayN / Streisand и параллельно смотрите
journalctl -u x-ui -f— там видно и панель, и вывод Xray.
Мелочь, которая иногда даёт больше, чем тюнинг конфига, — BBR на длинных RTT:
printf 'net.core.default_qdisc=fq\nnet.ipv4.tcp_congestion_control=bbr\n' >>/etc/sysctl.conf && sysctl -p
По наблюдениям, заметный эффект даёт на дальних маршрутах и потерях; на коротком плече разница в пределах погрешности.
Защита панели: чек-лист того же дня
Панель 3x-ui — это полный доступ к сервису: клиенты, ключи, конфиги. Открытая наружу панель компрометируется быстро, поэтому список выполняется сразу, а не «потом».
1. Логин, пароль, секрет-токен. В меню x-ui есть сброс учётных данных. Пароль — длинный, из менеджера, не переиспользованный.
2. Свой порт и длинный webBasePath. Путь в 24–32 случайных символа — самая дешёвая защита от массовых сканеров:
head -c 24 /dev/urandom | base64 | tr -d '/+='
3. TLS на панель. Без него пароль и токены ходят открытым текстом. В меню x-ui есть выпуск сертификата через acme.sh: файлы кладутся в /root/cert/<domain>/, дальше в Panel Settings указываются пути к fullchain.pem и privkey.pem, затем systemctl restart x-ui.
4. Файрвол — важнее всего остального. Порт панели не должен быть доступен всему интернету:
ufw default deny incoming
ufw allow 22/tcp
ufw allow from 203.0.113.7 to any port 2053 proto tcp # панель — только с вашего IP
ufw allow 443/tcp # порт инбаунда для клиентов
ufw enable
Правила ufw не отменяют облачный security group у хостера — настраивайте оба слоя. И помните про DOCKER-USER, если панель в контейнере с опубликованными портами.
5. Лучший вариант — панель вообще не наружу. В Panel Settings можно задать listen IP 127.0.0.1 и ходить через SSH-туннель:
ssh -L 2053:127.0.0.1:2053 root@node1
# затем http://127.0.0.1:2053/<webBasePath>/ в браузере
6. 2FA и телеграм-бот. Двухфакторка (TOTP) в свежих версиях включается в настройках панели. У бота ограничьте admin-id своим chat id — иначе он отвечает командами кому угодно.
7. Бэкап БД по cron. Копировать живой SQLite через cp рискованно — можно поймать несогласованный снимок. Корректно:
cat >/usr/local/bin/xui-backup.sh <<'EOF'
#!/bin/bash
set -e
install -d /root/xui-backup
sqlite3 /etc/x-ui/x-ui.db ".backup '/root/xui-backup/x-ui-$(date +%F).db'"
find /root/xui-backup -name 'x-ui-*.db' -mtime +14 -delete
EOF
chmod +x /usr/local/bin/xui-backup.sh
В cron: 0 4 * * * /usr/local/bin/xui-backup.sh. Хранение — вне этого же сервера. В x-ui.db лежат все клиенты; потерять её — значит перевыпускать конфиги всей базе.
8. SSH. Вход по ключам, PermitRootLogin prohibit-password, парольная аутентификация выключена.
Типичные ошибки установки и как их чинить
| Симптом | Вероятная причина | Что делать | ||
|---|---|---|---|---|
| Панель не открывается, connection refused | сервис не поднялся или порт закрыт | systemctl status x-ui, `ss -tlnp \ | grep x-ui`, проверить ufw и SG хостера | |
404 по http://IP:PORT/ | забыт webBasePath | x-ui settings — подставить путь в URL | ||
Скрипт отработал, x-ui.service не стартует | контейнер без systemd (OpenVZ/LXC) | сменить тип VPS на KVM либо ставить docker-вариант | ||
| Панель отвалилась сразу после включения TLS | неверные пути к сертификату | обнулить пути в БД (см. ниже) и перезапустить | ||
| Забыты логин/пароль/порт | случайные креды из установки не сохранены | меню x-ui → сброс учётных данных; порт и путь — x-ui settings | ||
Failed to start x-ui | порт занят nginx/apache | `ss -tlnp \ | grep -E ':80\ | :443'`, остановить сервис или сменить порт панели |
| Инбаунды «красные», Xray не стартует | битый geo-файл или невалидный конфиг | `journalctl -u x-ui -n 200 --no-pager \ | grep -i xray, обновить geo из меню x-ui` | |
| В Docker клиенты не подключаются к новому инбаунду | bridge-сеть вместо host | network_mode: host либо явная публикация портов | ||
| VMess не работает, VLESS работает | расхождение системного времени (окно AEAD узкое) | timedatectl set-ntp true, проверить timedatectl status | ||
| Панель тормозит, «database is locked» | распухший access.log и статистика | ротация логов, отключить лишнее логирование | ||
| После обновления панель не поднялась | несовместимость релиза | откат: установка предыдущего тега скриптом либо фиксированный docker-тег |
Сброс путей к сертификату, когда панель после включения TLS перестала отвечать (частый способ закрыть себе дверь снаружи):
systemctl stop x-ui
sqlite3 /etc/x-ui/x-ui.db "update settings set value='' where key in ('webCertFile','webKeyFile');"
sqlite3 /etc/x-ui/x-ui.db "select key,value from settings;" # проверить, что строки существуют
systemctl start x-ui
Оговорка: update меняет только существующие строки. Если ключей в таблице нет, вывод select это покажет — значит, TLS был задан иначе и панель падает по другой причине, ищите её в journalctl -u x-ui.
Правило отладки: разделяйте слои. «Панель не открывается», «Xray не стартует» и «клиент не подключается» — три разные проблемы. Первая лечится в systemd и файрволе, вторая — в логах Xray, третья — в параметрах инбаунда и клиентской ссылке. Смешивать их — верный способ потратить вечер.
Чего установка не решает: IP ноды под белыми списками
Панель поднята, инбаунд работает — устойчивостью это ещё не стало. Слабое место не в 3x-ui, а в IP ноды: статичный адрес из хостинг-диапазона. Когда мобильный оператор переводит сеть в режим белого списка, наружу выпускается трафик только к разрешённым ресурсам, и ДЦ-подсети отваливаются целыми /24 — независимо от того, насколько аккуратно настроен Reality.
Архитектурный ответ — не менять панель, а вынести вход за whitelisted CDN: клиент идёт на «белый» домен edge, edge проксирует до вашей origin-ноды с 3x-ui. Что важно знать до того, как начнёте это строить:
- Рабочий транспорт через CDN — XHTTP в режиме
packet-up; Reality и Hysteria2 edge не пропускает. - У Yandex CDN нет POST, поэтому XHTTP-аплинк уходит методом GET, а GET разрешён только при
mode packet-up. Это не настройка по вкусу, а жёсткое условие: с другим режимом соединение через такой edge не поднимется. pathи блокextraна ноде и в клиенте должны совпадать точь-в-точь. Экзотические obfuscation-поля лучше не трогать вовсе: они ломают совместимость между версиями Xray, и клиент на другой сборке молча перестаёт подключаться.- Whitelist привязан к конкретной /24, а не к хостеру целиком, и статус меняется.
Детали XHTTP-инбаунда и ручной сборки клиентской ссылки для 3x-ui за CDN разобраны в отдельном материале. Здесь важен вывод для этапа установки: закладывайте архитектуру так, чтобы наружу смотрел заменяемый слой, а не единственный сервер с вашей базой клиентов. Если поднимать и держать собственный whitelisted edge не хочется, Clearway даёт такой «белый» вход как сервис: нода с 3x-ui остаётся вашей, меняется только то, куда ходит клиент.