Most uptime monitoring is HTTP monitoring: send a request to a URL, judge the response. That works for anything that serves web pages or APIs. It does nothing for the Postgres server on port 5432, the SMTP relay on 587, the SSH bastion, the Redis instance, or the message broker your workers depend on. None of those speak HTTP, and all of them can take your product down without a single web page going dark.
A TCP port monitor covers that gap. It is also the check type most often set up with the wrong expectations.
The sections below cover what a TCP check actually measures, when to use it instead of (or alongside) an HTTP check, and what neither one can see.
What does a TCP port check do?
A TCP check opens a connection to a host and port and reports up if the connection completes inside the timeout. That is the whole test. The handshake is the three-way exchange defined in RFC 9293. On RealUptime a TCP monitor sends no bytes after the handshake and reads none back. It never speaks the protocol on the other end, never authenticates, never touches data.
So "up" means: the host is reachable from the probe region, something is listening on that port, and the operating system accepted the connection. "Down" means one of a refused connection, an unreachable host, or a connection that did not complete in time.
Everything else works like an HTTP monitor. The check runs from the four core regions by default (Virginia, California, Frankfurt, Tokyo), on the interval you chose. On Growth you can pick from eight live regions, and on Scale from all ten.
Connect time is recorded per region, so latency charts and history bars fill in the same way. A region has to keep failing for 3 minutes before it flips to down. A state change then fires the same alert fan-out: incidents, status page components, subscriber email, Slack, PagerDuty, webhooks.
What does a TCP check prove?
A TCP check proves more than it might seem.
- The network path works. DNS resolved, routing got there, no firewall dropped the packet. For a database on a private host reachable only through a specific address, this is most of what you want to know.
- The process is running and accepting. A crashed database does not listen. A database that is up but has exhausted its connection slots still accepts the TCP handshake and then refuses at the protocol layer. PostgreSQL's
max_connectionsis the usual example. A TCP check will not see that refusal, which brings us to the limits. - Per-region reachability. The check runs from several places. A firewall rule that blocks one cloud provider's range, or a route that breaks between Europe and your host, shows up as one region down while the others stay green. That is a different fault from "the service is down," and the per-region view separates them.
What does a TCP check not prove?
- That the service works. Accepting a connection is not the same as answering a query. A Postgres server in recovery, a Redis instance that is out of memory and rejecting writes, an SMTP server that greets you and then refuses to relay: all accept the handshake. A TCP monitor is a reachability check, not a functional check. If you need "can it do its job," you need something that speaks the protocol. Two options work: an HTTP endpoint in your own app that runs a real query, or a heartbeat from a job that exercises the service and pings only on success.
- That the right thing is listening. Port 5432 answering proves something accepted a connection on 5432. If a deploy left a stale process there, the check is green and the app is broken.
- Anything about the data. By design. A monitor that reads your database traffic is a monitor you have to trust with your database traffic. Connect-and-close means there is nothing to trust it with.
The TLS option, and the deliberate choice inside it
Turning on TLS adds a handshake (TLS 1.3 is RFC 8446) to the success condition and starts reading the certificate the server presents. Expiry alerts then work exactly as for an HTTPS monitor: 30, 14, 7 and 1 days before the certificate lapses, with the remaining days shown on the monitor.
One decision in there is worth understanding. The up/down result depends on the TLS handshake completing, not on the certificate chaining to a public authority or matching the hostname. Databases and internal brokers routinely present private or self-signed certificates. Reporting a healthy database as down because it was signed by your own CA would be a false alarm, and false alarms are what teach people to ignore monitors. The expiry date is still read and still alerted on, because that is the part you need to act on.
If you do need "the certificate is publicly valid," that is an HTTPS monitor's job, and for a public-facing endpoint you should have one.
How do you choose between TCP and HTTP checks?
Choose HTTP for anything that serves web traffic and TCP for everything that does not.
A short table, since this is the decision most people are really making:
| The thing | Check type | Why |
|---|---|---|
| Public website, API, login page | HTTP | You want status code, response time, and optionally a body assertion |
| Postgres, MySQL, Redis, brokers | TCP (TLS if enabled) | No HTTP endpoint; reachability and expiry are the useful signals |
| SMTP, IMAP | TCP with TLS | Same, plus certificate expiry on mail (SMTP is RFC 5321) is a classic silent failure |
| SSH bastion | TCP | Proves the door is open; nothing else should be exposed |
| A service behind a private CA | TCP with TLS | Handshake proves listening; expiry tracked; validity not judged |
| "Does the app actually work" | HTTP against a real endpoint, or a heartbeat | TCP cannot answer this |
The pattern that works for most small teams has three parts:
- An HTTP check on every customer-facing surface.
- A TCP check on every dependency the app cannot live without.
- A heartbeat on every scheduled job.
The result is three check types, three different questions, and no overlap.
Two mistakes we see
Monitoring the load balancer's port and calling the database monitored. The load balancer accepts connections regardless of what is behind it. Point the TCP check at the thing you actually care about, or at the closest address that would refuse if it died.
Exposing a port to the internet so a monitor can reach it. Do not open 5432 to the world for the sake of a check. If a dependency is not reachable from outside, monitor it from inside. A heartbeat job on a host that can reach it, pinging on success, gives you the same "it stopped working" signal without a new attack surface. The TCP check is for things that are already meant to be reachable from where the probes run.
Where to start
TCP monitors are part of the free tier, from the four core regions, with TLS and expiry tracking included. Full details, including exactly what counts as up, are in the TCP monitor docs. If the only thing you have monitored so far is the homepage, the database behind it is a good second monitor to add, and it takes about the same minute.