Diagnosing "Connection refused"

Diagnosing "Connection refused"

What Does "Connection refused" Mean?

Conclusion: The remote host is reachable, but it actively rejected the connection on that port. It is not "unreachable" — it arrived and was turned away. The usual cause is that no service is listening there.

Connection refused appears when a TCP connection request (SYN) is answered with a RST (reset) from the other end. In other words, the packet did reach the host. The host itself — or a device in front of it — explicitly replied "this port will not accept connections".

$ curl http://192.0.2.10:8080
curl: (7) Failed to connect to 192.0.2.10 port 8080 after 3 ms: Connection refused
$ ssh user@192.0.2.10
ssh: connect to host 192.0.2.10 port 22: Connection refused

At the system-call level this is the error ECONNREFUSED, which apps surface as "Connection refused". The reply comes back immediately (a few milliseconds), and that speed is the key difference from a timeout, discussed next.

Prerequisites

  • OS: Ubuntu / a typical Linux
  • Target: a service listening over TCP (SSH / web / DB, etc.)
  • We assume you can get a shell on both client and server

How Is "Connection refused" Different from a Timeout?

Conclusion: Refused returns a RST instantly (the host is alive); a timeout returns nothing (packets are being dropped). That single difference splits the cause into "service side" versus "network path".

The first thing to settle is whether you were rejected or met with silence. Tell them apart by the message and the time to respond.

Symptom What comes back Main causes
Connection refused TCP RST (instant) Service down / wrong port / REJECT rule
Connection timed out No response (long wait) DROP firewall / broken path / host down
$ time nc -zv 192.0.2.10 22
nc: connect to 192.0.2.10 port 22 (tcp) failed: Connection refused

real    0m0.004s

If the failure returns in 0.0xx seconds, it is refused (the host is responding). If it hangs for ten-plus seconds before failing, it is a timeout — suspect a DROP firewall or a path problem. This article covers the refused side; for timeout-leaning triage see Port Connectivity.

Refused means "the host is alive but the port turned me away". Whether ping works is not a reliable signal (ICMP and TCP are separate; SSH can succeed even when ping fails).

Why Does "Connection refused" Happen?

Conclusion: Nine times out of ten, nothing is listening on the target port. The cause reduces to four things — service down, wrong port, wrong listen address, or a REJECT rule — and working top to bottom is fastest.

The paths that produce refused, organized by cause:

Cause When it happens Where to start
Service is stopped Crashed / failed to start / not up after a reboot systemctl status
Wrong port number Config change, listening on a non-default port ss -tlnp
Wrong listen address Bound to 127.0.0.1, unreachable from outside ss -tlnp
Firewall REJECT nftables/iptables actively rejects with RST/ICMP ss + rule check

The reliable starting point is checking on the server whether anything is actually listening. The client-side error alone cannot tell a stopped service from a wrong port.

How Do You Check Whether the Service Is Up?

Conclusion: On the server, run ss -tlnp and confirm the target port is in LISTEN. If it is missing, the service is down or the port is wrong. Confirm the state and reason with systemctl status and journalctl.

Log into the server and list the listening ports.

$ sudo ss -tlnp
State    Recv-Q   Send-Q   Local Address:Port   Peer Address:Port   Process
LISTEN   0        128            0.0.0.0:22          0.0.0.0:*       users:(("sshd",pid=812,fd=3))
LISTEN   0        511          127.0.0.1:8080        0.0.0.0:*       users:(("node",pid=1043,fd=18))

The ss options are -t (TCP), -l (LISTEN only), -n (numeric, no name resolution), -p (show the process, needs root). If the target port is not in this list, nothing is listening there, and the client sees an immediate refused.

When the port is absent, check the corresponding service.

$ systemctl status nginx
× nginx.service - A high performance web server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled)
     Active: failed (Result: exit-code) since ...

Active: failed or inactive (dead) means it is stopped. Try to start it, and if it fails, follow the reason in the logs.

$ sudo systemctl start nginx
$ journalctl -u nginx -n 30 --no-pager

If refused persists while the service is clearly up, it is almost always a mismatched listen address or port. Move to the next section.

The Listen-Address Trap (the 127.0.0.1 Problem)

Conclusion: A service bound to 127.0.0.1:port is reachable from the same server but always refused from outside. Always check whether the Local Address column in ss -tlnp is 127.0.0.1 or 0.0.0.0 (or ::).

This is the classic "the service is running but I can't reach it from outside" case. Look at the earlier output again.

LISTEN   0   128       0.0.0.0:22       ...   sshd
LISTEN   0   511     127.0.0.1:8080     ...   node
  • 0.0.0.0:22 → listening on all interfaces. Reachable from outside.
  • 127.0.0.1:8080loopback only. curl localhost:8080 on the server works, but anything external or from another host is refused.

If it succeeds on the server and is refused from outside, this is almost certainly the cause. Change the app's bind address (often a bind / listen / host setting) to 0.0.0.0 or the target IP, then restart.

# Succeeds on the server (loopback reaches it)
$ curl -sS http://127.0.0.1:8080/ >/dev/null && echo OK

# Refused from outside / another host
$ curl http://192.0.2.10:8080/
curl: (7) Failed to connect ... Connection refused

Databases (PostgreSQL / MySQL, etc.) often bind to 127.0.0.1 by default. Opening them to external connections must be done together with a review of authentication and firewalling — do not just open them to 0.0.0.0.

When a Firewall Is the Cause (REJECT vs DROP)

Conclusion: A firewall set to REJECT returns RST/ICMP, producing Connection refused; DROP discards the packet, producing a timeout. If you see refused, suspect an explicit REJECT rule.

A firewall can deny traffic two ways, and the symptom on the client differs.

  • REJECT: explicitly signals the denial (TCP RST or ICMP port-unreachable) → the client gets an immediate Connection refused
  • DROP: silently discards the packet → the client waits and gets a timeout

So when you see refused, a DROP-style firewall (ufw's default deny is DROP) is usually not the culprit. Still, check for an explicit REJECT rule.

# Inspect nftables rules
$ sudo nft list ruleset | grep -i -E 'reject|dport'

# On systems using iptables
$ sudo iptables -L -n -v --line-numbers
Chain INPUT (policy ACCEPT)
num  target  prot  ...  destination
1    REJECT  tcp   ...  tcp dpt:8080 reject-with tcp-reset

If a REJECT ... reject-with tcp-reset rule matches that port, it is the source of the refused. Decide whether the rule is needed, and remove it if not. If you use ufw, see ufw SSH Troubleshooting for confirming and restoring allow rules.

Shortcut: if a loopback connection on the server (curl 127.0.0.1:port) works and only outside access is refused, the cause is the listen address or the firewall. If it is refused even on the server, narrow it to a stopped service or wrong port.

How to Triage from the Client Side

Conclusion: When you cannot get onto the server, use nc -zv to test reachability only and tell refused from timeout. ss -tn also shows how far the handshake got.

When you cannot immediately reach the server, do a minimal check from the client.

# Test only whether the port connects (-z: no data sent, -v: verbose)
$ nc -zv 192.0.2.10 22

If Connection refused returns instantly, the host is responding but the port is closed. If it timed out, suspect the path or a DROP firewall, and go to Port Connectivity rather than this article.

Looking at the TCP state after a connection attempt shows how far the handshake got.

$ ss -tn dst 192.0.2.10

If it stays in SYN-SENT, no response came back (timeout family). With refused the connection never establishes and fails immediately.

Run port scans and reachability checks only against hosts you own or are authorized to test. Avoid scanning third-party hosts without permission.

Checklist When It Still Fails

Conclusion: Refused confirms "host alive, port rejecting". Work through service → port → listen address → REJECT rule, and the cause collapses to one of those four.

  • [ ] Did you tell refused (instant) from timed out (long wait)?
  • [ ] On the server, did ss -tlnp show the target port in LISTEN?
  • [ ] If no LISTEN, did you check the stop reason with systemctl status / journalctl -u?
  • [ ] Is the Local Address 127.0.0.1 (external access needs 0.0.0.0 etc.)?
  • [ ] Does the port number match on the client and the listener?
  • [ ] Is there a REJECT rule for that port in nft list ruleset / iptables -L?
  • [ ] Does a server-side loopback (curl 127.0.0.1:port) work (if so, the cause is outside)?

Next Reading