Uptime monitoring mostly means HTTP: fetch a URL, check the status code, maybe look for a phrase in the body. That covers websites, APIs, and anything with a health endpoint. It leaves out a surprising amount of infrastructure: the SSH port on a server, a Postgres replica exposed to a partner, a mail server's IMAP and submission ports, a Minecraft server, an RDP host, an FTP drop, and the whole category of machines that have an IP address but no domain name. For those, the right check is simpler and older than HTTP. Try to open a TCP connection to the port. If it's accepted, something is listening. If it's refused or nothing answers, it isn't.
CronAlert now has a TCP port monitor type. This post covers how it works, what a passing check does and doesn't prove, the port we can't reach, why there's no ping option, and when to pair a port check with a health endpoint or a heartbeat so a listening-but-broken service can't hide behind an open port.
How a port check works
You give the monitor a host, which can be a hostname or a public IP address, and a port from 1 to 65535. On each check, CronAlert's worker opens a TCP connection to that host and port and waits for the handshake to complete. The outcome is one of:
- Up: the connection was accepted within the timeout. The response time recorded is the time to establish the connection, which is a reasonable proxy for network latency and for how loaded the host is.
- Down, connection refused: the host answered and said nothing is listening on that port. The service has stopped, or a firewall is actively rejecting.
- Down, timeout: no answer at all within the timeout (10 seconds by default, adjustable to 60). The host is off, the packet is being dropped by a firewall, or the network path is broken.
- Down, resolution failed: the hostname didn't resolve. Same class of problem as any DNS failure.
The check then closes the connection without sending anything. That last part matters: a port check doesn't speak SSH, or the Postgres wire protocol, or Minecraft's handshake. It never authenticates, never runs a query, never appears in your application logs as anything but a connection that opened and closed. It's deliberately dumb, which is what makes it universal.
Everything else about a port monitor works like an HTTP monitor. Checks run at your plan's interval, failures open an incident and go out on every alert channel, you can set a failure threshold so a single dropped handshake doesn't page you, maintenance windows silence planned reboots, and on Team and above the check can run from multiple regions so a routing problem on one path isn't mistaken for an outage. You can create them in the dashboard, through the REST API with type: "tcp", or by asking an AI assistant connected to the MCP server. TCP monitoring is on the Pro plan and above.
Servers with no domain name
This is the case that prompted the feature. Plenty of servers are only ever addressed by IP: a fleet of dedicated boxes at a hosting provider, a database server, a game server, a customer's on-prem machine. Before TCP monitors, you couldn't monitor those with CronAlert at all, and the reason is worth understanding because it will bite you elsewhere. CronAlert's checks run on Cloudflare's network, and Cloudflare refuses to make an HTTP request to a bare IP address; it returns a synthetic 403 (error 1003, "Direct IP access not allowed") without ever contacting the target. An HTTP monitor on http://203.0.113.10 would show as permanently down while the server was fine, so CronAlert rejects those URLs when you try to create them.
TCP checks use a different connection path that reaches public IPs directly. So for an IP-only server you have two good options: a TCP monitor on port 80 or 443 (or 22, if it's not a web server), or, if you want the richer HTTP check with status codes and keyword matching, point a DNS A record like srv1.example.com at the IP and monitor the hostname. Private and internal addresses (10.x, 192.168.x, 127.x, and the rest) are rejected for both types; CronAlert checks from the internet, so the target has to be reachable from it.
What to monitor with a port check
| Service | Port | Notes |
|---|---|---|
| SSH | 22 | The universal "is the box up" check for a Linux server. Pair with a heartbeat for what's happening inside; see monitoring a VPS. |
| PostgreSQL / MySQL / Redis / MongoDB | 5432 / 3306 / 6379 / 27017 | Only if the port is intentionally exposed (a replica for a partner, a managed database with an allowlist that includes the internet). If it isn't, a database health endpoint on your app is the right check instead. |
| IMAP / SMTP submission | 993 / 465, 587 | Mail server reachability. Port 25 can't be checked; see below and the email monitoring guide. |
| Minecraft (Java) | 25565 | Bedrock uses UDP and isn't coverable this way. Game server monitoring covers both. |
| RDP / VNC | 3389 / 5900 | Windows hosts and jump boxes. Consider also heartbeats from scheduled tasks for what runs on them. |
| FTP / SFTP | 21 / 22 | Partner file drops that fail silently until someone asks where the file is. |
| OpenVPN (TCP mode) | 443 or 1194 | WireGuard is UDP-only and can't be checked; use a heartbeat from inside the tunnel. |
| Reverse proxy / load balancer | 80 / 443 | Fine as a reachability check, but an HTTPS monitor by hostname tells you far more; see reverse proxy monitoring. |
| Anything custom | your port | Internal protocols, IoT gateways with a public listener, message brokers exposed for clients. |
The one port we can't reach
Cloudflare blocks outbound connections on port 25 from its network, as most cloud providers do, to keep Workers from being used to send spam. So a TCP monitor can't check SMTP relay on 25. Submission on 587 and implicit TLS on 465 work fine and are what your own clients use. If you specifically need to know that port 25 is answering (you run an inbound mail server), expose a small HTTP endpoint on the host that attempts a local SMTP handshake and returns 200 or 500, and monitor that with an HTTP check. The email monitoring guide has the pattern.
Why there's no ping
Every port-monitoring conversation eventually asks for ICMP ping. CronAlert doesn't offer it, for two reasons. The practical one: raw ICMP isn't available from Cloudflare Workers, and we're not going to run a separate fleet of servers for it when the whole point of CronAlert's pricing is not running servers. The better one: ping is a weak check. It proves the host's kernel is answering, which is true of a machine whose disk is full, whose SSH daemon has crashed, and whose web server is returning nothing but 502s. A TCP connection to a port the host actually serves proves everything ping does and one more thing: the service is listening. If what you want is "is the box alive," monitor port 22. If you want "is the site alive," monitor the site.
The same limitation applies to UDP. DNS servers on 53, WireGuard, QUIC-only services, Bedrock Minecraft, and most game servers other than Java Minecraft use UDP and can't be reached by a TCP handshake. For those, flip the direction: a heartbeat from a script on the host that checks the service locally and pings CronAlert on success, or an HTTP health endpoint you expose next to the service. Both are covered in the game server and VPS guides linked above.
When an open port lies
A TCP check passing means "something accepted a connection on that port." It does not mean the service works. The failure modes that slip past a port check are all variations of listening-but-broken:
- The database accepts connections and rejects every query because it's out of connections, out of disk, or in recovery.
- The web server accepts connections and returns 502 because the app behind it is down. The port check is green; every user sees an error page.
- A Java process is alive and deadlocked, holding the socket open and doing nothing with it. Common on game servers and app servers alike.
- SSH is listening, but the disk is full, so nothing else on the host works.
The fix is layering. Use the port check for reachability, where it's the only option or the cheapest one. Add an HTTP monitor on anything with a web interface, ideally a health endpoint that actually exercises dependencies. Add a heartbeat from inside the host for the things no external check can see, like disk space and whether the nightly backup ran. When the port check goes red and the health check is fine, it's the network. When the health check goes red and the port check is fine, it's the application. Two monitors that disagree tell you where to look.
Setting one up
- Create a monitor and choose the TCP Port type.
- Enter the host (a hostname or public IP) and the port.
- Leave the timeout at 10 seconds unless the host is on a slow link; a healthy handshake takes milliseconds, and a long timeout only delays the alert.
- For a host that occasionally drops a packet, set the failure threshold to 2 so one lost handshake doesn't page you. With 1-minute checks that still means an alert within about two minutes.
- Add a maintenance window covering any scheduled reboot, such as unattended-upgrades on a Sunday morning.
- Give it a name that tells the on-call person what it is: "db-replica-1 Postgres 5432," not "TCP monitor 3."
Then test the alert: stop the service for a minute, or block the port, and confirm the down and recovery notifications arrive where you expect.
Frequently asked questions
What does a TCP port monitor check?
Whether a TCP connection to host:port completes within the timeout. Accepted is up; refused, timeout, or DNS failure is down. No application data is exchanged.
Can I monitor a server by IP?
Yes, with a TCP monitor. HTTP monitors need a hostname because Cloudflare won't make HTTP requests to bare IPs. Or add an A record and monitor the hostname over HTTPS.
Is there ICMP ping?
No. Monitor a port the host serves instead; it proves strictly more. UDP services aren't covered either; use a heartbeat or health endpoint.
Which plan?
Pro and above. Multi-region port checks on Team and above.
Can I check port 25?
No, Cloudflare blocks outbound 25. Use 465 or 587, or an HTTP endpoint on the host that tests the local SMTP handshake.
Monitor the port, then monitor the service
A port check is the right tool for a specific job: knowing that a non-HTTP service, or a server without a domain, is reachable from the internet. It's fast, it's universal, and it doesn't need anything installed. It isn't a health check, so put one next to it where you can. Create an account, upgrade to Pro, and add a TCP monitor for the SSH port on the server you'd least like to discover is down from a customer. Related reading: monitoring a VPS end to end, game server monitoring, mail server monitoring, and uptime monitoring vs APM vs logging for where port checks sit in the stack.