Ubuntu Port Connectivity: ss / lsof / nc / curl Troubleshooting

Port Connectivity Troubleshooting

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:

  1. DNS correct? (if needed)
  2. Can reach the port? nc -vz HOST PORT
  3. Server listening? ss -lntp | grep :PORT
  4. What process? lsof -i :PORT
  5. HTTP response? curl -I http(s)://HOST
  6. Service and logs systemctl status / journalctl

Table of Contents

  1. First, Clarify What's Not Connecting
  2. Client Side: Can You Reach It? (nc)
  3. Server Side: Is It Listening? (ss)
  4. Who's Holding the Port? (lsof)
  5. HTTP: Check Response (curl)
  6. DNS Troubleshooting
  7. Service Check (systemctl / journalctl)
  8. Error Messages Explained

1. First, Clarify What's Not Connecting

Establish these 3 things first:

  • HOST: Domain or IP
  • PORT: 22 / 80 / 443 / 3000 / 8080 / 3306 etc.
  • Protocol: SSH? HTTP? Database?

2. Client Side: Can You Reach It? (nc)

Key point: Test reachability from client BEFORE touching the server.

$ nc -vz example.com 22

Reading the results (crucial)

  • succeededNetwork path works (now check server/app)
  • timed outCan't reach (FW/SG/route/DNS/server down)
  • refusedReached but nothing listening (service stopped/wrong port/wrong bind)

3. Server Side: Is It Listening? (ss)

When nc refused, this tells you definitively.

$ sudo ss -lntp

Options:

  • -l: Listening sockets
  • -n: Numeric (no DNS lookup)
  • -t: TCP
  • -p: Show process (needs sudo)

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.

4. Who's Holding the Port? (lsof)

$ sudo lsof -i :80

If an unexpected process is holding the port, that's your conflict.

5. HTTP: Check Response (curl)

For web/API, "port open but 500 error" is common.

$ curl -I http://example.com
$ curl -I https://example.com

Status code meanings

  • 200: OK
  • 301/302: Redirect
  • 403: Forbidden (auth/WAF/IP restriction)
  • 404: Not found (routing/path)
  • 500: Internal error (check logs)
  • 502/503/504: Upstream/backend problem

6. DNS Troubleshooting

# Check what IP the domain resolves to
$ dig example.com +short

# Test with IP directly (bypasses DNS)
$ nc -vz 203.0.113.10 80

If IP works but domain doesn't → DNS or CDN/WAF issue.

7. Service Check (systemctl / journalctl)

When nc succeeded but app isn't responding:

$ sudo systemctl status nginx
$ sudo journalctl -u nginx -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

nc: connect ... timed out

Causes: ufw, cloud FW (SG), corporate FW, routing, server down

nc: connect ... refused

Causes: Nothing listening, wrong port, process down

curl: (7) Failed to connect

Causes: Can't reach (timeout) or nothing listening

curl: (60) SSL certificate problem

Causes: Certificate/hostname/intermediate cert issue

curl -I returns 502 Bad Gateway

Causes: nginx/apache → upstream (app) is dead or wrong port

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

Test Environment

Commands in this article were tested on Ubuntu 24.04 LTS / bash 5.2.

Next Reading