Nezha на VPS: мониторинг всех своих серверов на одной странице
Когда серверов становится больше двух, начинается бардак: у каждого своя панель хостера, свой счётчик трафика и свой пароль. Nezha собирает всё это в одну вкладку и, главное, сам пишет в Telegram, когда сервер упал или когда месячный пакет трафика подходит к концу.
Задача: несколько серверов и ни одной общей картины
Типичная ситуация человека, который несколько лет что-то арендует. Один VPS в России под сайт, один в Германии под VPN, один совсем дешёвый в Финляндии под бота и ещё старый где-то, про который вы уже забыли, но деньги за него списываются. Четыре разные панели управления, четыре логина, четыре способа посмотреть нагрузку.
Из-за этого регулярно случаются три вещи. Первая: сервер упал, а вы узнаёте об этом через час от пользователя. Вторая: на диске кончилось место — обычно из-за логов или бэкапов, которые никто не чистил, — и приложение молча перестало работать. Третья, самая обидная и самая частая у дешёвых зарубежных хостеров: кончился месячный лимит трафика. Дальше в зависимости от тарифа хостер либо режет скорость до неприличной, либо выставляет счёт за перерасход, либо просто выключает сервер.
Идея Nezha в одном предложении
На отдельный дешёвый сервер ставится веб-панель, на все остальные — крошечный агент, и все машины оказываются на одной странице: нагрузка, аптайм, расход трафика за расчётный период и уведомления, когда что-то пошло не так.
Nezha (哪吒 — имя персонажа китайской мифологии) развивается с 2019 года и живёт на GitHub под лицензией Apache 2.0. К августу 2026 у репозитория около 10 300 звёзд, актуальная версия панели — 2.3.7 от 17 августа 2026, агента — 2.3.3 от 12 августа. Релизы выходят буквально каждую неделю.
Все команды, конфиги и сообщения об ошибках ниже проверены на реально развёрнутой установке версии 2.3.7 с агентом 2.3.3 — включая работу через Nginx с сертификатом и подключение агента по HTTPS.
Nezha или что-то другое: короткая развилка
Мониторинг — область, где легко взять слишком тяжёлый инструмент и бросить его через неделю. Четыре варианта и когда какой брать (про три из них у нас есть отдельные статьи):
| Инструмент | За чем следит | Когда брать |
|---|---|---|
| Uptime Kuma | сайты и порты снаружи, сертификаты | «жив ли мой сайт» — и больше ничего не нужно |
| Beszel | ресурсы машины и контейнеры Docker | один-два домашних сервера, нужна простота |
| Nezha | ресурсы, аптайм, трафик за период, проверки сайтов с каждого сервера | 3–10 арендованных VPS, часть с лимитом трафика |
| Prometheus + Grafana | любые метрики, включая метрики приложений | нужны свои метрики, запросы и сложные дашборды |
Ниша Nezha — ровно между «слишком просто» и «слишком сложно». Prometheus с Grafana умеют больше, но там нужно самому придумать экспортеры, метки, запросы и дашборды, а потом всё это поддерживать. Nezha даёт фиксированный набор из коробки: поставил агент — сервер появился на странице со всеми графиками. За это платите отсутствием гибкости: своих метрик сюда не завезти.
Как это устроено
Две части. На одном сервере — панель (dashboard): веб-интерфейс, база SQLite и хранилище графиков. На каждом наблюдаемом сервере — агент: одна программа на 18 МБ, которая раз в три секунды отправляет панели состояние машины.
Соединение всегда начинает агент: он сам подключается к панели и держит соединение открытым. Это удобно — на наблюдаемых серверах не нужно открывать ни одного входящего порта. Открытый порт нужен только у панели.
Важная особенность: всё на одном порту
Начиная со второго поколения Nezha веб-интерфейс и канал агентов живут на одном и том же порту — по умолчанию 8008. Браузер ходит туда обычным HTTP, а агенты — по протоколу gRPC, который работает поверх HTTP/2. Отсюда следует главное практическое правило всей статьи: обратный прокси перед панелью обязан уметь HTTP/2 и gRPC. Если этого нет, панель в браузере откроется идеально, а агенты не подключатся никогда. Ниже разберём это подробно — там же лежит самая частая ошибка при установке.
По той же причине адрес для агентов нельзя прятать за CDN: обычный CDN не пропускает gRPC-соединения либо рвёт их по таймауту. Об этом прямо предупреждает и официальная документация. Если очень хочется CDN для красивой публичной страницы, заводите два домена — один для браузеров через CDN, второй для агентов напрямую.
Что понадобится
- Отдельный VPS под панель. Хватает 1 vCPU, 1 ГБ памяти и 10–15 ГБ диска — измеренный расход самой панели ниже 100 МБ памяти. Брать отдельную машину, а не подселять панель к боевым, стоит по простой причине: мониторинг, который падает вместе с наблюдаемым сервером, бесполезен ровно тогда, когда нужен.
- Ubuntu 24.04 LTS на этом сервере (команды ниже — под неё; на 22.04 всё то же самое).
- Домен и A-запись на IP сервера панели. В статье используется
nezha.example.com— подставляйте свой. Домен нужен обязательно: без него не будет сертификата, а без сертификата секрет агента полетит по сети открытым текстом. - Наблюдаемые серверы — любые машины на Linux, macOS или Windows, к консоли которых у вас есть доступ. Ничего входящего на них открывать не нужно.
Проверьте, что A-запись уже разошлась, — иначе certbot не выдаст сертификат:
dig +short nezha.example.com @8.8.8.8В ответ должен прийти IP сервера панели. Если пусто — подождите, записи расходятся от нескольких минут до часа. Если команды dig под рукой нет, подойдёт любой онлайн-сервис проверки DNS.
Шаг 1. Установка панели
У проекта есть официальный установочный скрипт с меню, но мы поставим панель вручную из двух файлов. Так понятно, что именно происходит, легко делать бэкап и — главное — можно сразу закрыть порт от интернета, чего скрипт не делает.
Ставим Docker на сервер панели:
sudo apt update
curl -fsSL https://get.docker.com | sudo shСоздаём каталог. Путь /opt/nezha/dashboard — тот же, что использует официальный установщик, так что документация проекта будет совпадать с вашей установкой:
sudo mkdir -p /opt/nezha/dashboard/data
cd /opt/nezha/dashboardПервый файл — описание контейнера:
services:
dashboard:
image: ghcr.io/nezhahq/nezha:v2.3.7
container_name: nezha-dashboard
restart: always
ports:
- "127.0.0.1:8008:8008"
volumes:
- ./data:/dashboard/dataПрефикс 127.0.0.1 в строке портов обязателен. Nezha стартует с учётной записью admin и паролем admin, без всякого мастера начальной настройки: панель полностью работоспособна с первой секунды. Если опубликовать порт наружу, у вас будет открытый в интернет административный интерфейс со стандартным паролем. Строка выше говорит Docker пускать в контейнер только с самого сервера.
Второй файл — конфигурация панели. Он должен лежать именно в подкаталоге data:
listen_port: 8008
site_name: "Мои серверы"
language: ru_RU
location: Europe/Moscow
# адрес, по которому агенты будут подключаться к панели
install_host: nezha.example.com:443
tls: true
# реальный IP агента берём из заголовка, который проставит Nginx
agent_real_ip_header: nz-realip
# хранилище истории — без него не будет графиков за 7 и 30 дней
tsdb:
data_path: "data/tsdb"
retention_days: 30
min_free_disk_space_gb: 1
max_memory_mb: 256Четыре места здесь неочевидны, и каждое решает реальную проблему:
location: Europe/Moscow— по умолчанию Nezha живёт по Шанхаю, и это не косметика. От этого значения зависит, в котором часу сработают задачи по расписанию. Часовой пояс на самих графиках берётся из браузера, тут всё в порядке.install_hostиtls: true— адрес и режим подключения агентов. Мы сразу указываем домен и порт 443, потому что агенты будут ходить через Nginx по HTTPS. Эти же поля есть в интерфейсе: «Системные настройки» → «Адрес подключения агента» и «Использовать TLS для подключения агента».agent_real_ip_header: nz-realip— без этой строки панель будет считать, что все ваши серверы находятся по одному адресу: адресу самого Nginx. Мы это проверили: до настройки заголовка в карточке сервера светился внутренний IP прокси, после — реальный адрес агента.- Блок
tsdb— хранилище истории по умолчанию выключено. Пока его нет, на странице сервера доступен только режим «сейчас», а вкладки «1 день», «7 дней» и «30 дней» заблокированы. Внутри — встроенная VictoriaMetrics, отдельно ставить ничего не нужно.
Запускаем и смотрим журнал:
cd /opt/nezha/dashboard
sudo docker compose up -d
sudo docker compose logs --tail 20В журнале должны быть две строки — про хранилище и про запуск:
NEZHA>> TSDB initialized successfully
NEZHA>> Dashboard::START ON :8008Если вместо первой строки написано TSDB is disabled (tsdb.data_path not configured) — блок tsdb не попал в конфиг: проверьте отступы (два пробела) и то, что файл лежит в data/config.yaml, а не рядом с docker-compose.yaml.
Шаг 2. Первый вход и смена пароля
Порт закрыт от интернета, а пароль пока стандартный — значит, войти нужно так, чтобы наружу ничего не открывать. Для этого есть SSH-туннель: одна команда на вашем компьютере, которая пробрасывает порт сервера на ваш локальный.
ssh -L 8008:127.0.0.1:8008 root@IP_СЕРВЕРАПока это окно открыто, откройте в браузере http://127.0.0.1:8008/dashboard — это административная часть панели. Логин и пароль: admin / admin.
Первым делом — пароль. Нажмите на аватар в правом верхнем углу, выберите «Профиль» (Profile), заполните «Старый пароль» и «Новый пароль», нажмите «Обновить профиль». Пароль берите длинный, от 18 символов: панель Nezha — это root-доступ ко всем серверам, которые вы к ней подключите.
Интерфейс должен быть на русском — это даёт строка language: ru_RU из конфига. Отдельные новые пункты перевод ещё не догнал и они остаются английскими. Язык при желании переключается и вручную: аватар → «Язык».
Туннель после этого можно закрывать (Ctrl+C в том окне) — дальше панель будет доступна по домену.
Шаг 3. Домен, Nginx и HTTPS
Ставим веб-сервер и клиент Let's Encrypt:
sudo apt install -y nginx certbot python3-certbot-nginxСоздаём конфиг сайта. Замените nezha.example.com на свой домен:
upstream nezha_dashboard {
server 127.0.0.1:8008;
keepalive 512;
}
server {
listen 80;
server_name nezha.example.com;
underscores_in_headers on;
# канал агентов: gRPC
location ^~ /proto.NezhaService/ {
grpc_set_header Host $host;
grpc_set_header nz-realip $remote_addr;
grpc_read_timeout 600s;
grpc_send_timeout 600s;
grpc_socket_keepalive on;
grpc_buffer_size 4m;
client_max_body_size 10m;
grpc_pass grpc://nezha_dashboard;
}
# живое обновление страницы, терминал и файлы: веб-сокет
location ~* ^/api/v1/ws/(server|terminal|file)(.*)$ {
proxy_pass http://127.0.0.1:8008;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header nz-realip $remote_addr;
proxy_set_header Origin https://$host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
# всё остальное: обычный веб-интерфейс
location / {
proxy_pass http://127.0.0.1:8008;
proxy_set_header Host $host;
proxy_set_header nz-realip $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
proxy_max_temp_file_size 0;
}
}Что здесь важно и почему именно так:
- Три блока
location, а не один. Агенты ходят по адресу/proto.NezhaService/и требуютgrpc_pass— обычныйproxy_passих не пропустит. Веб-сокет по/api/v1/ws/нужен, чтобы цифры на странице обновлялись сами и работал встроенный терминал. Всё прочее — обычное проксирование. underscores_in_headers on;— Nginx по умолчанию выбрасывает заголовки, в имени которых есть подчёркивание, а агент передаёт учётные данные именно такой парой:client_secretиclient_uuid. Свежие версии для надёжности дублируют их и в варианте через дефис, так что без этой строки, скорее всего, тоже заработает — но она стоит в официальном конфиге проекта, ничего не ломает и страхует от старых агентов.nz-realipво всех трёх блоках — тот самый заголовок, который мы указали в конфиге панели. Благодаря ему в карточках серверов видны их настоящие адреса.keepalive 512в блокеupstream— соединения агентов держатся часами, и без пула Nginx будет постоянно их переустанавливать.
Включаем конфиг и открываем порты. Файрвол настраиваем до выпуска сертификата: Let's Encrypt проверяет домен обращением на 80-й порт, и если он закрыт, сертификата не будет.
sudo ln -s /etc/nginx/sites-available/nezha /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enableТеперь сертификат:
sudo certbot --nginx -d nezha.example.comCertbot спросит почту, попросит согласиться с условиями и предложит включить перенаправление с HTTP на HTTPS — соглашайтесь. Он сам допишет в конфиг блок с сертификатом и настроит автопродление.
Обязательный шаг, о котором забывают все: включить HTTP/2
Certbot добавляет строку listen 443 ssl; # managed by Certbot — без HTTP/2. Панель в браузере после этого работает прекрасно, а агенты не подключатся вообще никогда: gRPC живёт только поверх HTTP/2. Мы специально воспроизвели эту ситуацию, и в журнале агента она выглядит так:
transport: authentication handshake failed: remote error: tls: no application protocol
Лечится дописыванием одного слова.
Откройте sudo nano /etc/nginx/sites-available/nezha и найдите строку, которую дописал certbot. Допишите в неё одно слово — http2:
# было:
listen 443 ssl; # managed by Certbot
# стало:
listen 443 ssl http2; # managed by CertbotЕсли рядом есть такая же строка для IPv6 — listen [::]:443 ssl ipv6only=on; — допишите http2 и в неё. Затем проверьте и перезагрузите:
sudo nginx -t
sudo systemctl reload nginxНа Nginx версии 1.25.1 и новее команда nginx -t выдаст предупреждение the "listen ... http2" directive is deprecated. Это именно предупреждение, а не ошибка: конфиг рабочий, и мы это проверили. Если предупреждение раздражает, уберите http2 из строки listen и добавьте отдельной строкой http2 on; внутри блока server — но только на 1.25.1 и новее; в Nginx из репозитория Ubuntu 24.04 (это версия 1.24) такой директивы ещё нет и конфиг не запустится.
Теперь две проверки, которые надо сделать до того, как идти ставить агенты. Первая — что сайт вообще отдаётся по HTTP/2:
curl -s -o /dev/null -w 'HTTP-версия: %{http_version}\n' https://nezha.example.com/Должно быть HTTP-версия: 2. Если 1.1 — слово http2 в строку listen не попало или Nginx не перезагрузился, и агенты не подключатся.
Вторая — что адрес, по которому стучатся агенты, доходит до нужного обработчика:
curl -s -o /dev/null -w '%{http_code} %{content_type}\n' \
-X POST https://nezha.example.com/proto.NezhaService/ \
-H 'Content-Type: application/grpc'Ожидаемый ответ — 200 application/grpc. Если пришло 404 text/html, значит в конфиге потерян или неверно записан блок location ^~ /proto.NezhaService/. Обратите внимание: вторая проверка проходит успешно и без HTTP/2 — Nginx примет запрос по версии 1.1 и сам переведёт его в gRPC. Именно поэтому нужны обе, и первая важнее.
Откройте https://nezha.example.com/dashboard и убедитесь, что вход работает по домену. Если вы предпочитаете Caddy, он умеет и HTTPS сам, и h2c из коробки — конфиг для него есть в документации Nezha, а про сам Caddy у нас есть отдельная статья.
Шаг 4. Подключаем первый сервер
Адрес подключения агентов мы уже задали в конфиге, так что готовая команда установки лежит прямо в интерфейсе. Проверить настройку можно в «Системные настройки» → «Адрес подключения агента [домен/IP:порт]»: там должно быть nezha.example.com:443, а галочка «Использовать TLS для подключения агента» — включена.
Дальше на странице «Сервер» нажмите кнопку «Команда установки» и выберите Linux — команда скопируется в буфер обмена. Выглядит она так (секрет у вас будет свой):
curl -L https://raw.githubusercontent.com/nezhahq/scripts/main/agent/install.sh -o agent.sh \
&& chmod +x agent.sh \
&& env NZ_SERVER=nezha.example.com:443 NZ_TLS=true NZ_CLIENT_SECRET=ВАШ_СЕКРЕТ ./agent.shСкрипт кладёт агент в /opt/nezha/agent/, создаёт рядом файл config.yml и регистрирует службу systemd с именем nezha-agent.
Скрипт ставит службу, но не запускает её
Внутри он вызывает установку службы, которая создаёт юнит и включает автозапуск, но команду старта не выполняет. Пишет при этом бодрое nezha-agent successfully installed — и человек ждёт сервер в панели, а его нет. Запустите агент сами:
sudo systemctl enable --now nezha-agentЧерез несколько секунд сервер появится в панели сам — со случайным именем вроде talented-caiman. Нажмите на карандаш и переименуйте во что-то понятное.
Проверить агент на месте:
sudo systemctl status nezha-agent
sudo journalctl -u nezha-agent -n 30 --no-pagerВторой, третий и десятый сервер подключаются той же самой командой: секрет привязан к вашей учётной записи, а не к машине, и уникальный идентификатор агент генерирует себе сам. Никаких «добавить сервер» в панели заранее делать не нужно.
Если сервер в России и GitHub недоступен
Скрипт качает и сам себя, и бинарник агента с github.com. Когда доступа туда нет, установка молча падает на скачивании. Обходной путь — принести бинарник любым удобным способом (например, скачать на машине, где GitHub открывается, и скопировать через scp) и зарегистрировать службу вручную:
sudo mkdir -p /opt/nezha/agent
# положите распакованный nezha-agent в /opt/nezha/agent/ и дайте права
sudo chmod +x /opt/nezha/agent/nezha-agent
sudo env NZ_SERVER=nezha.example.com:443 \
NZ_TLS=true \
NZ_CLIENT_SECRET=ВАШ_СЕКРЕТ \
/opt/nezha/agent/nezha-agent service -c /opt/nezha/agent/config.yml install
sudo systemctl enable --now nezha-agentКоманда service install сама создаст config.yml из переменных окружения и юнит systemd — это ровно то, что делает официальный скрипт на последнем шаге. Архивы под все архитектуры лежат в разделе Releases репозитория nezhahq/agent, для обычного VPS нужен nezha-agent_linux_amd64.zip.
Что видно в панели
По каждому серверу: загрузка процессора, занятая память и swap, занятый диск, средний load за 1/5/15 минут, число процессов, количество TCP- и UDP-соединений, скорость приёма и отдачи прямо сейчас и — то, ради чего всё затевалось — суммарный трафик. Плюс аптайм, модель процессора, дистрибутив с версией, архитектура, тип виртуализации и флаг страны.
Клик по карточке открывает страницу сервера с графиками. Режимы «1 день», «7 дней» и «30 дней» работают только при включённом хранилище истории — мы включили его блоком tsdb на первом шаге.
Отдельно стоит заглянуть в раздел «Сервис». Там задаются проверки доступности: HTTP GET по адресу сайта, ICMP Ping по домену или IP, TCPing по паре адрес-порт. Особенность в том, что проверку выполняют сами агенты, каждый из своей страны, — и на вкладке «Network» на странице сервера видно, как из разных точек отличается задержка до одной и той же цели. Для HTTPS-адресов Nezha заодно следит за сроком сертификата и предупреждает, когда он подходит к концу.
Чтобы трафик считался честно
По умолчанию агент складывает трафик всех сетевых интерфейсов. На сервере с Docker, WireGuard или туннелем это даёт цифры заметно больше тех, что считает хостер: локальные интерфейсы попадают в общий котёл. Лечится списком разрешённых интерфейсов. Посмотрите имена через ip -br link и оставьте только внешний:
nic_allowlist:
eth0: trueРядом работает такой же список для дисков — hard_drive_partition_allowlist, простой список разделов. Он пригождается, когда в «занятом месте» учитывается что-то лишнее. После правки файла: sudo systemctl restart nezha-agent. То же самое можно сделать не заходя на сервер — в панели у каждого сервера есть кнопка «Редактировать конфигурацию сервера», агент применит настройки примерно через 10 секунд.
Шаг 5. Алерт «трафик заканчивается» — главное, ради чего это ставят
Дешёвые зарубежные VPS почти всегда идут с месячным лимитом: 500 ГБ, 1 ТБ, 2 ТБ. Пока лимит не выбран, о нём никто не думает; когда выбран — сервер режут или выставляют счёт. Nezha умеет предупредить заранее, и настраивается это за пять минут.
Правила живут в разделе «Уведомление», на вкладке «Правила тревог». Нажмите кнопку добавления — откроется окно «Создать правила оповещений». Писать JSON руками не нужно: в форме есть конструктор — выпадающий список типа и поля под выбранный тип. Заполняем так:
| Поле | Значение | Что это |
|---|---|---|
| Тип | transfer_out_cycle | исходящий трафик за расчётный период |
| max | 966367641600 | порог в байтах — здесь 900 ГБ |
| cycle_start | 2026-08-01T00:00:00+03:00 | начало первого периода, с часовым поясом |
| cycle_interval | 1 | длина периода |
| cycle_unit | month | единица периода: раз в месяц |
| Охват | Monitor all servers | следить за всеми серверами (пункт пока без перевода) |
То же самое можно вставить одним куском в поле «Advanced JSON» внизу формы:
[
{
"type": "transfer_out_cycle",
"max": 966367641600,
"cycle_start": "2026-08-01T00:00:00+03:00",
"cycle_interval": 1,
"cycle_unit": "month",
"cover": 0
}
]Порог считается в байтах, поэтому нужное число гигабайт умножьте на 1073741824. Готовые значения для порога в 90% от пакета:
| Пакет хостера | Порог 90% | Значение max |
|---|---|---|
| 500 ГБ | 450 ГБ | 483183820800 |
| 1 ТБ | 921 ГБ | 988916219904 |
| 2 ТБ | 1843 ГБ | 1978906181632 |
Ниже в форме выберите «Группу уведомлений» (создадим на следующем шаге), а в «Режиме срабатывания» поставьте «Один раз» — тогда придёт одно сообщение при переходе через порог, а не поток одинаковых. Не забудьте галочку «Включить».
Три честные оговорки про счётчик трафика. Первая: Nezha считает трафик с момента установки агента, а не с начала вашего расчётного периода, поэтому в первый месяц её цифра будет меньше хостерской. Вторая: расчётный период у большинства хостеров начинается в день оплаты, а не первого числа — подставьте в cycle_start свою дату. Третья: у хостера и у агента разные точки измерения, расхождение в несколько процентов — норма, поэтому и берём порог 90%, а не 99%.
Вторым правилом заведите оповещение о падении — оно самое простое:
[
{ "type": "offline", "duration": 10, "cover": 0 }
]Поле duration здесь — это количество проверок, а не секунд. Панель опрашивает состояние примерно раз в три секунды, так что 10 — это около тридцати секунд подряд без ответа. Минимальное разумное значение — 3; ставить меньше бессмысленно, будете получать ложные срабатывания на каждом сетевом чихе. Значение cover: 0 означает «следить за всеми серверами».
Шаг 6. Уведомления в Telegram
Отдельного «интеграционного» модуля у Nezha нет: любое уведомление — это HTTP-запрос, который панель отправляет по заданному адресу. Для Telegram это делает всё простым.
Сначала бот:
- Напишите
@BotFather, отправьте/newbot, придумайте имя — получите токен вида123456789:AAE... - Напишите
@userinfobot— он ответит вашим числовым идентификатором - Обязательно напишите что-нибудь своему новому боту — пока вы не начали диалог первым, Telegram не разрешит ему писать вам
Дальше в панели: раздел «Уведомление», вкладка «Уведомитель», кнопка добавления — откроется окно «Создать уведомитель». Название — Telegram, «Метод запроса» — GET, тело запроса оставьте пустым. В поле URL вставьте строку, подставив свой токен и свой идентификатор:
https://api.telegram.org/bot123456789:AAE.../sendMessage?chat_id=987654321&text=#NEZHA#Плейсхолдер #NEZHA# подставляет текст события. Есть и другие — #SERVER.NAME#, #SERVER.IP#, #SERVER.CPU#, #DATETIME# — но для Telegram хватает одного. При сохранении панель сразу отправит тестовое сообщение: если оно пришло, всё настроено верно.
Последний штрих, про который забывают чаще всего: правила тревог отправляют сообщения не напрямую уведомителю, а группе уведомлений. Группы лежат в другом разделе — «Группа», вкладка «Уведомление». Создайте там группу, положите в неё Telegram, и вернитесь в оба правила из прошлого шага, чтобы выбрать её в поле «Группа уведомлений». Без этого шага уведомитель есть, тестовое сообщение приходит, а алерты молчат.
Если сервер панели не ходит в Telegram. Уведомление отправляет сама панель, поэтому доступ к api.telegram.org нужен только с одного сервера — того, где стоит Nezha. Если оттуда не получается, есть три пути: разместить панель у хостера, откуда доступ есть; поднять свой прокси Telegram API и указать его адрес вместо официального; или отправлять уведомления не в Telegram, а на почту или в любой другой сервис с HTTP-адресом. Отдельно учтите: панель по соображениям безопасности отказывается слать уведомления на локальные и внутренние адреса — прокси должен быть доступен по публичному имени.
Публичная страница статуса
Корень домена — https://nezha.example.com/ — это и есть публичная страница: карточки серверов, суммарный трафик, карта и панель проверок сервисов. По умолчанию она открыта всем, без входа. Это удобно, если вы хотите показывать статус пользователям, и совершенно не нужно, если серверы личные.
Что с этим можно сделать — от мягкого к жёсткому:
- Спрятать отдельный сервер. В «Редактировать сервер» галочка «Скрыто для гостей» — карточка останется видна только вам.
- Спрятать чувствительное. Гостям и так не видны приватные заметки, а публичные заметки видны всем — не кладите туда ничего лишнего.
- Закрыть страницу целиком. Добавьте в
data/config.yamlстрокуforce_auth: trueи перезапустите панель — тогда без входа не покажут ничего.
cd /opt/nezha/dashboard && sudo docker compose restartЕсли решили оставить страницу публичной, имеет смысл сделать так, чтобы встроенная защита от подбора паролей видела реальные адреса посетителей, а не адрес Nginx. Для этого добавьте в конфиг ещё одну строку — web_real_ip_header: nz-realip — и перезапустите панель.
Осторожно: этой строкой легко закрыться снаружи. После неё панель перестаёт отвечать на любые запросы, в которых нет заголовка nz-realip — то есть на всё, что идёт мимо Nginx. Мы проверили: прямое обращение к порту 8008 (в том числе через SSH-туннель) начинает возвращать {"error":"real ip header not found"}. Если после перезапуска панель перестала пускать и по домену — значит, в конфиге Nginx потерялась строка proxy_set_header nz-realip $remote_addr;. Аварийный выход: убрать строку web_real_ip_header из data/config.yaml и перезапустить контейнер.
Задачи и терминал: мощно и опасно
Nezha умеет не только смотреть, но и делать. В разделе «Задача» можно завести команду с расписанием в формате cron и запускать её сразу на всех серверах: раз в сутки чистить логи, перезапускать сервис, дёргать бэкап. У каждого сервера есть кнопка с терминалом прямо в браузере и простенький файловый менеджер.
Это действительно удобно. И это же — главный риск всей конструкции, который нужно назвать прямо:
Агент работает от root. Значит, администратор панели Nezha автоматически имеет root на всех подключённых серверах. Взломанная или просто плохо запароленная панель — это не «кто-то увидел мои графики», это «кто-то получил все мои машины». Относитесь к ней как к связке ключей от инфраструктуры.
Отсюда четыре правила, которые стоит выполнить сразу:
- Длинный уникальный пароль и HTTPS — мы это уже сделали на шагах 2 и 3.
- Выключите выполнение команд там, где оно не нужно. В конфиге агента параметр
disable_command_execute: trueзапрещает и задачи, и терминал, и файловый менеджер. Ставится он и через панель, кнопкой «Редактировать конфигурацию сервера» — заходить на сервер не нужно. - Не селите на сервер панели ничего лишнего. Только Nezha и Nginx.
- Секрет из команды установки — это пароль. Не выкладывайте его в переписку, issue или чат: кто им владеет, тот может зарегистрировать в вашей панели свой сервер.
Если удалённые команды вам не нужны в принципе, ставьте агенты сразу с этим параметром — допишите в команду установки ещё одну переменную:
curl -L https://raw.githubusercontent.com/nezhahq/scripts/main/agent/install.sh -o agent.sh \
&& chmod +x agent.sh \
&& env NZ_SERVER=nezha.example.com:443 \
NZ_TLS=true \
NZ_CLIENT_SECRET=ВАШ_СЕКРЕТ \
NZ_DISABLE_COMMAND_EXECUTE=true \
./agent.sh
sudo systemctl enable --now nezha-agentПолучится честный мониторинг без права что-либо менять на серверах. Общие правила про SSH, ключи и файрвол — в нашей статье про безопасность VPS.
Бэкап и обновление
Всё состояние панели лежит в одном каталоге: /opt/nezha/dashboard/data. Там конфиг, база SQLite со списком серверов и правил, и хранилище истории. Чтобы копия была согласованной, панель на время копирования лучше остановить — это несколько секунд:
cd /opt/nezha/dashboard
sudo docker compose stop
sudo tar czf ~/nezha-$(date +%F).tar.gz -C /opt/nezha/dashboard data
sudo docker compose startПолученный архив отправьте туда же, куда возите остальные бэкапы — например, через restic. В конфиге лежит ключ подписи сессий и секреты — архив шифруйте.
Обновление панели — правка одной строки. Посмотрите последнюю версию в разделе Releases репозитория nezhahq/nezha, поменяйте тег в docker-compose.yaml и выполните:
cd /opt/nezha/dashboard
sudo docker compose pull
sudo docker compose up -dАгенты обновляются сами и никаких действий не требуют. Точные версии панели и агента совпадать не обязаны — это два независимых релизных цикла. Перед обновлением делайте бэкап: релизы выходят часто, и иногда в них едет миграция базы.
Частые проблемы
Панель открывается, а сервер в ней так и не появился
Сначала убедитесь, что служба вообще запущена: systemctl is-active nezha-agent должно ответить active. Если inactive — установочный скрипт её не стартовал, выполните sudo systemctl enable --now nezha-agent. Если служба работает, смотрим журнал: sudo journalctl -u nezha-agent -n 30 --no-pager. Строка tls: no application protocol означает ровно одно: на Nginx не включён HTTP/2 — вернитесь к шагу 3 и допишите http2 в строку listen 443 ssl. Строка connection refused — агент не достучался до порта 443: проверьте домен в NZ_SERVER и файрвол. Если в журнале вообще пусто, включите в /opt/nezha/agent/config.yml параметр debug: true и перезапустите службу — по умолчанию агент молчит.
В журнале агента «you were blocked by nezha WAF»
Так и есть: у панели встроенная защита от подбора, и после нескольких попыток подключиться с неверным секретом она блокирует адрес. Мы поймали это на проверочной установке — хватило нескольких попыток подряд. Коварство в том, что после исправления секрета агент продолжает получать отказ, пока блокировка не снята. Лечится в «Системные настройки» → «Файрвол (WAF)»: найдите строку с IP своего сервера и удалите её. Агент подключится в течение минуты, перезапускать его не нужно — мы проверили и это.
У всех серверов один и тот же IP-адрес
И этот адрес — адрес самого сервера панели. Значит, панель видит подключение от Nginx, а не от агента. Проверьте две вещи: строку agent_real_ip_header: nz-realip в data/config.yaml и строку grpc_set_header nz-realip $remote_addr; в блоке location ^~ /proto.NezhaService/. Имя заголовка в обоих местах должно совпадать посимвольно. После правки конфига панель нужно перезапустить, Nginx — перезагрузить.
Графики за 7 и 30 дней серые и не нажимаются
Не включено хранилище истории. Проверьте блок tsdb в data/config.yaml и поищите в журнале строку TSDB initialized successfully. Учтите: старая история проверок сервисов при первом включении хранилища не переносится, поэтому включать его лучше сразу, а не через полгода. Ещё одна деталь: гостям доступны только «сейчас» и «1 день», семь и тридцать дней показываются после входа.
Задача из панели падает с «command not found»
Команда выполняется не в вашей привычной оболочке, и переменная PATH там другая. В документации советуют начинать команду с source ~/.bashrc &&, но на Ubuntu это часто не помогает: стандартный ~/.bashrc в самом начале выходит, если оболочка неинтерактивная. Надёжное решение — писать полный путь: /usr/bin/docker вместо docker. Узнать путь: which docker.
Панель отвечает «real ip header not found» и не пускает
Это последствие строки web_real_ip_header в конфиге: панель перестала принимать запросы, в которых нет заголовка с реальным IP. Если так отвечает прямое обращение к порту 8008 — всё в порядке, так и задумано, ходите через домен. Если так отвечает и домен, значит Nginx не проставляет заголовок: проверьте строку proxy_set_header nz-realip $remote_addr; в блоке location /. Аварийный выход — убрать web_real_ip_header из data/config.yaml и выполнить sudo docker compose restart.
Цифры трафика в панели не сходятся с цифрами хостера
Полного совпадения не будет никогда, и это нормально: измеряют в разных точках. Но если расхождение в разы, почти всегда виноваты лишние сетевые интерфейсы — Docker, WireGuard, туннели. Ограничьте учёт внешним интерфейсом через nic_allowlist в конфиге агента. И помните, что счёт начинается с момента установки агента, а не с начала расчётного периода у хостера.
Коротко: порядок действий
- Отдельный VPS от 1 ГБ памяти под панель, домен и A-запись на его IP
- Docker из
get.docker.com, каталог/opt/nezha/dashboard docker-compose.yamlс портом127.0.0.1:8008:8008иdata/config.yamlсlocation,install_host,agent_real_ip_headerи блокомtsdbdocker compose up -d, в журнале —TSDB initialized successfully- SSH-туннель
ssh -L 8008:127.0.0.1:8008, входadmin/admin, немедленная смена пароля - Nginx с тремя блоками location,
certbot --nginx - Дописать
http2в строкуlisten 443 ssl— иначе агенты не подключатся - Проверить:
curlна корень домена показываетHTTP-версия: 2, а на/proto.NezhaService/—200 application/grpc - «Команда установки» → Linux → выполнить на каждом сервере, затем
sudo systemctl enable --now nezha-agent - Правила: трафик
transfer_out_cycleи падениеoffline - Уведомитель Telegram, группа уведомлений, привязать группу к правилам
- Решить судьбу публичной страницы и выключить лишние права агентов
Если не подошло: sudo docker compose down в /opt/nezha/dashboard останавливает панель, каталог data остаётся на месте. Агент удаляется командой sudo /opt/nezha/agent/nezha-agent service -c /opt/nezha/agent/config.yml uninstall, а затем sudo rm -rf /opt/nezha/agent.
Панели Nezha хватает самого простого тарифа — 1 ГБ памяти и 10 ГБ диска
Дешёвые VPS 2026 →