Ubuntu Port Connectivity - ss, lsof, nc, curl Troubleshooting Guide
What You'll Learn
- How to isolate "can't connect" issues to network / server / application layer
- Avoid common mistakes like "assuming it's ufw" or "only checking if service is running"
- Master the command set (ss / lsof / nc / curl) for quick diagnosis
Quick Summary
When "can't connect", check in this order:
- DNS correct? (if needed)
- Can reach the port?
nc -vz HOST PORT - Server listening?
ss -lntp | grep :PORT - What process?
lsof -i :PORT - HTTP response?
curl -I http(s)://HOST - Service and logs
systemctl status/journalctl
Prerequisites
- OS: Ubuntu (server side)
- Target: Server beginners
- Focus here is on "investigation and isolation" (fixes link to other articles)
1. First, Clarify What's Not Connecting
Conclusion: Establish HOST, PORT, and protocol first — without them, diagnosis cannot start.
Establish these 3 things first:
- HOST: Domain or IP
- PORT: 22 / 80 / 443 / 3000 / 8080 / 3306 etc.
- Protocol: SSH? HTTP? Database?
Examples:
- SSH not connecting ->
HOST=example.comPORT=22 (or 2222) - Web not loading ->
PORT=80/443 - API dead ->
PORT=3000/8080
If this is unclear, you'll be lost forever.
2. Client Side: Can You Reach It? (nc)
Conclusion: Run
nc -vz HOST PORTfirst — three outcomes each point to a different layer.
Key point: Test reachability from client BEFORE touching the server.
2-1. nc for Connectivity Check (TCP)
$ nc -vz example.com 22
2-2. Different Port (e.g., 2222)
$ nc -vz example.com 2222
2-3. Reading the Results (Crucial)
succeeded-> Network path works (now check server/app)timed out-> Can't reach (FW/SG/route/DNS/server down)refused-> Reached but nothing listening (service stopped/wrong port/wrong bind)
timed out can be ufw, but cloud FW (SG etc.) causes the same symptom. Fixing only ufw often doesn't help, so check server-side listening in the next step.
3. Server Side: Is It Listening? (ss)
Conclusion:
ss -lntp | grep :PORT— check whether the bind is127.0.0.1, not0.0.0.0.
When nc refused, this tells you definitively.
3-1. Show All Listening Sockets
$ sudo ss -lntp
-l: Listening sockets-n: Numeric (no DNS lookup)-t: TCP-p: Show process (needs sudo)
3-2. Filter Specific Port (e.g., 80)
$ sudo ss -lntp | grep ':80 '
3-3. Common Trap: 127.0.0.1 Only
If you see:
LISTEN 0 4096 127.0.0.1:3000 ...
This only accepts localhost connections. For external access, it needs to bind to 0.0.0.0:3000 (app config side).
4. Who's Holding the Port? (lsof)
Conclusion:
lsof -i :PORTfinds the owner — an unexpected process means a port conflict.
ss shows this too, but lsof output is more human-readable.
4-1. Show Process Holding Port 80
$ sudo lsof -i :80
4-2. Port 3000 Example
$ sudo lsof -i :3000
If an unexpected process is holding the port, another service is conflicting.
5. HTTP: Check Response (curl)
Conclusion: Use
curl -Ifor HTTP status — a reachable port can still return 500/502/503.
For web/API, "port open but 500 error" is common.
5-1. Headers Only
$ curl -I http://example.com $ curl -I https://example.com
5-2. Status Code Meanings
200: OK301/302: Redirect (check if intentional)403: Forbidden (WAF/Basic auth/IP restriction)404: Not found (routing/path)500: Internal error (check logs)502/503/504: Upstream/backend problem
6. DNS Troubleshooting (When HOST Is a Domain)
Conclusion: If IP works but domain fails, it is DNS, CDN, or certificate — check with
dig.
"Domain only doesn't connect" is often DNS, cert, or redirect issues.
6-1. Check What IP the Domain Resolves To
$ dig example.com +short
6-2. Test Directly with IP (Bypass DNS)
$ nc -vz 203.0.113.10 80 $ curl -I http://203.0.113.10
IP works but domain doesn't -> DNS (or CDN/WAF) is likely the issue
7. Service Check (systemctl / journalctl)
Conclusion: When
ncsucceeds but the app fails, check service logs withjournalctl.
When nc succeeded but app isn't responding or returning errors:
7-1. Service Status
$ sudo systemctl status nginx $ sudo systemctl status apache2 $ sudo systemctl status ssh
7-2. Logs (Fastest)
$ sudo journalctl -u nginx -n 200 $ sudo journalctl -u ssh -n 200
Don't stop at "it's running". Services can crash immediately after start or be in restart loops - check logs.
8. Error Messages Explained
Conclusion: Timed out means blocked route; refused means nothing listening; SSL means cert.
8-1. nc: connect ... timed out
Causes: ufw, cloud FW (SG), corporate FW, routing, server down
8-2. nc: connect ... refused
Causes: Nothing listening, wrong port, process down
8-3. curl: (7) Failed to connect
Causes: Can't reach (timeout) or nothing listening
8-4. curl: (60) SSL certificate problem
Causes: Certificate/hostname/intermediate cert issue
8-5. curl -I returns 502 Bad Gateway
Causes: nginx/apache -> upstream (app) is dead or wrong port
9. Things to Avoid
Conclusion: Never disable the firewall first — use
ncandssto isolate the problem.
Don't: Disable FW Immediately to Work Around It
ufw disable is a last resort. Use nc and ss to determine the problem layer first.
Don't: Assume Port 22 Without Checking
If it's actually listening on 2222, "allowed 22" wastes time.
Don't: Skip Checking Server-Side Listening (ss) and Blame the App
If nothing is listening, the problem is before the app. Follow the isolation order.
Copy-Paste Template
# Client side (reachability) nc -vz example.com PORT # Server side (listening check) sudo ss -lntp | grep ":PORT " # Server side (process holding port) sudo lsof -i :PORT # HTTP response check curl -I http://example.com curl -I https://example.com # Service/logs sudo systemctl status <service> sudo journalctl -u <service> -n 200
Summary
- Use
ncto first confirm reachable/not reachable - Use
ssto confirm listening/not listening - Use
curlto check if app is responding correctly - When stuck, return to the difference between
timed out / refused / succeeded