Skip to content

Active monitors

Unlike the heartbeat, where your service “calls in” itself, an active monitor works the other way around: a Notifly serverless function polls the specified resource every N seconds. If the specified number of consecutive attempts fail — an alert is sent; when connectivity is restored — a recovery is sent.

There are 11 supported check types — from a simple HTTP-GET to a lightweight protocol handshake for SMTP / IMAP / POP3 / SSH / Redis / Postgres / MySQL, DNS resolution and TLS certificate expiry checks:

kindWhat it checksFormat of target
httpGET <url>, expect 2xx (or expectedStatus)https://example.com/health
tcpTCP handshake — port is openhost:port
tlsTCP+TLS handshake; expectedStatus = minimum certificate validity threshold (days)host:port
smtpTCP, read 220 banner, send EHLO/QUITmx.example.com:25
imapTCP, expect * OK ..., send LOGOUTimap.example.com:143
pop3TCP, expect +OK ..., send QUITpop3.example.com:110
sshTCP, read SSH-2.0-... bannerhost:22
redisTCP, send PING\r\n, expect +PONGredis.example.com:6379
postgresTCP, send StartupMessage, expect R/E/Ndb.example.com:5432
mysqlTCP, read handshake packet (protocol_version=10)db.example.com:3306
dnsResolve the name using the system or specified resolverexample.com or example.com@8.8.8.8:53

All TLS variants are performed over plain TCP (without StartTLS), so for encrypted ports specify the already-encrypted port (smtp:465, imap:993, pop3:995, redis:6380, etc.). To check raw TCP connectivity — use kind: "tcp".

every minute: timer-trigger → notifly-monitor
└─▶ SELECT WHERE next_check_at <= now AND status != "paused"
└─▶ HTTP GET / TCP Dial with timeoutSec
├─ success → status=up, fail_count=0
│ if was "down" and recoveryMessage exists → recovery
└─ error → fail_count++; if ≥ consecutiveFails and not "down":
send alert, status=down
  • Storage — the monitors table in YDB Serverless, indexed by next_check_at.
  • Check — a separate Cloud Function notifly-monitor, invoked by a Yandex Cloud timer- trigger (cron * * * * ? * — once per minute).
  • Notification — a regular Notifly message, sent via the channel chosen when creating the monitor and delivered to all your clients (web, Android, desktop), like any other push notification.

Since Notifly initiates the communication, there are two explicit events:

EventWhen it’s sentText
Loss of connectivity (alert)After consecutiveFails consecutive failures, only on the first transition to down. The message includes the technical error (e.g., dial tcp: connect: connection refused)alertMessage
Recovery (recovery)On the first successful check after a down state, only if recoveryMessage is setrecoveryMessage

There will be no repeated alert spam: while the monitor is down, no new messages are sent, checks continue at the regular interval and only record the status.

  1. Open app.notifly.ruMonitors.
  2. Click Create monitor, fill in:
    • Name — for display, e.g. “Prod site” or “Postgres replica”.
    • Channel — which channel to send the notification through.
    • Check type — one of the 11 supported kind values (see table above).
    • Target — format depends on the type: URL, host:port or DNS name.
    • Interval (sec) — between checks (minimum 30, maximum 86400).
    • Timeout (sec) — how long to wait for a response in one attempt (1–60, default 10).
    • Consecutive fails — number of consecutive failed checks before an alert (default 1; typically 2–3 to avoid reacting to single network jitters).
    • Expected HTTP status (only http) — 0 means “any 2xx”, otherwise an explicit code (e.g., 200).
    • Certificate buffer, days (only tls) — 0 = do not check; 30 = alert 30 days before expiry.
    • Alert / recovery text — what will appear in the push.
Окно терминала
# HTTP-проверка
curl -X POST "$NOTIFLY_URL/monitor" \
-H "Content-Type: application/json" \
-H "X-Gotify-Key: <client-token>" \
-d '{
"appid": 12345,
"name": "Прод-сайт",
"kind": "http",
"target": "https://example.com/health",
"intervalSec": 60,
"timeoutSec": 10,
"expectedStatus": 0,
"consecutiveFails": 2,
"alertMessage": "Сайт недоступен!",
"alertPriority": 9,
"recoveryMessage": "Сайт снова отвечает."
}'
# TCP-проверка
curl -X POST "$NOTIFLY_URL/monitor" \
-H "Content-Type: application/json" \
-H "X-Gotify-Key: <client-token>" \
-d '{
"appid": 12345,
"name": "Postgres-реплика",
"kind": "tcp",
"target": "db-replica.internal:5432",
"intervalSec": 60,
"timeoutSec": 5,
"consecutiveFails": 3,
"alertMessage": "Реплика не принимает соединения.",
"recoveryMessage": "Реплика снова доступна."
}'
# TLS — алёрт за 30 дней до истечения сертификата
curl -X POST "$NOTIFLY_URL/monitor" \
-H "Content-Type: application/json" \
-H "X-Gotify-Key: <client-token>" \
-d '{
"appid": 12345,
"name": "SSL prod",
"kind": "tls",
"target": "example.com:443",
"intervalSec": 3600,
"timeoutSec": 10,
"expectedStatus": 30,
"alertMessage": "Сертификат example.com истекает менее чем через 30 дней!"
}'
# DNS — проверка резолва через указанный сервер
curl -X POST "$NOTIFLY_URL/monitor" \
-H "Content-Type: application/json" \
-H "X-Gotify-Key: <client-token>" \
-d '{
"appid": 12345,
"name": "DNS prod-зоны",
"kind": "dns",
"target": "example.com@8.8.8.8:53",
"intervalSec": 300,
"timeoutSec": 5,
"alertMessage": "DNS-зона example.com не резолвится через 8.8.8.8"
}'

Typical kind + target combinations for common tasks:

{ "kind": "smtp", "target": "mx.example.com:25" } // SMTP-MX responds
{ "kind": "imap", "target": "imap.example.com:143" } // IMAP is alive (plain)
{ "kind": "pop3", "target": "pop3.example.com:110" } // POP3 is alive
{ "kind": "ssh", "target": "jump.example.com:22" } // SSH banner
{ "kind": "redis", "target": "redis.example.com:6379"} // Redis responds to PING
{ "kind": "postgres", "target": "pg.example.com:5432" } // PostgreSQL accepts connections
{ "kind": "mysql", "target": "db.example.com:3306" } // MySQL sends handshake
{ "kind": "tls", "target": "smtps.example.com:465", "expectedStatus": 14 } // SSL certificate is valid and will not expire within the next 14 days

If you have a Notifly MCP server configured (see MCP), just ask:

Create a monitor for https://api.example.com/health, check every 30 seconds, alert after 3 consecutive failures with the text “API is down”, recovery “API is working again”.

Available MCP tools: list_monitors, create_monitor, update_monitor, delete_monitor, pause_monitor, resume_monitor.

MethodPathDescription
GET/monitorList monitors for the user
POST/monitorCreate
PUT/monitor/:idUpdate settings (cannot change appid)
DELETE/monitor/:idDelete
POST/monitor/:id/pausePause (checks are not performed)
POST/monitor/:id/resumeResume

All endpoints, except pause/resume, require a client (or MCP) token with write permission.

See the same reasoning for heartbeat: an indexed point query by next_check_at is cheaper and faster than listing S3 objects.

  • Minimum interval: 30 seconds (to avoid overloading YDB).
  • Maximum interval: 24 hours (for infrequent checks it’s better to use heartbeat).
  • One invocation of notifly-monitor processes at most 200 overdue monitors (cron runs once per minute; checks are performed sequentially).
  • Requests originate from Yandex Cloud, so the target resource must be reachable from the public internet (or from a private YC network if the function is in the appropriate VPC).
  • All protocol-specific checks (smtp/imap/pop3/…) are performed without StartTLS — for the encrypted variant use the already-encrypted port (:465, :993, :995, :6380) with kind: "tls" or kind: "tcp".