ssh: connect to host sshd port 22: Connection refused means the client reached the machine but nothing there accepted a connection on port 22. Check on the server whether anything listens on 22, and if nothing does, start the SSH server.

In this test the server machine was up and reachable, but sshd had never been started. Starting it fixed the error without any change on the client.

The error

$ ssh -o BatchMode=yes dev@sshd true
ssh: connect to host sshd port 22: Connection refused
exit=255

Why it happens

The name sshd resolved (otherwise the message would be Could not resolve hostname) and the connection attempt reached the server, which answered that no program was listening on port 22. The refusal came before any login step, so neither the user name nor the key played a part, and the client exited with code 255. On the server side the listening sockets showed nothing on port 22, and the process list held only the shell and the container's sleep:

$ ss -ltn
State  Recv-Q Send-Q Local Address:Port  Peer Address:PortProcess
LISTEN 0      4096      127.0.0.11:44595      0.0.0.0:*
exit=0
$ ls /proc/[0-9]*/comm | xargs cat | sort -u
...
bash
sleep
xargs
exit=0

The one listener is at 127.0.0.11, the address the container's /etc/resolv.conf names as its DNS server, not SSH. A container started from ubuntu:24.04 runs the command it was given (here sleep infinity); installing openssh-server with apt inside it did not leave an SSH server running.

The fix

$ /usr/sbin/sshd -t; echo config-ok
config-ok
exit=0
$ /usr/sbin/sshd
exit=0
$ ss -ltn
State  Recv-Q Send-Q Local Address:Port  Peer Address:PortProcess
LISTEN 0      128          0.0.0.0:22         0.0.0.0:*
LISTEN 0      4096      127.0.0.11:44595      0.0.0.0:*
LISTEN 0      128             [::]:22            [::]:*
exit=0

sshd -t checks the configuration first. The server then listened on port 22 on all interfaces, and the client's next connection went through:

$ ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new dev@sshd hostname
Warning: Permanently added 'sshd' (ED25519) to the list of known hosts.
eed60eb6a2a1
exit=0

The test accepted the host key of its own fresh container with accept-new; on a real server, compare the fingerprint first, as shown in SSH: Remote host identification has changed. Before this run a setup script had created the folder /run/sshd with mkdir -p /run/sshd and host keys with ssh-keygen -A. This test started the daemon by hand inside a container; an Ubuntu machine that runs systemd was not part of it.

How it was reproduced

Reproduced inside ubuntu:24.04 containers on Docker engine 29.9.3, client and server on one user-defined Docker network, with OpenSSH_9.6p1 Ubuntu-3ubuntu13.20 installed by apt-get install -y openssh-client openssh-server (and iproute2 for ss on the server). The server container had a test user dev with the client's public key; all keys were generated inside the containers. The containers were removed afterwards.

Frequently asked

What does ssh connect to host port 22 Connection refused mean?
The host answered, but nothing on it accepted connections on port 22. In this test the SSH server had not been started.
How do I check if sshd is listening on port 22?
On the server run ss -ltn and look for a line with :22 in the Local Address column. Before the fix there was none; after starting sshd there were lines for 0.0.0.0:22 and [::]:22.
Why is SSH refused in a Docker container with openssh-server installed?
In this test installing the package did not start the server. The container ran only its own command until /usr/sbin/sshd was started by hand.

If the name does not resolve at all, see SSH: Could not resolve hostname. More decoded errors in the Fixes category.