The forms of this message
kex_exchange_identification: read: Connection reset by peer
Connection reset by 203.0.113.10 port 22
kex_exchange_identification: Connection closed by remote host
Connection closed by 203.0.113.10 port 22
Recent clients may print only the last line, Connection closed by 203.0.113.10 port 22, unless you add -v. Reset and closed differ only in how the connection ended. Treat them as the same problem, because the same cause can show either one depending on what sits in between.
Start here
ssh -v user@server true
This line in the output is a very useful clue:
debug1: kex_exchange_identification: banner line 0: Not allowed at this time
That text comes from sshd itself. It means the server is up and is dropping you on purpose, so go to cause 1 and cause 2. To read the reason, use a console or another connection on the server:
sudo journalctl -u ssh -u sshd --since "1 hour ago" | grep -iE 'maxstartups|penalty'
If you are locked out, also try from another network, such as a phone hotspot. If that works, the block is tied to your address.
Cause 1: sshd penalties
Since OpenSSH 9.8, sshd penalizes addresses that fail to log in, and this is on by default. While the penalty lasts, new connections from that address are dropped. The server log shows:
drop connection #0 from [203.0.113.10]:41056 on [203.0.113.20]:22 penalty: failed authentication
Waiting is enough: the default maximum is 10 minutes. It is more likely to hit you behind a shared office address, or with a script or monitoring job that keeps failing, so fix what is failing first. For a trusted address, add this to sshd_config:
PerSourcePenaltyExemptList 203.0.113.10
A client that offers many keys can fail several times in one login. See Too many authentication failures to limit it to the right key.
Cause 2: MaxStartups
MaxStartups limits connections that have not logged in yet. The default is 10:30:100: from 10 such connections on, sshd drops new ones at random with a 30% chance, rising until all are dropped at 100. Bots scanning a public server can fill this. The log shows:
drop connection #1 from [203.0.113.10]:37954 on [203.0.113.20]:22 Maxstartups
Older versions log exceeded MaxStartups. Check the setting and the current connections:
sudo sshd -T | grep -iE '^(maxstartups|persourcemaxstartups|logingracetime)'
ss -tn state established '( sport = :22 )'
The failure is often intermittent: a retry works. To make room, raise the limit, and shrink the bot traffic with a firewall allow list or a shorter LoginGraceTime:
MaxStartups 30:30:100
Validate and apply it. The service is ssh on Debian and Ubuntu and sshd on Red Hat style systems:
sudo sshd -t && sudo systemctl reload ssh
Cause 3: TCP wrappers
OpenSSH upstream dropped this feature long ago, but some distributions still build it in. Debian 13 does, and a sshd: ALL line in /etc/hosts.deny produced the reset message in our test. Since OpenSSH 9.8 the library is linked to sshd-session, not to sshd, so check both:
ldd /usr/sbin/sshd /usr/lib/openssh/sshd-session 2>/dev/null | grep libwrap
grep -v '^#' /etc/hosts.deny /etc/hosts.allow
Remove the rule, or allow your address in /etc/hosts.allow:
sshd: 203.0.113.10
Cause 4: a firewall or fail2ban
A firewall, a load balancer or an intrusion prevention device that sends a TCP reset after the connection opens gives this family of messages. We reproduced it with this rule, where SSH printed Connection reset by peer:
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate ESTABLISHED -j REJECT --reject-with tcp-reset
fail2ban is a less likely cause of this message than people assume. Its default actions reject new connections, which the client reports as Connection refused, not as a reset. It is still worth a look if you failed several logins:
sudo fail2ban-client status sshd
sudo fail2ban-client set sshd unbanip 203.0.113.10
See fail2ban for SSH for more.
Cause 5: a tunnel with a dead end
If you connect through a tunnel or port forward, the first hop accepts your connection and then fails to reach the real target. The client sees the reset. With ssh -L, the terminal running the tunnel shows the true reason:
channel 3: open failed: connect failed: Connection refused
Look there, or at your proxy's log. Check that the target address and port are right, and that sshd is running on the target.
Cause 6: sshd itself has a problem
When the cases above do not fit, test the configuration and run a debug copy on a spare port. Your normal service keeps running:
sudo sshd -t
sudo /usr/sbin/sshd -d -p 2200
Then connect with ssh -p 2200 user@server and read what the debug copy prints. Allow that port in your firewall first, and close it afterwards.
Questions
What does kex_exchange_identification: Connection reset by peer mean?
The TCP connection opened but was cut before the SSH greeting. The server, or something in between, turned you away before any key or password was checked.
How long does an sshd penalty last?
Since OpenSSH 9.8 penalties grow with repeated failures, up to 10 minutes by default. Waiting clears it, and PerSourcePenaltyExemptList exempts trusted addresses.
Why does the reset happen only sometimes?
An intermittent reset often points to MaxStartups: when too many connections are still logging in, sshd drops new ones at random.
Sources
- sshd_config(5), MaxStartups and PerSourcePenalties
- OpenSSH 9.8 release notes
- sshd(8), OpenBSD manual
- hosts_access(5), Debian manual