kex_exchange_identification: Connection closed by remote host — how to fix
kex_exchange_identification: Connection closed by remote host
Connection closed by 203.0.113.10 port 22
kex_exchange_identification: read: Connection reset by peer
Quick fix: Run ssh -vvv user@host and check on the server: sudo journalctl -u ssh -n 50 (or sshd). If you're banned, unban with sudo fail2ban-client set sshd unbanip <your-ip>. Check /etc/hosts.deny, MaxStartups in /etc/ssh/sshd_config, and validate the config with sudo sshd -t. For GitHub, try ssh -T -p 443 git@ssh.github.com.
What it means
The SSH handshake starts with each side sending a version banner (SSH-2.0-OpenSSH_9.6). Your client connected but the server closed (or reset) the connection before its banner arrived. That rules out "host down" and "port closed" (those give timeouts or Connection refused): something on or in front of the server is accepting and immediately dropping you.
Get details:
ssh -vvv user@host
nc -v host 22 # a healthy server prints SSH-2.0-... straight away
If nc shows no banner and closes, the problem is server- or network-side. On the server (via console if needed):
sudo journalctl -u ssh -u sshd -n 50 --no-pager
sudo sshd -t # config syntax check
Cause 1: fail2ban, sshguard or a firewall banned your IP
Repeated failed logins get an IP blocked, and some setups reject rather than drop. On the server:
sudo fail2ban-client status sshd
sudo fail2ban-client set sshd unbanip 198.51.100.7
Add your IP to ignoreip in /etc/fail2ban/jail.local if you get banned often, and fix whatever caused the failures (often a client offering many keys — see Too many authentication failures).
Cause 2: MaxStartups — too many unauthenticated connections
sshd drops new connections when too many are still in the auth phase (default MaxStartups 10:30:100: start randomly dropping at 10). Brute-force bots or parallel automation (Ansible forks, CI jobs) trigger it. The log shows drop connection #10 from ... past MaxStartups.
In /etc/ssh/sshd_config:
MaxStartups 30:30:100
LoginGraceTime 30
sudo sshd -t && sudo systemctl reload ssh # service is "sshd" on RHEL/Fedora/Arch
Better long-term: restrict port 22 to known IPs, or use fail2ban to cut bot traffic.
Cause 3: TCP wrappers (/etc/hosts.deny)
On systems still using libwrap, a line like sshd: ALL in /etc/hosts.deny closes connections instantly. Check /etc/hosts.allow and /etc/hosts.deny.
Cause 4: sshd is broken
If sshd can't load its host keys or config, it may accept and immediately close. Signs in the log: no hostkeys available, Missing privilege separation directory: /run/sshd, permission errors on /etc/ssh/ssh_host_*_key.
sudo ssh-keygen -A # regenerate missing host keys
sudo mkdir -p /run/sshd
sudo chmod 600 /etc/ssh/ssh_host_*_key
sudo sshd -t && sudo systemctl restart ssh
Full disk can cause the same — check df -h.
Cause 5: something else is on that port, or a proxy/VPN interferes
A load balancer, port forward or Docker container may be listening on port 22 but forwarding to nothing. Corporate proxies and some VPNs also reset SSH. Test from another network, and check what's listening on the server: sudo ss -tlnp | grep :22.
For GitHub/GitLab blocked on port 22, use their SSH-over-443 endpoints in ~/.ssh/config:
Host github.com
HostName ssh.github.com
Port 443
User git
Then ssh -T git@github.com. GitLab.com uses altssh.gitlab.com port 443.
Cause 6: wrong port or server is still booting
Right after a reboot, cloud VMs may accept TCP before sshd is ready. Wait a minute and retry. If sshd listens on a custom port, connect with ssh -p 2222 user@host.
Last updated 2026-11-05.