Why ss Might Show No Open TCP Ports Even When Services Are Running
I’ve seen this happen on fresh installs, on servers that have just rebooted, and even on production machines that have been up for months. The symptom is simple: ss -tuln prints nothing, but systemctl status nginx (or whatever daemon you’re checking) says active (running). The mismatch usually boils down to one of three things:
- The service is listening on a non‑TCP socket
- The service is bound to a Unix domain socket or a local‑only address
- The socket is managed by a socket‑activated unit that isn’t yet in use
Below is a quick checklist that will help you zero in on the culprit, plus a few hardening tips that come with each case.
1. Check the Listening Address
ss only shows IPv4/IPv6 sockets that are listening on a network interface unless you explicitly ask for it. If a service is bound to 127.0.0.1 or ::1, you’ll see it only if you add the -4 or -6 flags:
ss -tuln4
ss -tuln6
If the service is listening on a local‑only address, you’ll spot it here. PostgreSQL, for instance, often defaults to 127.0.0.1:5432. If you’re expecting a public port, double‑check the config file (postgresql.conf, nginx.conf, etc.) for the listen_addresses or server_names directives.
Hardening tip: If a service must stay host‑only, keep it bound to 127.0.0.1 or a Unix socket. It shrinks the attack surface.
2. Unix Domain Sockets
Many daemons expose a Unix domain socket instead of a TCP port. ss can list them with -x:
ss -x
For example, the systemd‑resolved DNS stub listens on /run/systemd/resolve/stub-resolv.conf. These sockets won’t show up in ss -tuln, but they’re real IPC endpoints.
If you’re troubleshooting a service that should expose a TCP port, verify that the configuration explicitly sets listen to a network address and not a socket path.
3. Socket‑Activated Units
Systemd can start a service on demand via a socket unit. In this model, the socket unit creates the listening socket, and the service is spawned when a connection arrives. Until a client connects, the service may appear running but no TCP port will be visible.
systemctl list-units --type=socket
Look for a unit like ssh.socket. If you see it, the port is already open; ss will show it. If the socket unit is missing, the service might be configured to start manually without a socket.
Hardening tip: Disable unnecessary socket units (systemctl disable --now <unit>.socket) to avoid accidental exposure. Use systemctl edit <unit>.socket to bind to a specific interface or port range.
4. SELinux or AppArmor Restrictions
On systems with SELinux or AppArmor enabled, a service might be prevented from binding to a port even though the config says it should. Check the audit log:
ausearch -m avc -ts recent | tail
If you see denied entries for socket_bind, the policy is blocking the bind. Temporarily set SELinux to permissive to confirm:
setenforce 0
If the port appears after this change, adjust the policy with semanage port -a -t http_port_t -p tcp 8080 (example for HTTP on 8080).
5. Firewall Rules Hiding the Port
A firewall can drop SYN packets, making the port appear closed to external scanners while the service is listening. Verify with:
iptables -L -n -v
nft list ruleset
If a rule drops traffic to the port, either adjust the rule or add a REJECT with a comment for clarity.
Quick Troubleshooting Flow
| Step | Command | What to Look For |
|---|---|---|
| 1 | ss -tuln | No output? |
| 2 | ss -tuln4 / ss -tuln6 | Local‑only binding? |
| 3 | ss -x | Unix socket? |
| 4 | systemctl list-units --type=socket | Socket activation? |
| 5 | ausearch -m avc | SELinux blocking? |
| 6 | iptables -L -n -v | Firewall hiding? |
Run through the table until you find the root cause. Once identified, apply the appropriate hardening measure.
Practical Example: A Misconfigured Nginx
Suppose systemctl status nginx shows active, but ss -tuln shows nothing. You discover:
ss -tuln4
# no output
ss -x | grep nginx
# /run/nginx/nginx.sock (Unix socket)
systemctl list-units --type=socket | grep nginx
# nginx.socket not listed
The config contains:
listen unix:/run/nginx/nginx.sock;
Change to:
listen 80;
Reload:
systemctl reload nginx
Now ss -tuln4 shows LISTEN 0 128 0.0.0.0:80.
Final Thoughts
When ss shows no open TCP ports, it’s almost always a configuration or policy nuance rather than a system failure. Use the checklist above to locate the hidden socket, and then tighten the configuration to expose only what’s necessary. This keeps the network surface minimal while maintaining operational visibility.
See also
- When nftables silently drops HTTP traffic: a step‑by‑step port‑filter investigation
- Why My Nginx Service Stops After a Kernel Upgrade: A Systemd Timer Debugging Session in a Home‑Lab Server
- Hardening Serverless Function IAM: A Step‑by‑Step Fix for Over‑Permissive Execution Roles
- The Cost of Over‑Broad Retention: How a 30‑Day Backup Policy Exposed 2 TB of PHI
- Turning a Splunk dashboard into a hands‑on threat‑hunting playbook: a step‑by‑step recipe for real‑world detection agility