Uptime Kuma: установка в Docker и мониторинг сайтов и API
Uptime Kuma: установка в Docker на отдельный VPS, свой домен с TLS, мониторинг доступности сайтов и API, уведомления в Telegram, статус-страница и переход на 2.x.
Актуально на сентябрь 2026 года: Uptime Kuma 2.5.5, ветка 1.x больше не обновляется. Упор — на то, что ломается в эксплуатации: ложные тревоги, прокси, миграция и рост нагрузки.
Что понадобится
- Отдельный VPS под мониторинг. Для десятков мониторов хватит 1 vCPU и 1 ГБ RAM, расчёт на 200 — ниже. ForestsNet Cloud разворачивает VPS за несколько минут после оплаты, площадки — в Нидерландах и Германии.
- Ubuntu 24.04 или Debian 12/13 с Docker и плагином Compose, см. Docker и Docker Compose.
- Поддомен вроде
kuma.example.comс A-записью на сервер, см. Как привязать домен к серверу. - Базовая защита: усиление SSH и файрвол ufw.
Пункты меню Kuma даны по английскому интерфейсу, русский включается в Settings → Appearance.
Шаг 1. Решите, где стоит мониторинг и что он проверяет
Не на том же сервере
Мониторинг на одной машине с сервисами молчит ровно тогда, когда нужен: сервер завис или потерял сеть — замолкает и Kuma, а тишина выглядит как «всё в порядке». Проверка изнутри к тому же идёт мимо внешней сети, DNS и файрвола. Ставьте Kuma на отдельный VPS, лучше в другой площадке или у другого провайдера.
За самой Kuma тоже нужен присмотр: второй экземпляр в другом месте или внешний «сторожевой таймер». Например, HTTP-монитор в Kuma на адрес проверки Healthchecks.io с интервалом в минуту: пока Kuma жива, она его дёргает, а замолчит — уведомит Healthchecks.io, бесплатно до 20 проверок.
Что мониторить
- Входы для пользователей: главная страница с ключевым словом, страница входа, эндпоинт здоровья API.
- Сроки TLS-сертификатов и доменов.
- Базы и внутренние сервисы, открытые только для IP мониторинга.
- Фоновые задачи через push: бэкапы и cron-задания.
- Не сюда: загрузку CPU, память и диск Kuma не собирает, для этого есть Beszel и Zabbix. Монитору Docker Container нужен доступ к Docker API, то есть фактически root на проверяемом хосте, так что с отдельного сервера он неудобен.
Шаг 2. Установите Uptime Kuma в Docker
mkdir -p /opt/uptime-kuma && cd /opt/uptime-kumaФайл compose.yaml:
services:
uptime-kuma:
image: louislam/uptime-kuma:2.5.5-slim
container_name: uptime-kuma
restart: unless-stopped
ports:
- "127.0.0.1:3001:3001"
volumes:
- ./data:/app/data
environment:
TZ: Etc/UTC # замените на свой, например Europe/Berlindocker compose up -d
docker compose logs -fДождитесь в логе строки Listening on. Что здесь важно:
- Тег.
latestдо сих пор указывает на 1.23.17 без исправлений безопасности. Тег2даёт последнюю 2.x, но версию лучше закрепить: 2.2.0–2.3.1, например, рассылали ложные DOWN из-за блокировок SQLite. - Образ slim. Полный образ занимает около 1,7 ГБ с MariaDB и Chromium для мониторов Browser Engine, slim — около 0,5 ГБ.
- 127.0.0.1. Порт, опубликованный Docker, обходит ufw: трафик уходит в цепочки iptables Docker раньше правил ufw, и порт на
0.0.0.0открыт всему интернету. - Каталог data держите на локальном диске: на NFS SQLite портится из-за блокировок файлов.
- TZ — часовой пояс для окон обслуживания, см. шаг 7.
ssh -L 3001:127.0.0.1:3001 deploy@IP_сервераНа http://localhost:3001 Kuma спросит, какую базу использовать. SQLite официально рекомендована для небольших установок, а на практике держит сотни мониторов. Встроенная MariaDB есть только в полном образе и ест больше памяти. Выбирайте сразу: перенос из SQLite в MariaDB официально не поддерживается.
Шаг 3. Подключите домен с TLS и WebSocket
Интерфейс Kuma работает через WebSocket, поэтому прокси должен пропускать заголовки Upgrade и Connection и не подменять Host. Подкаталог вроде example.com/kuma не поддерживается, нужен домен или поддомен.
Caddy
Caddy сам получит сертификат Let's Encrypt, пропустит WebSocket и выставит заголовки X-Forwarded-*, установка — в статье Caddy: автоматический HTTPS. Файл /etc/caddy/Caddyfile:
kuma.example.com {
reverse_proxy 127.0.0.1:3001
}sudo systemctl reload caddyNginx
Основа — Nginx: установка и первый сайт, сертификат — Let's Encrypt через certbot.
server {
listen 80;
server_name kuma.example.com;
location / {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d kuma.example.comHost обязателен: с версии 1.23.9 Kuma сверяет Origin у WebSocket с Host и отклоняет несовпадения, в логе появляется Origin (...) does not match host. В proxy_pass пишите 127.0.0.1: на Debian имя localhost разрешается и в ::1, и nginx засорит лог ошибками.
В самой Kuma включите Settings → Reverse Proxy → Trust Proxy, чтобы видеть IP клиентов, и задайте Settings → General → Primary Base URL https://kuma.example.com: из него строятся адреса push-мониторов. За Cloudflare включите переключатель WebSockets в разделе Network.
Шаг 4. Закройте прямой доступ и включите 2FA
sudo ufw default deny incoming
sudo ufw allow 22/tcp # или ваш порт SSH
sudo ufw allow 80,443/tcp
sudo ufw enable
sudo ss -tlnp | grep 3001В выводе ss должно быть 127.0.0.1:3001, а curl -m 5 http://IP_сервера:3001 с другой машины должен закончиться таймаутом или отказом. Затем включите 2FA в Settings → Security: форма входа доступна из интернета всегда, в том числе на домене статус-страницы.
Шаг 5. Настройте первые мониторы
Настройки HTTP-мониторов, которые снимают большую часть ложных тревог:
- Heartbeat Interval — 60 секунд.
- Retries — 2–3. В 2.x у новых мониторов по умолчанию 0, и одна потерянная проверка сразу шлёт DOWN. Пока идут повторы, монитор в статусе PENDING и уведомлений нет.
- Heartbeat Retry Interval — 20–30 секунд, чтобы подтверждать сбой быстрее.
- Request Timeout — 10–20 секунд вместо 48 по умолчанию, это 80% интервала.
HTTP с ключевым словом
Тип HTTP(s) - Keyword требует ответ 2xx и заданную строку в теле: так ловятся страница с ошибкой приложения и заглушка, которые тоже отдают 200. Поиск идёт по исходному HTML или JSON с учётом регистра, а у SPA текст появляется только после JavaScript, поэтому берите строку из <title>. Invert Keyword проверяет, что строки нет, а для API есть HTTP(s) - Json Query с выражением JSONata. Не включайте Ignore TLS/SSL errors: вместе с ним отключаются и уведомления о сроке сертификата.
Сроки сертификатов и доменов
Флажок Certificate Expiry Notification в HTTP-мониторе по умолчанию снят, включите его. За сколько дней предупреждать, задаётся в Settings → Notifications → TLS Certificate Expiry, по умолчанию за 7, 14 и 21 день. С версии 2.1 сертификат проверяет и монитор TCP Port: IMAPS и STARTTLS в SMTP, IMAP и XMPP.
Срок домена Kuma с версии 2.1 проверяет по RDAP, флажок Domain Name Expiry Notification включён по умолчанию. Но для .ru, .рф, .su, .de, .eu и .io в реестре RDAP IANA на сентябрь 2026 года записей нет, и проверка не работает: держите такие домены на автопродлении.
TCP для баз
TCP Port видит только, что порт принимает соединения. Мониторы PostgreSQL и MySQL/MariaDB выполняют запрос, по умолчанию SELECT 1. Дайте им пользователя с минимальными правами, а порт базы откройте только для IP мониторинга:
sudo ufw allow from 203.0.113.10 to any port 5432 proto tcpЕсли база в Docker с опубликованным портом, ufw её не закроет по той же причине, что в шаге 2. Публикуйте такой порт на внутренний интерфейс, например WireGuard.
Push для cron-заданий
Push-монитор работает наоборот: задание само стучится в Kuma по URL, и если отчёта не было дольше Heartbeat Interval, будет тревога. Для ночного бэкапа поставьте интервал 25 часов (90000 секунд) и Retries 0:
#!/bin/sh
PUSH="https://kuma.example.com/api/push/ТОКЕН"
if /usr/local/bin/backup.sh >>/var/log/backup.log 2>&1; then
curl -fsS -m 10 --retry 3 "$PUSH?status=up&msg=OK" >/dev/null
else
curl -fsS -m 10 --retry 3 "$PUSH?status=down&msg=backup_failed" >/dev/null
fiТак вы узнаете и о падении скрипта, и о том, что cron не запустил его вовсе. Сам скрипт и расписание — в статьях Автобэкапы по расписанию и Бэкапы баз данных.
Шаг 6. Настройте уведомления в Telegram
- В @BotFather отправьте
/newbotи получите токен. - Для личного чата напишите боту
/start. В группе отправьте/start@имя_бота: обычное сообщение бот из-за режима приватности может не увидеть. В канале сделайте бота администратором. - В Settings → Notifications → Set Up Notification выберите Telegram, вставьте токен и нажмите Auto Get: Kuma возьмёт ID последнего чата из
getUpdates. У групп ID отрицательный, у супергрупп начинается с-100, для темы форума есть Message Thread ID. - Нажмите Test. Флажок Default enabled подключит канал к новым мониторам, Apply on all existing monitors — к уже созданным.
Auto Get обращается к Telegram из браузера. Если не срабатывает, возьмите chat.id из ответа curl -s https://api.telegram.org/botТОКЕН/getUpdates на сервере.
Как не заспамить себя
- Сообщения идут только при смене состояния: UP → DOWN, DOWN → UP и DOWN после окна обслуживания. Пока монитор в PENDING на повторах, тишина.
- Resend Notification if Down X times consecutively по умолчанию 0 — одно сообщение на аварию. Значение 30 при интервале 60 секунд даст напоминание раз в полчаса.
- Объедините проверки сервера в монитор Group и вешайте уведомления на него: при падении сервера придёт одно сообщение со списком упавших вместо десяти.
- Плановые работы проводите через окна обслуживания, второстепенные проверки шлите в отдельный чат или с флажком Send Silently.
Шаг 7. Сделайте статус-страницу и окна обслуживания
Статус-страница
В Status Pages → New Status Page задайте имя и slug, страница откроется по адресу /status/slug. Добавляйте только публичные мониторы. Инциденты публикуются кнопкой Create Incident, с версии 2.1 хранится их история.
Отдельный домен вроде status.example.com впишите в Domain Names и добавьте в прокси с тем же reverse_proxy. Прокси должен передавать Host, иначе Kuma не поймёт, какую страницу показать. Статусы на публичной странице кэшируются до минуты.
Окна обслуживания
В Maintenance → Schedule Maintenance выберите мониторы, статус-страницы и стратегию: вручную, разовое окно, cron-выражение, интервал, дни недели или месяца. В окне мониторы синие, уведомлений нет, а на статус-странице висит плашка. Если после окна сервис всё ещё лежит, придёт DOWN.
С часовыми поясами есть подвохи:
- Пояс сервера Kuma при первом входе берёт из браузера администратора, а
TZперебивает его при каждом старте контейнера. Держите одинаковымиTZв compose и Settings → General → Server Timezone, иначе смена пояса в интерфейсе откатится после перезапуска. - Пояс окна задавайте явно, а не «как у сервера».
- На сентябрь 2026 года открыты баги: окно по времени суток не учитывает летнее время (#6233), список окон показывает время в поясе браузера (#7895). После перевода часов сверяйте окна в форме редактирования.
Шаг 8. Перейдите с 1.x на 2.x
Что нового в 2.x
- Агрегаты вместо сырой истории. Детальные проверки хранятся около суток, дальше остаются поминутные итоги за 24 часа, почасовые за 30 дней и суточные на весь срок хранения. Дашборды быстрее, база не разрастается до гигабайт. Срок задаётся в Settings → Monitor History: 365 дней у новых установок, прежний — после миграции.
- MariaDB, встроенная или внешняя, и образы slim и rootless.
- Новые мониторы: SNMP, SMTP, SFTP, NTP, RabbitMQ, WebSocket Upgrade и Globalping — проверка с чужих узлов в выбранной стране, анонимно до 250 тестов в час.
- Уведомления: шаблоны на LiquidJS, свой сервер Bot API для Telegram, прокси
NOTIFICATION_PROXY. - API для управления мониторами так и нет (#118 открыт с 2021 года): только push, бейджи, JSON статус-страниц и
/metricsдля Prometheus с ключом из Settings → API Keys.
Миграция
При первом старте 2.x переносит историю в агрегаты. У автора проекта 20 мониторов с историей за 90 дней мигрировали около 7 минут, большая история на медленном диске займёт часы.
cd /opt/uptime-kuma # каталог с compose.yaml
docker compose down
tar -czf kuma-v1-$(date +%F).tar.gz data # ваш каталог с /app/data
# в compose.yaml поменяйте тег на louislam/uptime-kuma:2.5.5-slim
docker compose up -d && docker compose logs -fВ логе идут строки [DON'T STOP] Migrating monitor ... с процентом, в конце — Aggregate Table Migration Completed. Ещё:
- Если старая история не нужна, до перехода сократите срок хранения в 1.23 и нажмите Shrink Database: мигрировать придётся меньше.
- Rootless-образы для перехода с 1.x не рекомендуются.
- Удалены экспорт бэкапа в JSON и DNS-кэш HTTP-мониторов, в Docker его заменяет nscd. Переменные в шаблонах SMTP стали чувствительны к регистру, период у бейджей — только
24,24h,30dили1y. Без Docker нужен Node.js 20.4+.
Шаг 9. Настройте бэкап и обновления
Официальный бэкап в 2.x — копия каталога data. Копировать файл SQLite на ходу нельзя: база работает в режиме WAL, и копия может оказаться битой. Снимок делает сам SQLite, утилита sqlite3 есть в образе:
#!/bin/sh
set -e
D=/opt/uptime-kuma
mkdir -p $D/backup
rm -f $D/data/snapshot.db
docker exec uptime-kuma sqlite3 /app/data/kuma.db "VACUUM INTO '/app/data/snapshot.db'"
tar -czf $D/backup/kuma-$(date +%F).tar.gz -C $D/data --exclude='kuma.db*' .
rm -f $D/data/snapshot.db
find $D/backup -name 'kuma-*.tar.gz' -mtime +14 -deleteДля восстановления остановите контейнер, распакуйте архив в пустой data, переименуйте snapshot.db в kuma.db и запустите ту же версию Kuma. Архивы уносите с сервера. Внешнюю MariaDB бэкапьте через mariadb-dump.
Обновление — смена тега в compose.yaml и:
docker compose pull && docker compose up -dПеред обновлением снимите бэкап и прочитайте релиз. Минимум — 2.5.5, где закрыта утечка памяти в TCP-мониторах.
Сколько ресурсов нужно
Kuma — один процесс Node.js, больше одного ядра он почти не использует. Замеры из issues: 200 HTTP-мониторов с интервалом 60 секунд на 1.23 — около 130 МБ памяти и 2–3% от двух vCPU, около 250 мониторов на 2.0 — в среднем меньше 400 МБ.
- 20 мониторов: 1 vCPU и 1 ГБ RAM с запасом: Kuma займёт 100–150 МБ, остальное — система, Docker и прокси.
- 200 мониторов: 2 vCPU и 2 ГБ RAM, второе ядро — системе, прокси и процессам
ping. Встроенная MariaDB или мониторы Browser Engine с Chromium прибавят сотни мегабайт, тогда закладывайте 4 ГБ. - Диск: образ slim около 0,5 ГБ, полный — около 1,7 ГБ. База 2.x по схеме хранения — порядка 1 МБ на монитор с интервалом 60 секунд, а 1.x за полгода накопила бы для 200 мониторов около 9 ГБ. Нужен SSD или NVMe: на медленных носителях SQLite ловит блокировки.
- Потолок. Список мониторов не разбит на страницы: с 600–900 мониторами дашборд грузится до 8–15 секунд, а сопровождающие прямо пишут, что на тысячи мониторов Kuma не рассчитана. Дальше — несколько экземпляров или Gatus.
- Трафик почти весь входящий: Kuma скачивает проверяемые страницы, а отправляет лишь короткие запросы и уведомления. В пакет ForestsNet Cloud входит только исходящий трафик, входящий не считается.
Альтернативы: Gatus и Beszel
Gatus
Gatus 5.37 написан на Go, и все проверки в нём описываются в YAML: конфиг живёт в git и проходит ревью вместе с кодом. Условия пишутся выражениями вроде [STATUS] == 200, [RESPONSE_TIME] < 300 и [CERTIFICATE_EXPIRATION] > 240h. Есть Telegram и десятки других каналов, push через external-endpoints, окна обслуживания, SQLite или PostgreSQL. Отличия от Kuma:
- мониторы создаются только в конфиге, не в интерфейсе;
- без блока
storageистория живёт в памяти и пропадает при перезапуске; - оповещение срабатывает после 3 провалов подряд, а о восстановлении по умолчанию молчит (
send-on-resolved: false); - конфиг перечитывается на лету, но если смонтировать отдельный файл, а не каталог, правки могут не подхватиться.
Берите Gatus, если мониторинг разворачивается из репозитория вместе с инфраструктурой.
Beszel
Beszel 0.20 отвечает на другой вопрос: что происходит внутри сервера. Хаб на PocketBase (порт 8090) собирает с агентов CPU, память, диски, сеть, температуру, SMART и статистику контейнеров Docker и Podman, хранит историю и шлёт оповещения по порогам. Агенты принимают хаб по SSH на порту 45876 или сами подключаются к нему по WebSocket. С версии 0.20 они выполняют сетевые пробы ICMP, TCP, HTTP и DNS, но статус-страниц и проверки содержимого нет. Разумная пара: Kuma — доступность снаружи, Beszel — ресурсы изнутри.
Для полноценного мониторинга инфраструктуры с шаблонами и триггерами есть Zabbix.
Диагностика
- «Cannot connect to the socket server. Reconnecting…» — прокси не пропускает WebSocket: проверьте
UpgradeиConnectionв nginx и WebSockets в Cloudflare. Если в логе KumaOrigin (...) does not match host, прокси подменяетHost. Отключать проверку черезUPTIME_KUMA_WS_ORIGIN_CHECK=bypass— крайняя мера. - 502 от прокси — контейнер не запущен или ещё мигрирует базу:
docker compose psиdocker compose logs --tail=50. - DOWN, а сайт открывается.
timeout of ... exceeded— сайт отвечает дольше таймаута или теряются пакеты. 403 и 503 — запросы режет WAF, например Browser Integrity Check в Cloudflare: разрешите IP мониторинга в IP Access Rules. Повторите запрос из контейнера:docker exec -it uptime-kuma curl -sv https://example.com. - Ложные DOWN с
SQLITE_BUSY: database is locked— регрессия 2.2.0–2.3.1, обновитесь. На свежей версии ищите медленный диск или сетевую ФС. - Не стартует после перехода с 1.x с ошибкой миграции агрегатов — миграцию прервали, восстановите бэкап.
- Память контейнера растёт до гигабайт — в версиях 2.1–2.5.4 TCP-мониторы с проверкой сертификата оставляли незакрытые сокеты. Обновитесь до 2.5.5.
- Telegram отвечает
chat not found— боту не писали/startили ID неверный. Ошибкаgroup chat was upgraded to a supergroup chatзначит, что группа стала супергруппой и нужен новый ID с-100. - Push-монитор краснеет, хотя задание работает — интервал монитора короче периода задания или
curlне доходит до Kuma: выполните его вручную. - Забыли пароль:
docker exec -it uptime-kuma npm run reset-password.
Источники
- Uptime Kuma на GitHub: README,
compose.yamlи исходники 2.5.5. - Релизы Uptime Kuma: 2.0.0-beta.0, 2.0.0, 2.3.2, 2.5.5.
- Вики Uptime Kuma: установка, reverse proxy, миграция с v1 на v2, теги Docker, переменные окружения, окна обслуживания, статус-страницы, внутренний API, сайты за Cloudflare.
- Issues Uptime Kuma: SQLITE_BUSY — #7289, #7338; прерванная миграция — #7274; утечка памяти — #7537, PR #7848; ресурсы — #5884, #5568, #6181, #7079; API — #118; часовые пояса — #6233, #7895; тег latest — #6396.
- Docker: packet filtering and firewalls и Caddy: reverse_proxy.
- IANA: реестр RDAP доменных зон.
- Gatus, релиз v5.37.0; Beszel, переменные окружения, релиз v0.20.0.
- Healthchecks.io: тарифы.