Port monitor (expected state)
Port monitor periodically checks a list of TCP ports on a host and compares the actual state with the expected one. 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 forward was left behind after a deploy).
This tool is about configuration consistency, not about whether a service is alive. You set a reference (“22 and 443 are open” or “5432 is closed from the outside”) — Notifly signals on any deviation from it.
How it differs from related checks
Section titled “How it differs from related checks”| Tool | Question it answers | When to use |
|---|---|---|
| Port monitor | ”Are these specific ports in the right state?“ | keep an open/closed reference for a list of ports |
Monitor kind: "tcp" | ”Is this one port open / responding?“ | check availability of a single service (handshake) |
| Port scan | ”What is open on this host at all?“ | one-off or periodic discovery of open ports |
Roughly: kind: "tcp" checks one port for liveness, port-scan is reconnaissance
(“show everything open”), and port-monitor ensures that a known set of ports
keeps a known state.
How it works
Section titled “How it works”- Every
intervalSecNotifly opens a TCP connection (handshake) to each port fromexpectedPortsonhost— up to 16 ports in parallel, with a per-port timeout oftimeoutSec. - A port is considered open if the connection is established, and closed if it is refused or times out.
- 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 all ports return to the expected state, the monitor is
upagain and (ifrecoveryMessageis set) a recovery message is sent.
The current snapshot of open ports is stored in lastSeenPorts (CSV like
"22,443") and is updated on every check — it shows what the monitor
currently “sees”.
Alert and recovery
Section titled “Alert and recovery”- The alert goes to channel
appidwith titlealertTitleand priorityalertPriority. The server appends toalertMessagewhat exactly happened — the list of expected ports that closed or of ports found open — and a line with the host. - One alert per outage. While the monitor is
downand the alert has already been sent (thealertedAtfield), there are no repeat alerts. If you change the check parameters viaPUT(host,expectedPorts,alertMode,intervalSec,timeoutSec), the monitor is checked right away andalertedAtis cleared — if the problem persists, a fresh alert arrives. - Recovery is sent only if
recoveryMessageis set and an alert about the outage was actually sent. The title isrecoveryTitle, or, if it is empty, a default title with the host name; the priority is the same as the alert’s.
Alerts are ordinary channel messages, so channel rules apply to them: during
quiet hours their escalation to external delivery methods
is suppressed if alertPriority is below the breakthrough threshold (8 by
default). During an active maintenance window
checks keep running and are written to history, but the outage alert is not
sent — if the ports are still in the wrong state after the window, the alert
arrives with the very first check.
A port monitor is a source with check history, so it appears on its channel’s
status page, and up ↔ down transitions open and
close auto-incidents there.
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 in prod.
expectedPorts: "9229,5005,8000",alertMode: "open"— catch a forgotten debug port after a release.
Limitations
Section titled “Limitations”- Public addresses only. A host that resolves to a private, loopback or
link-local address (
10.x,192.168.x,127.0.0.1,169.254.x, etc.) is rejected with400on create and update. The same check is repeated on every connection: if a DNS name later starts pointing to a private address, the connection is blocked. - Quota. Each monitor check is one event of the
dailyEventsdaily quota (regardless of how many ports are in the list). When the quota is exhausted, checks are postponed until the next day — see Quotas and tariffs.
Fields
Section titled “Fields”| Field | Type | Required | Description |
|---|---|---|---|
appid | number | yes | ID of the channel to send alerts to (your own or shared with send permission) |
name | string | yes | monitor name, 1–200 characters |
host | string | yes | host or IP (without / and spaces), e.g. db.example.com or 203.0.113.10 |
expectedPorts | string | yes | comma-separated list of ports: "22,80,443"; each port 1–65535 |
alertMode | string | yes | "closed" (expect ports to be open, alert on closing) or "open" (expect ports to be closed, alert on opening) |
intervalSec | number | yes | check interval in seconds, 60–86400 |
timeoutSec | number | no | per-port timeout in seconds, default 5, maximum 10 |
alertTitle | string | no | alert title |
alertMessage | string | yes | alert text, 1–2000 characters |
alertMessageMarkdown | bool | no | treat alert and recovery text as Markdown |
alertPriority | number | no | priority of the alert and recovery, 0–10 (default 0) |
recoveryTitle | string | no | recovery message title |
recoveryMessage | string | no | recovery text; empty = do not send |
Besides these fields, the response contains the monitor state:
status—pending(created or resumed, no checks yet),up(everything in the expected state),down(a deviation or a check error) orpaused(checks are disabled);lastSeenPorts— ports open at the last check (CSV);lastError— description of the problem at the last check (empty if all is well);lastCheckAt,nextCheckAt— time of the last and the next check;alertedAt— when the alert about the current outage was sent (nullif there was no alert);id,created.
Creation
Section titled “Creation”Via the admin UI
Section titled “Via the admin UI”- Open app.notifly.ru → Monitors → the Ports tab → the Monitors sub-tab.
- Click “Add port monitor” (or “Create” on the monitors page → the “Open ports of a host” card).
- Enter a name and host, add ports one by one (type + Enter), choose
“Alert when”: “Port closed” (
closed) or “Port opened” (open), the per-port timeout, interval, channel and notification text; optionally enable the recovery message. - Use the “Test” button to see which ports are currently open/closed
right in the dialog (calls
POST /port-monitor/test).
A monitor created in the admin UI gets priority 5; set a different
alertPriority via the API. Each monitor row has a “Scan all ports on this host
(Discovery)” button — it opens the port scan with the
host already filled in. It also works the other way round: a completed scan
turns into a monitor with a single
POST /port-scan/:id/to-monitor request.
Via the REST API
Section titled “Via the 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: <api-token>" \ -d '{ "appid": 12345, "name": "web-1 — 80/443 must listen", "host": "web-1.example.com", "expectedPorts": "80,443", "alertMode": "closed", "intervalSec": 300, "timeoutSec": 5, "alertTitle": "Port closed on web-1", "alertMessage": "One of the expected-open ports (80/443) is unavailable.", "alertPriority": 8, "recoveryMessage": "web-1 ports are open again." }'A DB must not be reachable from outside — alert if a port opens:
curl -X POST "$NOTIFLY_URL/port-monitor" \ -H "Content-Type: application/json" \ -H "X-Notifly-Key: <api-token>" \ -d '{ "appid": 12345, "name": "Public IP — DB must not be exposed", "host": "203.0.113.10", "expectedPorts": "5432,6379,27017", "alertMode": "open", "intervalSec": 600, "alertTitle": "Database reachable from the internet!", "alertMessage": "One of the DB ports (5432/6379/27017) is open on the public IP.", "alertPriority": 10 }'The response contains the saved monitor object (id, status: "pending",
nextCheckAt, etc.); the first check is scheduled “now”.
PUT /port-monitor/:id takes the same full set of fields as creation
(including appid), but the monitor’s channel does not change on update.
Check without saving
Section titled “Check without saving”POST /port-monitor/test checks the host’s ports right now and returns the
actual state — without creating a monitor. Useful to verify the reference
before creating one:
curl -X POST "$NOTIFLY_URL/port-monitor/test" \ -H "Content-Type: application/json" \ -H "X-Notifly-Key: <api-token>" \ -d '{ "host": "web-1.example.com", "expectedPorts": "22,80,443,5432", "timeoutSec": 5 }'Response:
{"open": [22, 80, 443], "closed": [5432]}Here a timeout and a refusal both end up in closed, and a timeoutSec
outside 1–10 is replaced with 5. If expectedPorts contains no valid port,
the response is {"open": [], "closed": [], "error": "no ports to scan"}.
Check history
Section titled “Check history”Every check is written to history: success, duration, error text and
metadata — openPorts, expectedPorts, alertMode, and on a deviation also
missing (expected ports that closed, closed mode) or opened (ports that
opened, open mode). In the admin UI the history is shown in the monitor’s
edit dialog: a chart of checks and a “port × time” grid where each cell shows
whether the port was in the right state.
Via the API: GET /monitor-history/port_monitor/:id — aggregated history,
GET /monitor-history/port_monitor/:id/log?limit=50 — the latest entries
(up to 200) with the metadata field.
Pause and resume
Section titled “Pause and resume”POST /port-monitor/:id/pause— disables checks (statuspaused).POST /port-monitor/:id/resume— brings the monitor back to work (statuspending, a check is scheduled “now”).
REST API
Section titled “REST API”| Method and path | Purpose |
|---|---|
GET /port-monitor | list of port monitors |
POST /port-monitor | create |
POST /port-monitor/test | one-off check without saving |
PUT /port-monitor/:id | update |
DELETE /port-monitor/:id | delete |
POST /port-monitor/:id/pause | pause |
POST /port-monitor/:id/resume | resume |
Authorization is the X-Notifly-Key header with an API token.
Creating, updating, deleting and pausing require a token with write access
(a read token gets 403); a token scoped to channels sees and changes only
the monitors of its channels. Channel tokens and share tokens are not accepted
on these endpoints (403).
See also
Section titled “See also”- Port scan — discovery of all open ports on a host.
- Monitors — checking one port’s availability (
kind: "tcp") and other protocols. - Maintenance windows and quiet hours — when alerts don’t wake you up.