Port monitor (expected state)
Port monitor periodically scans the list of TCP ports on a host and compares the actual state with the expected. An alert is sent not when a port is “unavailable”, but when the state differs from what you configured:
- an expected-open port suddenly closed (service crashed, firewall rule dropped);
- a port that should be closed suddenly opened (an extra service started, someone exposed a DB, a port-forwarding remained after deploy).
This tool is about configuration consistency, not about whether a service is alive. You set a reference (“22 and 443 are open, 5432 is closed externally”) — Notifly signals on any deviation from it.
How it differs from related checks
Section titled “How it differs from related checks”| Инструмент | Вопрос, на который отвечает | Когда брать |
|---|---|---|
| Монитор портов | «Эти конкретные порты в нужном состоянии?» | следить за эталоном open/closed по списку портов |
Monitor kind: "tcp" | «Этот один порт открыт / отвечает?» | проверить доступность одного сервиса (handshake) |
| Port scan | «А что вообще открыто на хосте?» | разовое или периодическое обнаружение открытых портов |
Roughly: kind: "tcp" — check one port for liveness, port-scan — reconnaissance
(“show everything open”), and port-monitor — ensure a predefined set of ports keeps a predefined state.
How it works
Section titled “How it works”- Once every
intervalSecNotifly connects (TCP handshake) to each port fromexpectedPortsonhost, with per-port timeouttimeoutSec. - Each port is classified as open (connection established) or closed (refused/timeout).
- The result is compared with
alertMode:alertMode: "closed"— ports listed inexpectedPortsshould be open; an alert is sent when an expected-open port closes;alertMode: "open"— ports listed inexpectedPortsshould be closed; an alert is sent when such a port opens.
- When the state returns to the expected — a recovery message is sent
(if
recoveryMessageis set; empty = do not send recovery).
The current snapshot of state is stored in lastSeenPorts (CSV like "22,443")
and is updated on each check — it shows what the monitor currently “sees”.
Typical scenarios
Section titled “Typical scenarios”- A web server must listen on 80 and 443.
expectedPorts: "80,443",alertMode: "closed"— alert if nginx crashed or the port closed. - A DB must not be exposed publicly.
expectedPorts: "5432,6379,27017",alertMode: "open"— alert if Postgres/Redis/Mongo suddenly become reachable from the public address. - No debug ports should exist in prod.
expectedPorts: "9229,5005,8000",alertMode: "open"— catch a forgotten debug port after a release.
Fields
Section titled “Fields”| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
appid | number | да | ID of the app/channel to send alerts to |
name | string | да | monitor name, 1–200 characters |
host | string | да | host or IP (without / and spaces), e.g. db.example.com or 203.0.113.10 |
expectedPorts | string | да | comma-separated list of ports: "22,80,443"; each port 1–65535 |
alertMode | string | да | "closed" (expect ports to be open, alert on closing) or "open" (expect ports to be closed, alert on opening) |
intervalSec | number | да | check interval in seconds, 60–86400 |
timeoutSec | number | нет | per-port timeout in seconds, default 5, maximum 10 |
alertTitle | string | нет | alert title |
alertMessage | string | да | alert text, 1–2000 characters |
alertMessageMarkdown | bool | нет | treat alertMessage as Markdown |
alertPriority | number | нет | priority 0–10 (0 = use channel’s defaultPriority) |
recoveryTitle | string | нет | recovery message title |
recoveryMessage | string | нет | recovery text; empty = do not send recovery |
The status field in responses is the monitor state: pending (created, no checks yet), up (everything in the expected state), down (there is a deviation, alert sent) or paused (checks are disabled).
Creation
Section titled “Creation”Via the admin UI
Section titled “Via the admin UI”- Open app.notifly.ru → Port monitoring.
- Click “Create”, fill in host, port list, mode (
open/closed), interval and alert text. - Use the “Test” button to see which ports are currently open/closed
right in the dialog (calls
POST /port-monitor/test).
Via REST API
Section titled “Via REST API”A web server must listen on 80 and 443 — alert if a port closes:
curl -X POST "$NOTIFLY_URL/port-monitor" \ -H "Content-Type: application/json" \ -H "X-Notifly-Key: <client-token>" \ -d '{ "appid": 12345, "name": "web-1 — 80/443 должны слушать", "host": "web-1.example.com", "expectedPorts": "80,443", "alertMode": "closed", "intervalSec": 300, "timeoutSec": 5, "alertTitle": "Порт закрылся на web-1", "alertMessage": "Один из ожидаемо-открытых портов (80/443) недоступен.", "alertPriority": 8, "recoveryMessage": "Порты web-1 снова открыты." }'A DB must not be accessible externally — alert if a port opens:
curl -X POST "$NOTIFLY_URL/port-monitor" \ -H "Content-Type: application/json" \ -H "X-Notifly-Key: <client-token>" \ -d '{ "appid": 12345, "name": "Публичный IP — БД не должна торчать", "host": "203.0.113.10", "expectedPorts": "5432,6379,27017", "alertMode": "open", "intervalSec": 600, "alertTitle": "База доступна из интернета!", "alertMessage": "Один из портов БД (5432/6379/27017) открыт на публичном IP.", "alertPriority": 10 }'The response will contain the saved monitor object (id, status: "pending",
lastSeenPorts, nextCheckAt, etc.).
Check without saving
Section titled “Check without saving”POST /port-monitor/test scans the host right now and returns
the actual state — without creating a monitor. Useful to verify the
reference before creating:
curl -X POST "$NOTIFLY_URL/port-monitor/test" \ -H "Content-Type: application/json" \ -H "X-Notifly-Key: <client-token>" \ -d '{ "host": "web-1.example.com", "expectedPorts": "22,80,443,5432", "timeoutSec": 5 }'Response:
{ "open": [22, 80, 443], "closed": [5432] }If there’s nothing to parse, it will return {"open": [], "closed": [], "error": "no ports to scan"}.
Pause and resume
Section titled “Pause and resume”POST /port-monitor/:id/pause— disables checks (status becomespaused, next check is pushed far into the future).POST /port-monitor/:id/resume— brings the monitor back to work (statuspending, a check is scheduled “now”).
REST API
Section titled “REST API”| Метод и путь | Авторизация | Назначение |
|---|---|---|
GET /port-monitor | client-token | list of port monitors |
POST /port-monitor | client-token (write) | create |
POST /port-monitor/test | client-token | one-off scan without saving |
PUT /port-monitor/:id | client-token (write) | update |
DELETE /port-monitor/:id | client-token (write) | delete |
POST /port-monitor/:id/pause | client-token (write) | pause |
POST /port-monitor/:id/resume | client-token (write) | resume |
Authorization header is X-Notifly-Key: <client-token>; you can also use Basic Auth (admin UI login/password) or an MCP code with write permission.