SSH Connection Refused: How to Diagnose and Fix Every Variant
Fix SSH connection refused, permission denied, segfault, and slow connections with step-by-step commands. Covers sshd, firewall, keys, and PAM issues.
- 'Connection refused' means the TCP handshake never completed — sshd is not listening, a firewall is dropping packets, or you are hitting the wrong host/port
- 'Permission denied (publickey)' means sshd is reachable but authentication failed — wrong key, wrong user, or bad file permissions on ~/.ssh/authorized_keys
- 'Connection timed out' usually points to a firewall silently dropping packets rather than an RST, use nc or nmap to confirm the port state
- SSH slowness on connect is almost always DNS reverse-lookup (UseDNS yes) or GSSAPI negotiation — both are one-line sshd_config fixes
- A segfault in ssh or sshd is nearly always a version/algorithm mismatch or a corrupted host key — check OpenSSH version and regenerate keys if needed
| Symptom | Root Cause | Fix Command | Time | Risk |
|---|---|---|---|---|
| Connection refused | sshd not running | systemctl start sshd | <1 min | Low |
| Connection refused | Wrong port | ssh -p 2222 user@host | <1 min | None |
| Connection refused | Firewall blocking 22 | ufw allow 22/tcp or iptables -I INPUT -p tcp --dport 22 -j ACCEPT | 2 min | Medium |
| Permission denied (publickey) | authorized_keys permissions | chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys | <1 min | Low |
| Permission denied (publickey) | Wrong key loaded | ssh -i ~/.ssh/id_ed25519 user@host | <1 min | None |
| Permission denied (publickey) | SELinux/AppArmor label | restorecon -Rv ~/.ssh | 2 min | Low |
| Slow SSH login | UseDNS reverse lookup | Set UseDNS no in sshd_config | 1 min | Low |
| Slow SSH login | GSSAPI negotiation | ssh -o GSSAPIAuthentication=no | <1 min | None |
| SSH segfault | Algorithm mismatch/old key | Remove stale host key, reconnect | 2 min | Low |
| Host key changed warning | MITM or rebuilt host | ssh-keygen -R hostname | <1 min | Low |
Understanding SSH Connection Errors
SSH errors fall into four distinct failure layers: network/transport (TCP never connects), authentication (sshd rejects your credentials), session (shell or subsystem fails post-auth), and performance (connects but slowly). Misdiagnosing the layer wastes hours. This guide works top-down.
Layer 1 — Transport: "Connection refused" and "Connection timed out"
Step 1: Confirm the exact error message
Run ssh with maximum verbosity to see exactly where the handshake fails:
ssh -vvv user@192.0.2.10
Key lines to look for:
connect to host 192.0.2.10 port 22: Connection refused— TCP RST received; sshd is not listening on this address/port.connect to host 192.0.2.10 port 22: Connection timed out— Firewall is silently dropping packets; no RST arrived.ssh: connect to host 192.0.2.10 port 22: No route to host— Routing or network-layer problem.
Step 2: Verify sshd is running
# On the remote host:
systemctl status sshd # systemd systems (RHEL, Ubuntu 16+, Debian 8+)
service ssh status # SysV / older Ubuntu naming
ps aux | grep sshd
If sshd is stopped, start it and check for config errors:
sshd -t # Test config without starting
systemctl start sshd
journalctl -u sshd -n 50 # Recent sshd log entries
Common startup failures:
- Port already in use:
sshd: Address already in use— Another process holds port 22. Find it:ss -tlnp | grep :22 - Bad config syntax:
sshd -twill report the offending line. - Missing host key:
sshd: no hostkeys available— regenerate:ssh-keygen -A
Step 3: Check which address and port sshd is bound to
ss -tlnp | grep sshd
# Expected: LISTEN 0 128 0.0.0.0:22 ...
If sshd listens on a non-standard port (e.g., 2222), connect with ssh -p 2222. The port is set in /etc/ssh/sshd_config via the Port directive.
Step 4: Rule out firewall
# Test reachability without ssh from a third host or using nc:
nc -zv 192.0.2.10 22
# Open: succeeded! -> sshd is up, problem is elsewhere
# refused: -> sshd not listening
# timeout: -> firewall dropping
# On the remote host — inspect active firewall rules:
iptables -L INPUT -n -v | grep 22
nft list ruleset | grep 22
ufw status verbose
firewall-cmd --list-all # firewalld (RHEL/CentOS 7+)
To open port 22 immediately:
# UFW:
ufw allow ssh
# iptables (does not persist across reboots without iptables-persistent):
iptables -I INPUT -p tcp --dport 22 -j ACCEPT
# firewalld:
firewall-cmd --permanent --add-service=ssh
firewall-cmd --reload
Layer 2 — Authentication: "Permission denied (publickey)"
Once the TCP connection is established, sshd authenticates you. The most common error is:
debug1: Offering public key: /home/user/.ssh/id_ed25519
debug1: Authentications that can continue: publickey
permission denied (publickey).
Step 1: Check the correct key is being offered
ssh -vvv user@host 2>&1 | grep 'Offering\|Trying\|identity'
If your key is not listed, the ssh-agent may not have it loaded:
ssh-add -l # List loaded keys
ssh-add ~/.ssh/id_ed25519 # Add your key
Force a specific key:
ssh -i ~/.ssh/id_ed25519 user@host
Step 2: Verify authorized_keys on the server
cat ~/.ssh/authorized_keys # Should contain your public key
Permission requirements — any deviation causes silent rejection:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
ls -la ~/.ssh/ # owner must be the login user, not root
On SELinux-enforcing systems (RHEL, CentOS, Fedora), wrong file context causes the same symptom even with correct permissions:
ls -Z ~/.ssh/authorized_keys # Check context
restorecon -Rv ~/.ssh # Restore correct context
Step 3: Check sshd_config on the server
grep -E 'PubkeyAuthentication|AuthorizedKeysFile|PasswordAuthentication|PermitRootLogin|AllowUsers|DenyUsers' /etc/ssh/sshd_config
Key settings that block login:
PubkeyAuthentication no— must beyesAuthorizedKeysFilepointing to a non-default pathAllowUsersorAllowGroupsthat excludes your userPermitRootLogin nowhen logging in as root
After editing sshd_config, always reload safely (do not restart with an active session):
sshd -t && systemctl reload sshd
Step 4: Inspect /var/log/auth.log or journald
tail -f /var/log/auth.log # Debian/Ubuntu
journalctl -fu sshd # systemd
grep sshd /var/log/secure | tail -30 # RHEL/CentOS
Telltale log messages:
Authentication refused: bad ownership or modes for directory /home/user/.ssh— fix withchmod 700 ~/.sshUser user from 1.2.3.4 not allowed because not listed in AllowUsers— update AllowUsersfatal: Access denied for user root by PAM— PAM policy blocking login (check/etc/pam.d/sshd)
Layer 3 — Performance: Slow SSH Login
A 5–30 second pause before the password/key prompt is almost always one of two things:
DNS reverse lookup: sshd tries to resolve the client IP. Fix in /etc/ssh/sshd_config:
UseDNS no
GSSAPI negotiation: Kerberos probes time out on non-domain machines. Fix client-side (no server change needed):
ssh -o GSSAPIAuthentication=no user@host
# Make permanent in ~/.ssh/config:
Host *
GSSAPIAuthentication no
Layer 4 — Crashes: "ssh segfault" / Process Aborted
A segfault in the ssh client or sshd daemon is uncommon but happens in three scenarios:
- Algorithm incompatibility between old server and new client. OpenSSH 8.8+ disabled RSA/SHA-1. If the server only supports
ssh-rsa, the handshake aborts:
# Temporary workaround (client side):
ssh -o PubkeyAcceptedAlgorithms=+ssh-rsa -o HostKeyAlgorithms=+ssh-rsa user@host
# Permanent fix: update openssh-server on the remote host to 8.x+
- Corrupted server host key. Regenerate all host keys:
rm /etc/ssh/ssh_host_*
ssh-keygen -A
systemctl restart sshd
- Stale known_hosts entry after server rebuild:
# Client side:
ssh-keygen -R hostname
ssh-keygen -R 192.0.2.10
Host Key Changed Warning
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Do not blindly remove the key if you are on a production host — this warning can indicate a man-in-the-middle attack. Verify the new fingerprint out-of-band (console, cloud dashboard, vendor key fingerprint API) before removing:
# Only after verifying legitimacy:
ssh-keygen -R hostname
Quick Reference Checklist
ssh -vvv— identify failure layersystemctl status sshd— is sshd running?ss -tlnp | grep sshd— what address/port?nc -zv host 22— firewall or sshd?chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keysgrep -E 'AllowUsers|PubkeyAuth' /etc/ssh/sshd_configjournalctl -u sshd -n 50— auth log for server-side denialUseDNS noin sshd_config — fix slow loginssh-keygen -A— regenerate missing/corrupt host keyssshd -t && systemctl reload sshd— safe config reload
Frequently Asked Questions
#!/usr/bin/env bash
# ssh-diagnose.sh — Run on the CLIENT to diagnose SSH connection issues
# Usage: bash ssh-diagnose.sh user@host [port]
TARGET="${1:-}"
PORT="${2:-22}"
if [[ -z "$TARGET" ]]; then
echo "Usage: $0 user@host [port]"
exit 1
fi
HOST="${TARGET##*@}"
echo "=== 1. TCP reachability (nc) ==="
nc -zv -w 5 "$HOST" "$PORT" 2>&1 && echo "[OK] Port $PORT open" || echo "[FAIL] Port $PORT closed or filtered"
echo ""
echo "=== 2. SSH verbose connect (first 40 lines) ==="
ssh -vvv -p "$PORT" -o ConnectTimeout=10 -o BatchMode=yes "$TARGET" exit 2>&1 | head -40
echo ""
echo "=== 3. Loaded SSH keys ==="
ssh-add -l 2>/dev/null || echo "[WARN] ssh-agent not running or no keys loaded"
echo ""
echo "=== 4. Known hosts entry for $HOST ==="
ssh-keygen -F "$HOST" 2>/dev/null || echo "[INFO] No known_hosts entry for $HOST"
echo ""
echo "=== 5. Client SSH config for $HOST ==="
ssh -G -p "$PORT" "$TARGET" 2>/dev/null | grep -E 'hostname|port|user|identityfile|gssapi|pubkeyauthentication'
echo ""
echo "=== REMOTE: Run these on the server ==="
cat <<'REMOTE'
# Check sshd status:
systemctl status sshd
# Check listening address and port:
ss -tlnp | grep sshd
# Check firewall:
ufw status verbose || firewall-cmd --list-all || iptables -L INPUT -n -v | grep 22
# Check authorized_keys permissions:
ls -la ~/.ssh/authorized_keys
# Check sshd_config for auth restrictions:
grep -E 'PubkeyAuth|PasswordAuth|AllowUsers|DenyUsers|PermitRoot|AuthorizedKeysFile' /etc/ssh/sshd_config
# Tail the auth log:
journalctl -u sshd -n 30 --no-pager
# or: tail -30 /var/log/auth.log
# Test sshd config syntax:
sshd -t && echo "Config OK"
REMOTEError Medic Editorial
Error Medic Editorial is a team of senior DevOps and SRE engineers with combined experience across Linux, cloud infrastructure, and distributed systems. Articles are peer-reviewed against current documentation and tested on live systems before publication.
Sources
- https://man.openbsd.org/sshd_config
- https://www.openssh.com/txt/release-8.8
- https://wiki.archlinux.org/title/OpenSSH#Troubleshooting
- https://serverfault.com/questions/130482/how-to-check-which-port-ssh-is-running-on
- https://stackoverflow.com/questions/2419566/what-does-permission-denied-publickey-mean-and-how-do-i-fix-it
- https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/securing_networks/configuring-openssh_securing-networks