The fast check is nc -zv host port: "succeeded" means the port is open, "refused" means the machine answered but nobody's listening, and dead silence almost always means a firewall is dropping the packet. Those are three different failures with three different fixes. This is the decision tree for telling them apart, not another list of five tools that all do the same handshake.
1. Is the port actually open?
$ clai check if port 8099 is open on 127.0.0.1→ nc -zv 127.0.0.1 8099Connection to 127.0.0.1 8099 port [tcp/*] succeeded!
-z only probes the connection without sending data, -v prints the result. Shortest possible answer to the question.
2. Port closed — and that's a specific answer
$ clai check port 8098→ nc -zv 127.0.0.1 8098nc: connect to 127.0.0.1 port 8098 (tcp) failed: Connection refused
"Connection refused" means the machine is reachable and actively rejected you — nobody is listening on that port. That's good news: the network is fine, the problem is the service.
3. Telling refused apart from silence
$ clai check ports 8098 and 8099 and show the reason→ nmap --reason -p 8099,8098 127.0.0.1PORT STATE SERVICE REASON8098/tcp closed unknown conn-refused8099/tcp open unknown syn-ack
The REASON column is the actual diagnosis. syn-ack is open. conn-refused is nobody listening. no-response means the packet vanished — almost always a firewall. This is the only place where the three causes show up separately.
4. Check from the other side: who's listening
$ clai show which process is listening on port 8099→ ss -tlnp | grep 8099LISTEN 0 5 0.0.0.0:8099 0.0.0.0:* users:(("python3",pid=2411411,fd=3))
0.0.0.0:8099 means all interfaces. If it said 127.0.0.1:8099 instead, the service would answer only locally, and a remote check would fail even though the service is perfectly healthy. That's the third cause, and it only shows up from here.
5. No nc, no nmap installed
$ clai check the port without installing any extra tools→ timeout 2 bash -c 'echo > /dev/tcp/127.0.0.1/8099' && echo "open" || echo "closed"open
bash can open a raw TCP connection through the /dev/tcp pseudo-device. Works on a bare container with neither nc nor nmap. timeout is mandatory — without it, a filtered port leaves you waiting out the kernel's own timeout.
6. Checking several ports on a remote host at once
$ clai check ports 22, 80, and 443 on example.com→ nmap -Pn -p 22,80,443 example.com
-Pn skips the ping check. Many hosts don't answer ICMP, and without this flag nmap decides the host is down and never scans the ports at all.
Gotchas
- "Connection refused" and "timeout" are different diagnoses. Refused means the packet reached the machine. Silence means a firewall or packet loss. Don't treat one like the other.
- Scanning hosts you don't own can be illegal. Point nmap at your own infrastructure, not someone else's.
- UDP can't be checked this way. There's no handshake for UDP, so
nc -zureports "succeeded" almost every time. You neednmap -sUand patience.
Related questions
The port is open but the app doesn't respond. The wrong process is listening, or it's too slow to answer. Check the ss -tlnp output and the service logs.
How do I check a port from Windows? Test-NetConnection -Port 443 example.com in PowerShell.
Why isn't telnet recommended anymore? It does the same thing, but it's rarely installed, while nc and /dev/tcp are available almost everywhere.
See also
- Kill the process on port 8080
- Network triage in plain English: connections, ports, DNS
- The process that won't die
Stop memorizing nc and nmap flags — describe the problem and CliAI writes the command. Install it in one line.