Gatus на сервере: частые ошибки и решения
Gatus подкупает тем, что не требует агентов, базы данных «из коробки» и отдельного UI-сервера — один бинарник и YAML-конфиг превращаются в статус-страницу с историей аптайма. Именно поэтому его часто ставят «на пять минут», а потом полдня разбираются, почему проверка HTTPS не проходит, история пропадает после каждого деплоя, а алерты в Telegram молчат. Ниже — конкретные причины и рабочие решения для самых частых ситуаций.
Содержание
- Где Gatus чаще всего спотыкается
- Ошибка в условиях (conditions): синтаксис, который легко перепутать
- Gatus в Docker: DNS не резолвит внутренние хосты
- TLS и сертификаты: self-signed, просрочка, клиентские сертификаты
- История проверок пропадает после перезапуска
- Алерты не приходят: providers, thresholds и общие ошибки конфигурации
- Gatus за реверс-прокси: base-path, порт и статус-страница
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Где Gatus чаще всего спотыкается
Почти все проблемы с Gatus укладываются в пять категорий:
- синтаксис условий (
conditions) в YAML — легко ошибиться в скобках и типах; - сеть контейнера — DNS-резолвинг и доступ к внутренним сервисам из Docker;
- TLS — самоподписанные сертификаты, просроченные цепочки, клиентские сертификаты;
- хранилище — по умолчанию история живёт в памяти и исчезает при рестарте;
- интеграции оповещений — providers для Telegram/Slack/Discord настраиваются не так, как в остальных инструментах мониторинга.
Дальше разберём каждую по отдельности с конкретными конфигами.
Ошибка в условиях (conditions): синтаксис, который легко перепутать
Условия в Gatus — не произвольный код, а строки с фиксированным набором плейсхолдеров: [STATUS], [BODY], [RESPONSE_TIME], [CONNECTED], [CERTIFICATE_EXPIRATION], [IP], [DNS_RCODE]. Самая частая ошибка — писать [STATUS] == 200 в кавычках или сравнивать не тот тип:
endpoints:
- name: api-health
url: "https://api.example.com/health"
interval: 30s
conditions:
- "[STATUS] == 200"
- "[RESPONSE_TIME] < 300"
- "[BODY].status == UP"
Тонкости, на которых спотыкаются чаще всего:
[BODY]— это JSON-путь через точку, а не JSONPath с$.в начале;[BODY].data.statusработает,[BODY].$.data.status— нет.- Сравнение строк из тела ответа без кавычек:
[BODY].status == UPсработает, но если значение со спецсимволами — лучше в кавычках:[BODY].status == "all good". [RESPONSE_TIME]всегда в миллисекундах, без единиц измерения —< 300, а не< 300ms.- Проверка сертификата:
[CERTIFICATE_EXPIRATION] > 720h— Gatus понимает Go-нотацию длительности (h,m), а не дни. - Забытые дефисы перед условиями в списке — YAML ломает парсинг, и Gatus падает при старте с ошибкой
yaml: line N: did not find expected.... Проверяйте конфиг перед деплоем:
gatus --config config.yaml -validate
# или, если бинарника нет локально — через Docker
docker run --rm -v $(pwd)/config.yaml:/config/config.yaml twinproduction/gatus -validate
Если после -validate конфиг проходит, а сервис всё равно не стартует — смотрите логи контейнера, там обычно указана конкретная строка.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверGatus в Docker: DNS не резолвит внутренние хосты
Частый сценарий: Gatus и проверяемые сервисы подняты в одном docker-compose, но при проверке http://backend:8080/health Gatus пишет no such host или connection refused, хотя из другого контейнера всё работает.
Причины обычно две:
- Gatus не в той же docker-сети, что и проверяемый сервис. Docker Compose создаёт сеть на проект, и если Gatus объявлен в отдельном compose-файле или стеке — имена сервисов друг друга не видят.
- Проверка идёт по хосту публичного домена, который резолвится во внешний IP, а NAT не пробрасывает петлю (hairpin NAT) — классика для проверки
https://ваш-домен.ruизнутри той же сети, где крутится сайт.
Решение — явная общая сеть:
# docker-compose.yml
services:
gatus:
image: twinproduction/gatus:latest
container_name: gatus
restart: unless-stopped
ports:
- "8082:8080"
volumes:
- ./config.yaml:/config/config.yaml
- gatus-data:/data
networks:
- monitoring
- backend-net # та же сеть, что и у проверяемых сервисов
networks:
monitoring:
backend-net:
external: true # если backend поднят отдельным стеком
volumes:
gatus-data:
Если проверяете внешний домен, а не внутренний сервис, и получаете странные тайм-ауты — проверьте, что у контейнера вообще есть доступ наружу (docker exec gatus wget -qO- https://ya.ru для быстрой диагностики) и что DNS контейнера настроен (docker exec gatus cat /etc/resolv.conf). Иногда помогает явно задать dns в compose:
services:
gatus:
dns:
- 1.1.1.1
- 8.8.8.8
Похожие грабли с сетью контейнеров разобраны в статье про отсутствие сети из контейнера — если DNS у Gatus в порядке, а соединения всё равно рвутся, стоит проверить именно этот сценарий.
TLS и сертификаты: self-signed, просрочка, клиентские сертификаты
Три типичные ситуации:
1. Внутренний сервис с самоподписанным сертификатом. Gatus по умолчанию проверяет валидность цепочки, и проверка падает с x509: certificate signed by unknown authority. Для внутренних эндпоинтов, где вы осознанно используете self-signed сертификат, отключайте проверку точечно, а не глобально:
endpoints:
- name: internal-service
url: "https://internal.local:8443/health"
client:
insecure: true
conditions:
- "[STATUS] == 200"
Для продовых внешних эндпоинтов insecure: true ставить не стоит — вы теряете смысл проверки TLS вообще.
2. Мониторинг срока действия сертификата. Это отдельная и полезная функция Gatus — можно алертить заранее, до того как сертификат протухнет и всё упадёт:
endpoints:
- name: cert-expiry-check
url: "https://example.com"
interval: 12h
conditions:
- "[CERTIFICATE_EXPIRATION] > 168h" # алерт, если осталось меньше недели
Это удобно держать рядом с автопродлением Let's Encrypt — если продление сломалось, Gatus заметит это за неделю, а не в момент, когда сертификат уже просрочен (о самих проблемах с автопродлением — в статье про ошибки Let's Encrypt на сервере).
3. mTLS / клиентские сертификаты. Если эндпоинт требует клиентский сертификат, укажите его в блоке client:
client:
certificate-file: /config/certs/client.pem
private-key-file: /config/certs/client-key.pem
Не забудьте примонтировать директорию с сертификатами в контейнер и выставить на неё права 600 — Gatus запускается не от root по умолчанию в официальном образе, и слишком закрытые права на файл тоже вызовут ошибку чтения.
История проверок пропадает после перезапуска
По умолчанию Gatus хранит результаты проверок в памяти (storage.type: memory), и это осознанное поведение «из коробки», а не баг — но новички об этом узнают только когда после docker compose restart вся история аптайма обнуляется.
Решение — постоянное хранилище. Для одного инстанса хватает SQLite:
storage:
type: sqlite
path: /data/gatus.db
и обязательно вынести /data в volume, как показано выше в примере docker-compose. Если Gatus работает в кластере или вы хотите общее хранилище для нескольких инстансов (например, при HA-развёртывании), можно подключить PostgreSQL:
storage:
type: postgres
path: "postgres://gatus:пароль@postgres-host:5432/gatus?sslmode=disable"
Частая ошибка здесь — забыть создать базу и пользователя заранее (Gatus не создаёт саму базу данных, только таблицы внутри неё), а также не указать sslmode=disable, если Postgres поднят без TLS — иначе подключение отвалится с ошибкой SSL is not enabled on the server.
Ещё один нюанс: при переходе с memory на sqlite/postgres старая история не переносится — это разные хранилища, и после смены типа статус-страница стартует с чистого листа.
Алерты не приходят: providers, thresholds и общие ошибки конфигурации
Оповещения в Gatus настраиваются на двух уровнях: глобальный блок alerting с провайдерами и alerts внутри каждого endpoint, который включает конкретный тип алерта для конкретной проверки. Забыть второй уровень — самая частая причина «я всё настроил, а сообщений нет»:
alerting:
telegram:
token: "123456:ВАШ_ТОКЕН_БОТА"
id: "-1001234567890" # id чата или канала, со знаком минус для групп
endpoints:
- name: api-health
url: "https://api.example.com/health"
interval: 30s
conditions:
- "[STATUS] == 200"
alerts:
- type: telegram
failure-threshold: 3 # алерт после 3 подряд неудачных проверок
success-threshold: 2 # "восстановлено" после 2 подряд успешных
send-on-resolved: true
Что чаще всего идёт не так:
- Нет блока
alertsв endpoint — провайдер настроен глобально, но конкретная проверка не подписана ни на один тип алерта, поэтому молчит. failure-thresholdслишком высокий относительноinterval: при интервале 5 минут и threshold 10 вы узнаете о падении сервиса почти через час.send-on-resolved: false(значение по умолчанию) — вы получаете алерт о падении, но не об восстановлении, и это путают с «алерты не работают».- ID Telegram-чата без минуса для групп — id супергрупп начинаются с
-100; если скопировать id канала как для личного чата, бот получитchat not found. - Токен бота без прав писать в чат — бота нужно добавить в группу/канал и (для каналов) выдать права хотя бы на публикацию сообщений.
Если у вас уже настроен отдельный алертинг-стек в Telegram для других сервисов, схема токенов и получения chat_id разобрана подробнее в статье про настройку алертов в Telegram на VPS — принцип получения id там такой же.
Gatus за реверс-прокси: base-path, порт и статус-страница
Gatus отдаёт веб-интерфейс на порту 8080 внутри контейнера — это статус-страница со всей историей проверок. Публиковать её напрямую наружу без прокси — плохая идея (нет TLS, нет контроля доступа), поэтому обычно ставят Traefik или nginx перед ним.
Типичная ошибка — Gatus отдаёт страницу с абсолютными путями к статике (/js/..., /css/...), и если он смонтирован на поддиректории (https://status.example.com/gatus/), CSS и JS перестают подгружаться, хотя сама страница открывается. Решение — задать web.path явно, чтобы Gatus сам генерировал корректные пути:
web:
port: 8080
path: /
Проще всего избежать головной боли — выделить под статус-страницу отдельный поддомен (status.example.com), а не поддиректорию основного сайта, и проксировать его целиком:
# фрагмент конфига Traefik (labels в docker-compose)
labels:
- "traefik.enable=true"
- "traefik.http.routers.gatus.rule=Host(`status.example.com`)"
- "traefik.http.routers.gatus.entrypoints=websecure"
- "traefik.http.routers.gatus.tls.certresolver=letsencrypt"
- "traefik.http.services.gatus.loadbalancer.server.port=8080"
Если статус-страница отдаёт 502 через прокси, но напрямую по curl http://localhost:8082 с сервера работает — почти всегда дело в сети docker-compose (см. раздел про DNS выше) или в том, что Traefik резолвит контейнер не в той сети. Общие проблемы с Traefik и типовые причины 502/404 за реверс-прокси разобраны в статье про частые ошибки Traefik на сервере.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Gatus вообще не стартует, в логах ошибка YAML — с чего начать?
Прогоните конфиг через gatus --config config.yaml -validate (или тот же флаг в Docker-образе) — он укажет строку с проблемой. Чаще всего это забытый дефис в списке conditions или несовпадение отступов.
Можно ли мониторить не HTTP, а, например, PostgreSQL или Redis?
Да, у Gatus есть встроенные типы проверок помимо HTTP: TCP (tcp://host:port), DNS, ICMP (ping), а также плагин для проверки через произвольную команду (STARTTLS/TLS для сертификатов почтовых серверов). Для TCP/сокет-проверок условия ограничены [CONNECTED] и [RESPONSE_TIME], тела ответа там нет.
Нужно ли Gatus root в контейнере для ICMP-проверок (ping)?
Да, ICMP-проверки требуют либо cap_add: [NET_RAW] в docker-compose, либо запуск от root — обычный непривилегированный контейнер их просто не отправит и будет писать permission denied в лог проверки.
История растёт бесконечно, диск заполняется — как ограничить?
Настройте storage.maximum-number-of-results и/или storage.maximum-number-of-events в блоке storage — Gatus начнёт вычищать старые записи автоматически, не дожидаясь ручной чистки.
Как проверить сразу несколько окружений (staging + prod) в одном конфиге?
Просто заведите разные endpoints с разными group — Gatus группирует их на статус-странице по этому полю, и в одном YAML спокойно уживаются десятки проверок из разных сред.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →