Linux Error Guide: 'ssh_exchange_identification: Connection closed by remote host' — restore SSH access
Fix 'ssh_exchange_identification: Connection closed by remote host': diagnose sshd crashes, TCP wrappers, fail2ban bans, MaxStartups throttling, full disk, and firewall drops.
- #linux
- #troubleshooting
- #errors
- #ssh
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
You try to SSH into a server and the connection is accepted just long enough to be dropped before authentication even begins:
ssh_exchange_identification: Connection closed by remote host
A close cousin appears when the daemon actively refuses the banner exchange:
ssh_exchange_identification: read: Connection reset by peer
The key detail is where it fails: the TCP connection reached the server (so the port is open and the network path works), but sshd closed the socket during the protocol-banner exchange — before you were ever prompted for a password or key. This almost always points at something on the server side, not your client or credentials.
Symptoms
ssh user@hostreturns instantly withssh_exchange_identification: Connection closed by remote hostand no password prompt.- The same host was reachable earlier; other services on it may or may not respond.
ssh -vshows the connection established, then closed immediately afterConnecting to ... port 22.- Some source IPs work while others are refused (a hint toward per-IP banning or access control).
- On the server, new SSH sessions cannot be opened even from the console-adjacent network, but an already-open session still works.
Common Root Causes
- fail2ban or a similar tool banned your IP after repeated failed logins, adding a firewall drop/reject rule.
- TCP wrappers denial — an entry in
/etc/hosts.deny(or missing allow in/etc/hosts.allow) rejects the connection forsshd. sshdconnection throttling viaMaxStartups— under a flood of half-open connections the daemon randomly drops new ones.- The server disk is full, so
sshdcannot create session files, write logs, or fork a child, and it closes the connection. sshdfailed to start or crashed after a bad config edit, leaving the port handled by a lingering listener or nothing at all.- PAM or
/etc/nologinblocking sessions, or resource exhaustion (out of PIDs / file descriptors) preventing a child from spawning. - A firewall or intermediate device resetting the connection right after the handshake.
Diagnostic Workflow
Start from the client with verbose output to confirm the failure stage:
ssh -vvv user@server.example.com 2>&1 | head -n 30
If you still have any working session (or out-of-band/console access), check whether sshd is actually healthy:
sudo systemctl status sshd
sudo sshd -t # validate config syntax without restarting
sudo ss -tlnp | grep ':22' # confirm sshd is listening on 22
Read the server logs for the real reason the connection was dropped:
sudo journalctl -u sshd --since '15 min ago' --no-pager
sudo tail -n 50 /var/log/auth.log # Debian/Ubuntu
sudo tail -n 50 /var/log/secure # RHEL/Fedora
Rule out a full disk, which silently breaks sshd:
df -h /
df -i / # inode exhaustion looks the same
Check for a fail2ban ban and for TCP wrapper / firewall blocks against your source IP:
sudo fail2ban-client status sshd
sudo iptables -S | grep -i drop
sudo nft list ruleset | grep -A3 sshd # nftables hosts
grep -v '^#' /etc/hosts.deny /etc/hosts.allow 2>/dev/null
Confirm the daemon is not throttling under connection pressure:
grep -i maxstartups /etc/ssh/sshd_config
sudo ss -tan state syn-recv | wc -l # many half-open = flood/throttle
Example Root Cause Analysis
A monitoring host suddenly could no longer SSH into an app server, while an engineer’s existing tmux session on that server stayed alive. From the console the admin ran:
sudo journalctl -u sshd --since '30 min ago' | tail
sshd[41233]: error: maximum authentication attempts exceeded for invalid user admin
sshd[41233]: Disconnecting: Too many authentication failures
fail2ban.actions: NOTICE [sshd] Ban 203.0.113.44
203.0.113.44 was the monitoring host — its health check was hammering SSH with an outdated key, tripping fail2ban’s threshold and getting the whole IP banned. Confirming the ban:
sudo fail2ban-client status sshd
|- Total banned: 1
`- Banned IP list: 203.0.113.44
The fix was to unban the IP, correct the monitoring key, and add the host to the fail2ban ignore list:
sudo fail2ban-client set sshd unbanip 203.0.113.44
Then, in /etc/fail2ban/jail.local, ignoreip = 127.0.0.1/8 203.0.113.44 and sudo systemctl reload fail2ban. The root cause was not SSH at all — it was a legitimate client repeatedly failing auth and getting banned.
Prevention Best Practices
- Put trusted management and monitoring hosts in fail2ban’s
ignoreipso a bad key never locks out your own tooling. - Keep console or out-of-band access (cloud serial console, IPMI/BMC, KVM) working so a broken
sshdnever means a lost server. - Always run
sudo sshd -tbefore restarting after editingsshd_config, and reload rather than blind-restart. - Alert on root-filesystem and inode usage before 90% so a full disk never quietly kills new SSH sessions.
- Raise
MaxStartups(e.g.10:30:100) on busy bastions and rate-limit port 22 at the firewall instead of relying on the daemon alone. - Prefer key-based auth and
AllowUsers/Matchblocks over broad password access to reduce brute-force bans in the first place.
Quick Command Reference
# Client: see exactly where it fails
ssh -vvv user@host 2>&1 | head -n 30
# Server health (via console / existing session)
sudo systemctl status sshd
sudo sshd -t
sudo ss -tlnp | grep ':22'
# Logs
sudo journalctl -u sshd --since '15 min ago' --no-pager
sudo tail -n 50 /var/log/auth.log # or /var/log/secure
# Disk / inodes
df -h / && df -i /
# Bans and access control
sudo fail2ban-client status sshd
sudo fail2ban-client set sshd unbanip <IP>
grep -v '^#' /etc/hosts.deny /etc/hosts.allow
# Throttling
grep -i maxstartups /etc/ssh/sshd_config
Conclusion
ssh_exchange_identification: Connection closed by remote host is a server-side signal that the SSH daemon accepted your TCP connection and then dropped it before authentication. Because it fires before any credential exchange, it is rarely about your key or password — it is almost always fail2ban bans, TCP wrapper denials, MaxStartups throttling, a full disk, or a crashed sshd. Work from the client’s -vvv output to the server’s logs, confirm the daemon is listening and the disk has space, and check whether your own IP has been banned. Keeping out-of-band access and a fail2ban ignore list for trusted hosts turns this from a lockout emergency into a two-minute fix.
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?
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.