Port Monitoring
Watch any TCP or UDP port — SSH, SMTP, a database, a message broker, a game server — and get told when it stops accepting connections, or when the wrong thing starts answering.
What is a port monitor?
A port monitor opens a connection to one host:port on a schedule and reports Up if the service accepts it, Down if it doesn't. Optionally it can also send an opening line and require specific text in the reply — so you're checking that the right service answered, not just that something is listening.
Example: "db.example.com should accept a connection on port 5432. If it ever stops, page me."
This is the check for everything that isn't a web page. Your website monitor can't tell you your Postgres instance stopped accepting connections, your SSH daemon died after a kernel upgrade, or your Redis box came back from a reboot with the service disabled. A port monitor can.
When to use one
- Databases and data stores. Postgres, MySQL, Redis, MongoDB, Elasticsearch — anything that listens on a port and isn't spoken to over HTTP.
- Mail. SMTP on 25/465/587, IMAP on 993, POP3 on 995. Pair it with the send/expect assertion below and you're verifying the mail server actually greets you, not just that the socket opens.
- Remote access. SSH on 22, RDP on 3389 — the ports whose silent death you only discover when you urgently need them.
- Message brokers and internal services. RabbitMQ, Kafka, a custom TCP daemon, a game server.
- Firewall and security-group regressions. A port that should be closed to the world is worth watching too: a monitor that suddenly goes Up on a port you thought was firewalled is a finding.
When to use something else
| You want to know | Use this instead |
|---|---|
| "Is my website returning 200 and loading fast?" | Website monitor — it speaks HTTP, follows redirects, checks status codes, keywords and certificates. A port monitor on 443 only proves the socket opens; it says nothing about what your app returns. |
| "Is this host reachable at all?" | Ping monitor — an ICMP echo, no port involved. Use it when the box has no service you can connect to, or when you want to separate "the host is gone" from "the service is gone". |
| "Do my DNS records still say what I expect?" | DNS monitor — a port monitor on 53 tells you a resolver is listening, not that it returns the right answer. |
A common, useful pairing is a Ping monitor plus a Port monitor on the same host. If both go Down, the machine is gone. If only the port goes Down, the machine is fine and the service crashed. That distinction saves real time at 3am.
How it works
Every check, Enori:
- Resolves your hostname to an IP address.
- Connects to that address on your port — a TCP connection, or a UDP datagram if you chose UDP.
- Optionally sends your opening line and checks the reply for the text you expect.
What counts as Up: the connection succeeded (TCP), or your datagram got a reply (UDP) — and, if you configured one, the reply contained your expected text.
What counts as Down:
| Reason | What happened |
|---|---|
| Connection refused | Something answered at the network level and actively refused you. The host is up; nothing is listening on that port. |
| Timed out | Nothing answered within your timeout. Usually a firewall silently dropping the packet, or a host that has stopped responding. |
| Host unreachable | The network could not deliver to that host — it's down or has no route. |
| Network unreachable | No route to that network at all. |
| No UDP response | UDP only: the datagram was sent and nothing came back within the timeout. |
| Response didn't match | The connection succeeded but the reply didn't contain your expected text. See the send/expect assertion. |
| Host or port is not valid | The hostname couldn't be resolved, or there's no usable port on the monitor. |
| Blocked target | The hostname resolves to a private, internal or reserved address. Enori only monitors publicly reachable hosts. |
Response time is how long the connection (or the UDP round trip) took. A failed connection records no response time — a fast refusal shouldn't drag your latency chart down. A check that connected but failed the text assertion does keep its measured time, because the connection genuinely happened.
How often. You pick the interval when you create the monitor — 1 minute through 24 hours, defaulting to 5 minutes. A 30-second interval is available as an add-on.
While a monitor is Down, Enori automatically re-checks it every 30 seconds — regardless of your normal interval, as long as your interval is longer than that. You don't have to tighten the interval to find out quickly when a service comes back; recovery is picked up within half a minute either way.
Setting one up
Go to Monitors, click Add Monitor, and pick Port. The wizard has two steps — Type and Configure — and everything below lives on the Configure step.
Screenshot: the Port option on the monitor-type picker (step 1).
1. Monitor Name (required)
Anything you'll recognise in an alert at 3am. e.g. "Postgres — primary".
2. Group (optional)
Put related monitors together, or leave it ungrouped.
3. Hostname or IP (required)
The bare hostname or IP address — example.com, 192.0.2.10. No scheme, no path, and no port.
Typing the port into this field does not work and is the single most common setup mistake. example.com:443 is rejected outright with "Host or domain is not valid.", and https://example.com:443 is accepted but the :443 is silently dropped. The port belongs in the next field, always.
Private, internal and reserved addresses are rejected when you save — Enori checks from the public internet, so it can only reach hosts the public internet can reach.
4. Port Number (required)
A whole number from 1 to 65535. Type it, or click one of the one-click presets underneath:
| Preset | Port | Preset | Port |
|---|---|---|---|
| HTTPS | 443 | DNS | 53 |
| HTTP | 80 | MySQL | 3306 |
| SSH | 22 | Postgres | 5432 |
| FTP | 21 | Redis | 6379 |
| SMTP | 25 | RDP | 3389 |
The presets are a shortcut, not a limit — any port in range is fine.
The port is part of the monitor's identity. You can watch the same host on as many ports as you like: db.example.com:5432 and db.example.com:22 are two separate monitors, not a duplicate.
5. Protocol
TCP (the default) or UDP.
- TCP opens a real connection. The handshake completing is the proof, so nothing else is required.
- UDP sends a datagram and waits for a reply. Because UDP is unreliable by design, Enori sends the probe up to three times within your timeout before reporting no response — a single dropped packet on the network is not treated as an outage. UDP has no handshake, so a reply is the only possible proof — which is why UDP requires a send payload (next field). Without one, saving fails with "UDP checks require a send payload to elicit a response".
If you're not sure, you want TCP. Almost everything you'd monitor — databases, SSH, SMTP, brokers — is TCP.
5b. IP version (optional)
Auto (the default), IPv4 or IPv6.
Most hosts resolve to both. On Auto, Enori connects to whichever address the resolver returns first — which can differ between checks, so you cannot be certain which of your two stacks is actually being tested. Pick a version when that matters:
- IPv6 — the only way to be sure an IPv6-only endpoint is reachable, and the way to catch an
AAAA record that points somewhere stale.
- IPv4 — pins the check to your v4 path while you are migrating, or while v6 is known-flaky.
If the host has no address of the version you chose, the check reports that specifically rather than a generic failure.
6. Send data (optional — required for UDP)
Text written to the service the moment the connection opens. Leave it blank for a plain "can I connect?" check.
The field is plain text with escape sequences for the bytes you can't type: \r \n \t \0 and \xHH for any byte in hex. Most protocol commands are lines that must end in a carriage return and newline, so they look like the placeholder the field shows you:
EHLO example.com\r\nUp to 1024 characters.
7. Expect in response (optional)
Text the service's reply must contain. If it's missing, the check goes Down even though the connection succeeded. Up to 512 characters.
This is what turns "a socket is open" into "the service I expect is running and healthy". It's explained properly in the next section.
The match is a plain substring and case-sensitive — 220 matches, OK does not match ok.
8. Primary Region
Where your monitor runs its checks from. Regions that aren't available yet are marked Coming soon.
Below it, Multi-region verification — a second region re-checks before you're alerted, so one bad network path doesn't page you. It's always on for Base; Pro can turn it off; Business can also choose which regions do the verifying.
Port monitors and regions, today. Region selection and multi-region verification currently apply to Port monitors watching the standard web and DNS ports — TCP 80, 443 and 53, and UDP 53 and 123. A Port monitor on any other port runs its checks from Enori's default location and is not re-verified from a second region. The check itself is unaffected — same connection, same assertion, same alerts, same uptime; only the from where is single-region. Your region choice is saved and takes effect as soon as we enable this for all ports.
9. Check Interval
Defaults to 5 minutes. Options run from 1 minute to 24 hours; 30 seconds requires the add-on.
10. Alert settings
Below the divider on the same step:
- When to alert — how many consecutive failures before you're notified. Defaults to 2 consecutive failures, which absorbs a single flaky connection.
- Notify me via — which of your alert channels to use.
- Repeat Alerts — whether to keep reminding you while it stays Down.
Then click Create Monitor. Enori queues a first check immediately, so you normally see a result within seconds.
Screenshot: the completed Port Configure step with host, port, a preset selected, and the send/expect fields filled in.
Proving the right service answered
A bare TCP connect answers one question: is something accepting connections on this port? That is often enough — and it's the default for a reason. But it has a real blind spot. A port can be held open by something that is no longer doing its job: a mail server that accepts connections and immediately drops them, a database mid-crash-recovery, a load balancer happily accepting on a port whose backend is gone.
Send data and Expect in response close that gap. You send the protocol's opening line, and require specific text in what comes back.
Worked example: SMTP
Say you run a mail server on mail.example.com:25. A plain port check goes Up as long as the daemon is listening. To check it actually speaks SMTP:
| Field | Value |
|---|---|
| Hostname or IP | mail.example.com |
| Port Number | 25 |
| Protocol | TCP |
| Send data | EHLO example.com\r\n |
| Expect in response | 250 |
Here's what happens on each check. Enori connects. Your mail server greets it — 220 mail.example.com ESMTP ready. Enori sends EHLO example.com followed by a carriage return and newline. The server replies with its capability list, every line of which begins 250. Enori finds 250 in the reply and the check is Up.
If the server accepts the connection but never gets as far as a 250 — it's overloaded, it's rejecting your greeting, it's not really an SMTP server anymore — the check goes Down with "Connected, but the response did not contain the expected text." That is exactly the failure a plain connect check would have called healthy.
Which text should I expect?
Whatever the protocol makes a promise about. A few common ones:
| Service | Send | Expect | What it proves |
|---|---|---|---|
| SMTP (25 / 587) | EHLO example.com\r\n | 250 | The server completed an SMTP greeting exchange. |
| SMTP, greeting only | (blank) | 220 | The server issued its ready banner. Simpler; proves less. |
| SSH (22) | (blank) | SSH-2.0 | An SSH daemon announced its version — SSH sends this on connect. |
| FTP (21) | (blank) | 220 | The FTP service is ready. |
| Redis (6379) | PING\r\n | PONG | Redis is not just listening, it's answering commands. |
| A custom health port | HEALTH\r\n | whatever your service returns | Your own protocol's healthy answer. |
Two rules of thumb: leave Send data blank for services that greet you first (SSH, FTP, SMTP and POP3 all announce themselves the moment you connect — there's nothing to send), and fill it in when you want the reply to a specific command rather than the greeting.
One thing to watch: some services greet in binary rather than text — MySQL's handshake is the common example. Expecting readable text from those rarely works. For a binary protocol, either leave Expect in response blank and let the connection itself be the signal, or expect a short fragment you have confirmed by hand.
How the reply is read
Enori keeps reading the service's response until your expected text appears, the reply stops, or your timeout runs out. It reads up to 4 KB of the reply.
This matters for the greet-first protocols above. When you send EHLO and expect 250, the very first thing the server sends is its 220 greeting — the 250 you're waiting for arrives afterwards, in a separate piece of the conversation. Enori reads through both, so the assertion checks the whole reply, not just the first fragment. The same is true for multi-line banners, which servers routinely split across several pieces.
One caveat worth knowing: if your expected text never appears, the check waits out the full timeout before failing. Keep the timeout sensible (the default is 30 seconds) and expect something the service actually sends early.
UDP
UDP is connectionless — there is no handshake to observe, so the only proof a UDP service is alive is that it replies to something. That's why Send data is required for UDP and why the payload has to be something the service will actually answer.
Enori sends your datagram, then waits for a reply from the same host and port. A reply means Up. Silence until the timeout means Down with "No UDP response within {n}s".
Because most UDP protocols are binary, the \xHH escape is what makes them expressible — you can spell out an exact byte sequence. Note that a binary reply won't be readable text, so Expect in response is often not useful for UDP; leave it blank and treat "it replied at all" as the signal.
If you're monitoring a DNS server, use a DNS monitor rather than a UDP port monitor — it sends a real query and checks the answer.
Reading the results
The Port detail page shows the standard monitor hero (current status, uptime, response-time chart, recent checks) plus one Port-specific panel.
Connection Details
| Field | What it shows |
|---|---|
| Host | The hostname or IP being checked. |
| Port | The port being checked. |
| Timeout | How long a connection may take before it's called a timeout. |
| Resolved IP | The address the last check actually connected to. Useful when a hostname is behind a load balancer or has just been repointed. |
Recent Checks
Each entry shows Success or Failure, the response time, and — on a failure — the exact error message in quotes. That message is the fastest route to the cause: "Connection refused" and "Connection timed out after 30s" mean genuinely different things (see Troubleshooting).
If you configured Expect in response, each check also carries a Keyword found / Keyword not found chip, so you can see at a glance whether the assertion passed. You can export the whole list to CSV.
Screenshot: a Port detail page with the Connection Details panel and a failed check showing "Connected, but the response did not contain the expected text."
Changing a monitor later
Open the monitor and click Edit. Everything you set at creation can be changed: host, port, protocol, send data, expected response, name, group, tags, check interval, and the alert settings.
Two things live only in Edit:
- Timeout (seconds) — 1 to 120, default 30. Lower it for a service on a fast local network; raise it for a distant or deliberately slow one.
- Tags, for filtering and grouping across your account.
Switching protocol from TCP to UDP requires a send payload before the form will save, for the reason described above.
Importing many at once
If you're migrating from another tool or standing up a lot of hosts, use Import on the Monitors page, then the Upload CSV tab and Download Template.
The template's columns are:
url,name,type,interval_seconds,tags,expected_status_code,portport is the last column, and it is the one Port monitors need:
example.com,HTTPS Port,Port,300,"infrastructure",,443
db.example.com,Postgres,Port,300,"infrastructure",,5432Three things to get right:
- Host and port go in separate cells. Put
example.cominurland443inport. A cell likeexample.com:443is rejected. - A Port row without a port is rejected — with the same message the create wizard gives you — rather than being created as a monitor that could never work. Rejected rows are listed after the import and can be downloaded as a CSV to fix and re-upload.
- A file written against the older six-column template still works. The
portcolumn being last means the other types keep importing exactly as before; only Port rows need the extra cell.
Imported monitors are created with a 30-second timeout and alert on the first failed check. Open any of them afterwards to change the failure threshold, timeout, protocol or assertion.
Alerts
Port monitors use the same alerting as every other monitor type — channels, escalation policies, on-call schedules, maintenance suppression. See the Alerts guide for the full picture, and Maintenance windows for suppressing planned restarts.
Any of the Down reasons listed earlier fires your normal Down alert once your failure threshold is met, and recovery fires the usual recovery alert. A failed text assertion is a Down like any other — it opens an incident, counts against uptime, and notifies your channels.
FAQ
Does a port monitor tell me my app is working?
Only as much as the port can. A plain TCP check proves something is accepting connections. Add Expect in response and it proves the service is speaking its protocol. For "is my API returning the right JSON", you want a Website monitor or an API flow instead.
Can I monitor several ports on one host?
Yes — one monitor per port. The port is part of the monitor's identity, so host:22 and host:5432 are two different monitors and Enori won't treat the second as a duplicate.
Why can't I put the port in the hostname field?
Because it wouldn't survive. example.com:443 doesn't parse as a hostname and is rejected; https://example.com:443 parses but the port is discarded, which would leave you with a monitor pointing at nothing. The Port Number field is the only place a port is kept.
Can I monitor a service on my private network?
No. Enori checks from the public internet, so private, internal and reserved addresses are rejected when you save. Either expose the service publicly, or monitor something publicly reachable in front of it.
If your firewall needs to know where our checks come from, see monitoring IP addresses — port checks don't have a fixed source-IP allowlist today, and that page tells you what to do if you need one.
Is the expected-response match case-sensitive?
Yes, and it's a plain substring — no wildcards or regular expressions. 250 matches anywhere in the reply; Ready will not match ready.
Does a failed connection count against my uptime?
Yes. Every Down check counts like any other downtime, including in SLOs and reports. Use a maintenance window around planned restarts so they don't page you or dent your numbers.
Why is my response time 0 ms on failed checks?
A connection that timed out or was refused never completed, so there's no meaningful latency to record — recording one would distort your response-time chart with the speed of your failures. Checks that connected and then failed the text assertion do keep their real response time.
How quickly will I hear about an outage?
Your check interval plus your failure threshold. At the defaults — 5 minutes, 2 consecutive failures — that's roughly 10 minutes. Tighten the interval for services where minutes matter.
Can I manage port monitors through the API?
Yes — see the API reference and the MCP server. The same rules apply: a port between 1 and 65535 is required, and UDP requires a send payload.
Troubleshooting
"Connection refused"
The host is alive and reachable — it answered, and said no. The service isn't running, isn't bound to that port, or is bound only to localhost.
- Check the service is actually running on the host.
- Check what it's listening on. A service bound to
127.0.0.1is invisible from outside even though it looks fine locally; it needs to bind to a public interface. - Confirm the port number. A single transposed digit produces exactly this error.
"Connection timed out after 30s"
Nothing answered at all. This is almost always a firewall, security group or network ACL dropping the packet silently — a refusal produces the error above, whereas a drop produces silence.
- Check the firewall or security group in front of the host allows inbound traffic on that port from the public internet.
- Try from somewhere outside your own network. A port that works from your office and times out from Enori is an allowlist problem, not a service problem.
- If the service is genuinely slow to accept, raise the Timeout in Edit.
"Host unreachable" or "Network unreachable"
The problem is above the service: the address has no route. Check the hostname resolves to the address you expect (the Resolved IP on the detail page tells you what we connected to), and that the host is up — a Ping monitor on the same host will separate "the machine is gone" from "the service is gone".
"Connected, but the response did not contain the expected text."
The socket opened, so the service is listening — but the reply didn't contain your Expect in response value.
- Check what the service actually sends. From a terminal,
nc mail.example.com 25(ortelnet) shows you the real banner. Copy the text from there rather than guessing it. - Check case. The match is case-sensitive.
- Check you're expecting the right stage. Expecting
250without sending anEHLOwill fail on most mail servers — the greeting is220;250only comes as a reply to a command. - Check your escapes. A command sent without
\r\nis often never processed, so the server never replies to it.EHLO example.comandEHLO example.com\r\nare very different requests. - Consider expecting less. A shorter, stabler fragment (
220,SSH-2.0) survives version upgrades that a full banner string won't.
"Connected, but the service sent no response before closing."
The connection was accepted and the service closed it without sending anything. Usually you've connected to something that isn't the protocol you think it is, or the service is rejecting your source address before it says a word. Try the connection by hand to see the same behaviour, and confirm the port.
"No UDP response within 30s"
UDP silence has three common causes, in order of likelihood:
- The payload doesn't elicit a reply. Most UDP services only answer a well-formed request. An arbitrary string usually gets ignored, which is indistinguishable from the service being down. Send something the protocol actually answers.
- A firewall is dropping it — outbound to the port, or the reply on the way back.
- The service is genuinely down. UDP has no "refused", so this is what down looks like.
If you're monitoring DNS this way, switch to a DNS monitor — it sends a real query and validates the answer.
"Host or port is not valid"
The hostname can't be resolved, or the monitor has no usable port. Confirm the hostname resolves publicly, and that the Port Number field is filled in — a port typed into the hostname field is not kept.
The monitor was created but is Down straight away
Check the error message on the first entry in Recent Checks — it will be one of the above. The most common causes for a brand-new monitor are a firewall that has never allowed public traffic on that port, and a port typed into the wrong field.
Reference: limits and defaults
| Setting | Value |
|---|---|
| Hostname or IP | Required. No scheme, no path, no port. Private/reserved addresses rejected |
| Port | Required. 1–65535 |
| Common-port presets | HTTPS 443, HTTP 80, SSH 22, FTP 21, SMTP 25, DNS 53, MySQL 3306, Postgres 5432, Redis 6379, RDP 3389 |
| Protocol | TCP (default) or UDP. UDP requires a send payload |
| Send data | Up to 1024 characters. Escapes: \r \n \t \0 \xHH |
| Expect in response | Up to 512 characters. Case-sensitive substring, matched against up to 4 KB of the reply |
| Timeout | 1–120 seconds, default 30 (editable after creation) |
| Check interval | 30 seconds (add-on), 1m, 2m, 5m, 10m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 24h — default 5 minutes |
| While Down | Re-checked every 30 seconds until it recovers |
| Failures before alert | First failure, 2, 3 or 5 — default 2 |
| Duplicates | Same host on different ports is allowed; the port is part of the monitor's identity |
| Monitors per plan | Base 10 · Pro 50 · Business 200 |
| Check-history retention | Base 30 days · Pro 60 days · Business 90 days |
Related documentation
- Alerts — channels, escalation policies and on-call rotas
- Maintenance windows — suppress alerts during planned restarts
- DNS monitoring — for watching records rather than reachability
- SLOs — reliability targets and error budgets
- Uptime reports — exportable uptime and SLA reports
- Monitoring IP addresses — where our checks come from, and firewall allowlisting
- API reference and MCP server — programmatic management
Feedback or corrections: support@enori.io