Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Linux Admins By James Joyner IV · · 9 min read Last reviewed Jul 2026

SSH 'Connection refused' on port 22: How to Fix It on Linux

Quick answer

Fix 'Connection refused' on Linux for SSH and curl: diagnose a stopped service, wrong port, firewall drop, or bind address so the server actually accepts connections on the port.

  • #linux
  • #troubleshooting
  • #errors
  • #networking
Free toolkit

Stuck on this Linux Admins error? Get the free incident triage checklist

A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.

Overview

Connection refused means your packet reached the target host, but nothing there accepted it on that port. The kernel on the far side answered with a TCP RST because no process is listening. It shows up from SSH and curl like this:

$ ssh deploy@10.0.3.14
ssh: connect to host 10.0.3.14 port 22: Connection refused

$ curl http://10.0.3.14:8080/health
curl: (7) Failed to connect to 10.0.3.14 port 8080 after 3 ms: Connection refused

This is fundamentally different from a timeout. Refused means the host is reachable and actively rejected you (no listener, or a firewall issuing a reject). Timed out means packets were silently dropped or the host never answered. Refused is usually the easier of the two to fix because it proves the network path works.

Symptoms

  • ssh fails instantly (milliseconds) with port 22: Connection refused.
  • curl returns (7) Failed to connect ... Connection refused.
  • telnet host port or nc -vz host port reports the connection was refused immediately.
  • The same command succeeds from the server itself (localhost) but fails from remote.
  • Ping to the host works, proving the host is up, while the specific port is refused.

Common Root Causes

  • Service not runningsshd, the web app, or the database isn’t started, so nothing listens on the port.
  • Wrong port — the service listens on a non-default port (e.g. SSH on 2222) while you connect to the default.
  • Bound to localhost only — the service listens on 127.0.0.1 instead of 0.0.0.0, so remote clients are refused.
  • Firewall REJECTufw, firewalld, nftables, or a cloud security group actively rejects (not drops) the port.
  • Service crashed / failed to start — the unit exited, freeing the port so the kernel sends RST.
  • Wrong host or NAT/port-forward gap — connecting to the right IP but no forwarding rule maps the port to the backend.

Diagnostic Workflow

Confirm the host is reachable and separate “refused” from “timed out”:

ping -c3 10.0.3.14                 # host up?
nc -vz 10.0.3.14 22               # refused = fast RST; timeout = firewall drop

On the server, verify something is actually listening on the port and on which address:

sudo ss -ltnp | grep -E ':22|:8080'   # LISTEN sockets, address, and owning process

If it listens only on 127.0.0.1, remote clients will be refused. Check the service status and logs:

sudo systemctl status sshd
sudo journalctl -u sshd --since '15 min ago' | tail

Check firewall rules for a reject/deny on the port:

sudo ufw status verbose               # Debian/Ubuntu
sudo firewall-cmd --list-all          # RHEL/Fedora
sudo nft list ruleset | grep -iE 'reject|22|8080'

From the client, add verbose SSH output to see exactly where it fails:

ssh -v deploy@10.0.3.14 2>&1 | grep -i 'connect\|refused\|port'

Example Root Cause Analysis

A web service that worked locally refused all remote connections:

$ curl http://10.0.3.14:8080/    # from another host
curl: (7) Failed to connect to 10.0.3.14 port 8080: Connection refused
$ curl http://127.0.0.1:8080/    # on the server itself
{"status":"ok"}

Local worked, remote refused — a classic bind-address problem. ss confirmed it:

$ sudo ss -ltnp | grep 8080
LISTEN 0 128 127.0.0.1:8080 0.0.0.0:* users:(("app",pid=812,fd=7))

The app was bound to 127.0.0.1:8080, so the kernel refused every non-local client. Changing the app’s listen address from 127.0.0.1 to 0.0.0.0 (all interfaces) and restarting it fixed remote access:

# app config: bind_address changed 127.0.0.1 -> 0.0.0.0
sudo systemctl restart app
sudo ss -ltnp | grep 8080     # now shows 0.0.0.0:8080

Had ss shown no listener at all, the fix would have been starting the service; had a firewall shown a reject rule, opening the port would have been the fix.

Prevention Best Practices

  • Bind services to the intended interface deliberately — 0.0.0.0 for public/internal access, 127.0.0.1 only for local-only.
  • Add a health check and monitoring that connects to the actual port from a remote host, not just localhost.
  • Keep firewall rules in version-controlled config so port openings are reviewed and reproducible.
  • Document non-default ports (e.g. SSH on 2222) and set them in ~/.ssh/config so clients use the right one automatically.
  • Ensure critical services are enabled in systemd so they restart on boot and after crashes.
  • Alert on sshd/app unit failures so a crashed listener is noticed before users hit Connection refused.

Quick Command Reference

nc -vz <host> <port>                     # refused vs timeout, fast
sudo ss -ltnp | grep :<port>             # is anything listening, and on which IP?
sudo systemctl status <service>          # is the service running?
sudo journalctl -u <service> --since '15 min ago'
sudo ufw status verbose                  # firewall (Debian/Ubuntu)
sudo firewall-cmd --list-all             # firewall (RHEL/Fedora)
ssh -v user@host                         # verbose SSH connection trace

Conclusion

Connection refused is the network path working and the destination actively saying “nothing here” on that port — a fast RST, not a silent drop. Rule it in with nc -vz, then check three things on the server: is the service running, is it listening on a reachable address (not just 127.0.0.1), and does a firewall reject the port. Because refused proves reachability, it is usually a quick fix once ss and systemctl status tell you which of those three is at fault.

Free download · 368-page PDF

Fixed it? Get 500 Linux Admins & DevOps AI prompts — free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.

Did this fix your issue?

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.