SSD Nodes Learn 🎉 VPS from $5.50/mo
Guides Matt ConnorBy Matt Connor

SSH: Connection Refused vs Connection Timed Out

Two SSH errors, opposite causes. Refused means the server answered. Timed out means nothing answered at all. Learn which test to run, and from where.

What "Connection refused" and "Connection timed out" mean in SSH

An SSH connection refused and an SSH connection timed out are opposite failures, so the fix for one is never the fix for the other. Refused means your packet reached the server and the server's kernel answered "nothing is listening here". Timed out means your packet reached nobody who would answer, so your client waited and gave up. Refused is a service problem on the server. Timed out is a path problem in front of it.

Read the exact line your client printed, because the wording is the whole diagnosis.

ssh: connect to host 203.0.113.10 port 22: Connection refused
ssh: connect to host 203.0.113.10 port 22: Connection timed out

Timing is the second clue. Refused comes back at once, in about the time one round trip takes. Timed out sits there for many seconds before it prints, because the client keeps retransmitting before it gives up. macOS prints Operation timed out for the same condition. If the protocol itself is new to you, how SSH works and what sshd does is the background this guide assumes.

Why "Connection refused" is good news

Refused is a TCP (transmission control protocol) reset. Your client sends a SYN packet to port 22. It crosses the internet, arrives at the server's network stack, and the kernel finds no socket listening on that port, so it answers with a RST (reset) packet. Your SSH client turns that RST into the words Connection refused.

That one returning packet proves a lot. The address is right. The host is powered on and routing. Nothing on the path is silently discarding traffic to that port, because something came back from the far end. So every remaining suspect lives on the server itself.

  • sshd is not running, because it failed to start or was never enabled.
  • sshd is listening on another port, usually after a hardening change.
  • sshd is bound to one address, such as ListenAddress 127.0.0.1, so only the server itself can reach it.
  • A firewall is set to reject rather than drop, so the firewall sends the RST on the host's behalf. The ufw reject action and an nftables rule ending in reject with tcp reset both do this.

One more case looks like those and is not: you typed an address that belongs to a different live host. That host answers your SYN, has no SSH on port 22, and refuses you politely. Confirm the address before you spend an hour on the wrong server. Knowing what a listening port actually is on Linux makes the rest of this section read faster.

How to fix Connection refused

You cannot fix this over SSH, because SSH is the thing that is broken. Open your provider's web console or serial console, sign in there, then work down these commands.

systemctl status ssh
sudo ss -tlnp
sudo sshd -T | grep -Ei '^(port|listenaddress|addressfamily)'

systemctl status ssh uses the unit name on Ubuntu and Debian. On RHEL and its rebuilds such as AlmaLinux, the unit is sshd. ss -tlnp lists every TCP socket in the listening state together with the process that owns it, and it is the ground truth: if no line mentions sshd, then nothing is listening, whatever the config file claims. sshd -T prints the effective configuration after every Include file is merged, which is where a forgotten port in /etc/ssh/sshd_config.d/ shows itself.

Read the address column carefully. 0.0.0.0:22 means every IPv4 address on the box. [::]:22 means every IPv6 address. 127.0.0.1:22 means loopback only, so every remote connection to it is refused while a local ssh localhost works perfectly.

If nothing is listening, start the service and read the failure when it will not start.

sudo sshd -t
sudo systemctl enable --now ssh
sudo journalctl -u ssh -n 50 --no-pager

sshd -t parses the configuration and prints the file and line number of a bad directive without touching the running service. Run it before every restart, because a rejected config means sshd exits at start and your next connection is refused.

The socket activation trap on Ubuntu

Ubuntu 24.04 ships a systemd socket unit for OpenSSH. Where that unit is enabled, systemd holds the listening port and starts sshd per connection, so Port 2222 in sshd_config changes nothing and the box keeps answering on the old port. Check which mode yours is in before you edit anything.

systemctl is-enabled ssh.socket
systemctl status ssh.socket

If the socket is enabled, set the port in the socket unit instead of in sshd_config.

sudo systemctl edit ssh.socket
[Socket]
ListenStream=
ListenStream=2222

The empty ListenStream= line is required, because systemd list settings add to what is already configured. Leave it out and the server listens on both ports. Apply the change with sudo systemctl daemon-reload and sudo systemctl restart ssh.socket, then confirm with sudo ss -tlnp that the new port is the one being held. Moving the port is a normal step in hardening SSH on a VPS, and it is the step that locks people out most often.

Why "Connection timed out" means nothing answered

A timeout is silence. Your client sent a SYN, retransmitted it several times over a minute or two, and never received one packet in return. Nothing about the server is proven here, because nothing from the server was ever heard.

Silence is exactly what a DROP rule produces, and dropping is deliberate. A rejection tells anyone scanning that the host exists, so ufw and every cloud provider's network firewall discard unwanted packets and send nothing back. Your timeout is usually a firewall doing its job on a port you wanted open.

  • The address is wrong: a DNS record still pointing at a server you rebuilt, or a typo that lands on an address nobody uses.
  • The host is not up: powered off, or halfway through a reboot. A provider suspension over billing looks identical from outside.
  • The host firewall drops port 22, most often because ufw enable ran before any allow rule existed.
  • A provider firewall in front of the instance drops it, and the operating system never sees the packet at all.
  • Your own network blocks outbound port 22, which is common on office and hotel connections.

Run the test from the right side of the connection

Here is the mistake that costs the most time. You cannot diagnose a dropped packet from inside the box the packets are not reaching. If you could log in to run the command, you would not have the problem. Every command in this section runs on your own machine.

getent hosts vps.example.com
ssh -G vps.example.com | grep -Ei '^(hostname|port|user)'
ssh -vvv -o ConnectTimeout=10 user@vps.example.com
nc -vz -w 5 203.0.113.10 22

getent hosts shows the address your machine will really use, which catches a stale DNS record in seconds. ssh -G prints the settings your client applies after reading ~/.ssh/config, so it catches an old Host block that quietly rewrites the hostname, the port or the user. ssh -vvv shows how far the attempt got: a last line about connecting to the address followed by a long pause is a timeout, while a line reporting the remote OpenSSH version means TCP already succeeded and your real problem is authentication. On Windows, Test-NetConnection 203.0.113.10 -Port 22 in PowerShell replaces nc.

Test the port, not the host. A failed ping proves nothing, because many providers filter ICMP (internet control message protocol) at the edge. A successful ping proves nothing either, because it says nothing about port 22.

Then change the one variable no command can change for you: your network. Retry from a phone hotspot. If the hotspot connects and your desk does not, the block is on your side of the internet, or your office address has been banned on the server.

The provider firewall you cannot see from the server

Most VPS panels offer a network firewall, sometimes called a security group or a cloud firewall, that runs upstream of your instance and keeps its own rule list. ufw status on the server cannot see it, which is why "but I already allowed port 22" is such a common sentence. Open the panel and read that list before you rewrite a single rule on the box.

One command settles the question, and it needs console access. Start it on the server, then try to connect from your laptop while it runs.

sudo tcpdump -ni any tcp port 22

If nothing appears while your client is trying, the packets are being discarded before they reach the operating system, so the fault is the provider firewall or the route to the host. If SYN packets arrive and no reply leaves, the drop is local and belongs to ufw or nftables. That single test splits the timeout branch in half, which is why it is worth the trip to the console.

ufw ordering, IPv6, and a ban you gave yourself

The ufw ordering mistake locks out more people than anything else here. sudo ufw enable applies a default policy of deny incoming immediately, so with no SSH rule in place your current session survives on established state while every new connection times out. Allow first, then enable.

sudo ufw allow OpenSSH
sudo ufw status verbose

The OpenSSH application profile covers port 22 only. If you plan to move SSH to 2222, the rule you need is sudo ufw allow 2222/tcp, added before the port changes rather than after. The wider rule set is covered in ufw firewall basics for a VPS, and the safe ordering is part of what to do in the first ten minutes on a new VPS.

IPv6 produces a timeout that feels supernatural. If the hostname carries an AAAA record, your client tries IPv6 first, so a server whose IPv6 rules are missing hangs while a plain IPv4 attempt works. Separate the two by hand.

ssh -4 user@vps.example.com
ssh -6 user@vps.example.com

If -4 connects and -6 does not, the fix is on the server's IPv6 rules, and opening the same port for IPv6 in ufw walks through it.

You may also have banned yourself. fail2ban watches the authentication log and inserts a firewall rule against addresses that fail repeatedly, so a wrong key or a script retrying in the background can lock out a whole office address. A ban that drops looks like a timeout. A ban that rejects returns No route to host instead. From the console:

sudo fail2ban-client status sshd
sudo fail2ban-client set sshd unbanip 198.51.100.24

Adding your own address to ignoreip is part of a working fail2ban setup on Ubuntu 24.04.

Errors that are neither refused nor timed out

No route to host means an ICMP unreachable message came back. Either your own machine has no route toward that network, or something on the path answered with an administrative rejection, which is what an iptables REJECT rule sends.

Network is unreachable is your own machine speaking. It has no route for that address family at all, and it is the usual answer when a hostname resolves only to an IPv6 address on an IPv4-only connection.

kex_exchange_identification: Connection closed by remote host means TCP connected and the server then hung up before the key exchange finished. The port is open and sshd is alive, so look at server load, at MaxStartups, or at a ban that landed while you were connecting.

Permission denied (publickey) means you reached authentication and failed there. The network is fine and the firewall is fine, so nothing in this guide applies. Go to fixing Permission denied (publickey) on SSH instead.

How to get back in, and how to avoid a second lockout

Every serious VPS host gives you a console that does not depend on the guest's network: a serial console, or a browser based VNC screen. That console is the recovery route for both branches of this guide, because it keeps working when sshd is stopped and it keeps working when a firewall rule discards everything. Find it in the panel, sign in as root or as your normal user, then run the checks above. If you never set a root password, most panels can reset one for you.

Where no console exists, the fallback is the provider's rescue mode. It boots a small recovery system and mounts your disk, so you can edit /etc/ssh/sshd_config or delete a firewall rule offline and reboot.

Two habits prevent the next lockout. Keep a second SSH session open whenever you edit sshd or the firewall, because that session survives on established state while you test a fresh one. And give yourself an automatic undo before a risky firewall change.

sudo systemd-run --unit=ufw-rollback --on-active=10min /usr/sbin/ufw disable
sudo systemctl stop ufw-rollback.timer

The first line schedules ufw to switch itself off in ten minutes. Apply your new rules, open a new SSH session to prove they work, then run the second line to cancel the rollback. If you lock yourself out instead, wait ten minutes and the firewall stands down on its own. It leaves the box unfiltered until you enable ufw again, so use this while you are at the keyboard and not as a permanent arrangement.

The order to work in

  1. Read the error text, and notice how long it took to appear.
  2. Refused: go to the console and check sudo ss -tlnp for a listening socket, its port, and the address it is bound to.
  3. Timed out: from your own machine confirm the address, then check the provider firewall in the panel, then the host firewall on the box.
  4. Neither of those strings: you already have a TCP connection, so treat it as an authentication or a server-load question, not a network one.

FAQ

Why does SSH say "Connection refused" when sshd is running?

Because a refusal comes from the socket, not from the service, and a running sshd can still refuse you. Open the provider console and run sudo ss -tlnp. A socket on 127.0.0.1:22 refuses every remote client, because it is bound to loopback only. A socket on another port refuses everyone still using 22. If systemd socket activation is in use, the port comes from ssh.socket and not from sshd_config, so check systemctl is-enabled ssh.socket as well. A ufw reject rule also returns a refusal on the host's behalf, so read sudo ufw status verbose before you conclude anything.

Why does SSH time out when ufw already allows port 22?

Because a timeout means no answer came back, and ufw is not the only firewall in the path. Most VPS panels run a network firewall in front of the instance, and the operating system never sees what that firewall drops. From the console, run sudo tcpdump -ni any tcp port 22 and try to connect from your laptop while it runs. No packets arriving means the drop is upstream, in the panel. Packets arriving with no reply leaving means the drop is local, in ufw or nftables.

Does a failed ping mean my VPS is down?

No. Many providers filter ICMP at the network edge, so a server that is serving traffic normally can ignore every ping you send. A successful ping is just as weak in the other direction, because it says nothing about whether port 22 is open. Test the port itself with nc -vz -w 5 203.0.113.10 22 from your own machine, or with Test-NetConnection 203.0.113.10 -Port 22 in PowerShell on Windows.

I changed the SSH port and now nothing connects. What went wrong?

Two orderings cause this. If the firewall never got a rule for the new port, attempts to the new port time out while port 22 refuses, so sudo ufw allow 2222/tcp belongs before the port change and not after it. If the box uses systemd socket activation for SSH, Port 2222 in sshd_config is ignored and systemd keeps holding the old port, which you can confirm with systemctl is-enabled ssh.socket. Recover through the provider console, fix whichever one applies, then connect with ssh -p 2222 user@203.0.113.10 once sudo ss -tlnp shows the new socket.