Installing and Configuring the SSH Server
This article explains how to install the SSH server (OpenSSH) on Ubuntu 26.04 LTS Server and how to configure public key authentication, disable password authentication, restrict root logins, and change the port number.
The examples were captured on a server whose prompt is kazulog@sv1, reachable at 10.19.12.11. The OpenSSH version is 10.2p1.
Checking Whether openssh-server Is Installed
If you selected “Install OpenSSH server” in the installer, or if you use a cloud image, it is already installed. Check with dpkg -l.
dpkg -l openssh-serverkazulog@sv1:~$ dpkg -l openssh-server
+-- Desired=Unknown/Install/Remove/Purge/Hold
|+- Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
||+ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||| Name Version Architecture Description
+++-==============-============-============-=================================
un openssh-server <none> <none> (no description available)
kazulog@sv1:~$A line starting with ii means it is installed; un means it is not. When it is not installed, systemd does not know the service and port 22 is not open.
kazulog@sv1:~$ systemctl status ssh --no-pager
Unit ssh.service could not be found.
kazulog@sv1:~$ ss -tlnp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 127.0.0.54:53 0.0.0.0:*
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
kazulog@sv1:~$ ls /etc/ssh/
ssh_config ssh_config.d ssh_import_id sshd_config.d
kazulog@sv1:~$Installing openssh-server
sudo apt update
sudo apt install -y openssh-serverkazulog@sv1:~$ sudo apt-get install -y openssh-server
/snip/
The following additional packages will be installed:
openssh-client openssh-sftp-server
Suggested packages:
molly-guard monkeysphere ssh-askpass
The following NEW packages will be installed:
openssh-server
The following packages will be upgraded:
openssh-client openssh-sftp-server
2 upgraded, 1 newly installed, 0 to remove and 89 not upgraded.
Need to get 1594 kB of archives.
After this operation, 2903 kB of additional disk space will be used.
/snip/
Get:1 http://archive.ubuntu.com/ubuntu resolute-updates/main amd64 openssh-sftp-server amd64 1:10.2p1-2ubuntu3.6 [37.4 kB]
Get:2 http://archive.ubuntu.com/ubuntu resolute-updates/main amd64 openssh-client amd64 1:10.2p1-2ubuntu3.6 [935 kB]
Get:3 http://archive.ubuntu.com/ubuntu resolute-updates/main amd64 openssh-server amd64 1:10.2p1-2ubuntu3.6 [622 kB]
Fetched 1594 kB in 3s (576 kB/s)
Preconfiguring packages ...
Preparing to unpack .../openssh-sftp-server_1%3a10.2p1-2ubuntu3.6_amd64.deb ...
Unpacking openssh-sftp-server (1:10.2p1-2ubuntu3.6) over (1:10.2p1-2ubuntu3.5) ...
Preparing to unpack .../openssh-client_1%3a10.2p1-2ubuntu3.6_amd64.deb ...
Unpacking openssh-client (1:10.2p1-2ubuntu3.6) over (1:10.2p1-2ubuntu3.5) ...
Selecting previously unselected package openssh-server.
Preparing to unpack .../openssh-server_1%3a10.2p1-2ubuntu3.6_amd64.deb ...
Unpacking openssh-server (1:10.2p1-2ubuntu3.6) ...
Setting up openssh-client (1:10.2p1-2ubuntu3.6) ...
Setting up openssh-sftp-server (1:10.2p1-2ubuntu3.6) ...
Setting up openssh-server (1:10.2p1-2ubuntu3.6) ...
Creating config file /etc/ssh/sshd_config with new version
Creating SSH2 RSA key; this may take some time ...
3072 SHA256:yPzZfYBLregCDh6TUn9u8D+dz88uz/sUuPws3BEyJCc root@sv1 (RSA)
Creating SSH2 ECDSA key; this may take some time ...
256 SHA256:eVS1zCTSGxd3paUBUk5E9UzYJe+zTP0EvQeYulweXvw root@sv1 (ECDSA)
Creating SSH2 ED25519 key; this may take some time ...
256 SHA256:vDLOqQp6GgOzTjPqeoR46WuIvZDNbZwA6yCtoqLsv/A root@sv1 (ED25519)
Creating user 'sshd' (sshd user) with UID 986 and GID 65534.
Created symlink '/etc/systemd/system/sockets.target.wants/ssh.socket' → '/usr/lib/systemd/system/ssh.socket'.
Created symlink '/etc/systemd/system/ssh.service.requires/ssh.socket' → '/usr/lib/systemd/system/ssh.socket'.
Created symlink '/etc/systemd/system/ssh.service.wants/sshd-keygen.service' → '/usr/lib/systemd/system/sshd-keygen.service'.
Created symlink '/etc/systemd/system/sshd.service.wants/sshd-keygen.service' → '/usr/lib/systemd/system/sshd-keygen.service'.
Created symlink '/etc/systemd/system/sshd@.service.wants/sshd-keygen.service' → '/usr/lib/systemd/system/sshd-keygen.service'.
Created symlink '/etc/systemd/system/ssh.socket.wants/sshd-keygen.service' → '/usr/lib/systemd/system/sshd-keygen.service'.
Processing triggers for man-db (2.13.1-1build1) ...
Processing triggers for ufw (0.36.2-9build1) ...
/snip/
Running kernel seems to be up-to-date.
No services need to be restarted.
No containers need to be restarted.
No user sessions are running outdated binaries.
No VM guests are running outdated hypervisor (qemu) binaries on this host.
kazulog@sv1:~$The host keys (RSA, ECDSA and ED25519) are generated during installation and the service is enabled. The server accepts connections on port 22 as soon as the installation finishes, so no extra start-up step is needed.
Checking the SSH Server Status
systemctl status ssh
ss -tlnpkazulog@sv1:~$ systemctl status ssh --no-pager -l | cat
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/usr/lib/systemd/system/ssh.service; disabled; preset: enabled)
Active: active (running) since Thu 2026-09-10 14:39:29 JST; 1min 39s ago
Invocation: 049398a869f542468581164f8dd0f5af
TriggeredBy: ● ssh.socket
Docs: man:sshd(8)
man:sshd_config(5)
Process: 1021 ExecStartPre=/usr/sbin/sshd -t (code=exited, status=0/SUCCESS)
Main PID: 1060 (sshd)
Tasks: 1 (limit: 1486)
Memory: 7.7M (peak: 9.4M)
CPU: 408ms
CGroup: /system.slice/ssh.service
└─1060 "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups"
Sep 10 14:40:00 sv1 sshd-session[1231]: Accepted publickey for kazulog from 10.100.3.1 port 63509 ssh2: ED25519 SHA256:NdURwOz/PbiL79UHvTtYE5vuTJfA2RD5RXsn/rG76rw
Sep 10 14:40:00 sv1 sshd-session[1231]: pam_unix(sshd:session): session opened for user kazulog(uid=1000) by kazulog(uid=0)
Sep 10 14:40:02 sv1 sshd-session[1367]: Accepted publickey for kazulog from 10.100.3.1 port 63510 ssh2: ED25519 SHA256:NdURwOz/PbiL79UHvTtYE5vuTJfA2RD5RXsn/rG76rw
Sep 10 14:40:02 sv1 sshd-session[1367]: pam_unix(sshd:session): session opened for user kazulog(uid=1000) by kazulog(uid=0)
Sep 10 14:40:03 sv1 sshd-session[1367]: syslogin_perform_logout: logout() returned an error
Sep 10 14:40:03 sv1 sshd-session[1367]: pam_unix(sshd:session): session closed for user kazulog
Sep 10 14:40:38 sv1 sshd-session[1584]: Accepted publickey for kazulog from 10.100.3.1 port 63530 ssh2: ED25519 SHA256:NdURwOz/PbiL79UHvTtYE5vuTJfA2RD5RXsn/rG76rw
Sep 10 14:40:38 sv1 sshd-session[1584]: pam_unix(sshd:session): session opened for user kazulog(uid=1000) by kazulog(uid=0)
Sep 10 14:41:07 sv1 sshd-session[1824]: Accepted publickey for kazulog from 10.100.3.1 port 63549 ssh2: ED25519 SHA256:NdURwOz/PbiL79UHvTtYE5vuTJfA2RD5RXsn/rG76rw
Sep 10 14:41:07 sv1 sshd-session[1824]: pam_unix(sshd:session): session opened for user kazulog(uid=1000) by kazulog(uid=0)
kazulog@sv1:~$kazulog@sv1:~$ ss -tlnp
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 4096 127.0.0.54:53 0.0.0.0:*
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:*
LISTEN 0 4096 [::]:22 [::]:*
kazulog@sv1:~$ssh.service itself is disabled; ssh.socket listens on port 22 and starts ssh.service when a connection arrives. Comparing the two with systemctl is-enabled shows the difference. Because of this, changing the port number works differently from the traditional setup (see below).kazulog@sv1:~$ systemctl is-enabled ssh ssh.socket
disabled
enabled
kazulog@sv1:~$Connecting with Password Authentication
From the client, connect with ssh [NAME]@[IP].
ssh [NAME]@[IP]| Field | Value |
|---|---|
| [NAME] | User name on the target server |
| [IP] | IP address of the target server |
taro@10.19.12.11's password:
Welcome to Ubuntu 26.04 LTS (GNU/Linux 7.0.0-30-generic x86_64)
* Documentation: https://docs.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Thu Sep 10 14:48:26 JST 2026
System load: 0.18 Processes: 111
Usage of /: 3.6% of 60.93GB Users logged in: 0
Memory usage: 12% IPv4 address for ens2: 10.19.12.11
Swap usage: 0%
Expanded Security Maintenance for Applications is not enabled.
96 updates can be applied immediately.
87 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
Last login: Thu Sep 10 14:47:34 2026 from 10.100.3.1
taro@sv1:~$ whoami
taroWhether a login succeeded can be checked with journalctl on the server. A successful password login is recorded as Accepted password.
sudo journalctl -u sshkazulog@sv1:~$ sudo journalctl -u ssh --since '3 min ago' --no-pager | grep -E 'taro' | tail -5
Sep 10 14:47:34 sv1 sshd-session[3981]: Accepted password for taro from 10.100.3.1 port 63884 ssh2
Sep 10 14:47:34 sv1 sshd-session[3981]: pam_unix(sshd:session): session opened for user taro(uid=1001) by taro(uid=0)
Sep 10 14:47:35 sv1 sshd-session[3981]: pam_unix(sshd:session): session closed for user taroSetting Up Public Key Authentication
Creating a Key Pair
Run ssh-keygen on the client. -t ed25519 selects the key type, which is the current recommendation. You are asked for the file name and a passphrase; press Enter to accept the defaults.
ssh-keygen -t ed25519 -C "[COMMENT]"| Field | Value |
|---|---|
| [COMMENT] | Comment identifying the key (optional; a user or machine name is common) |
kazulog@sv1:~$ ssh-keygen -t ed25519 -C 'kazulog@client'
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/kazulog/.ssh/id_ed25519):
Enter passphrase for "/home/kazulog/.ssh/id_ed25519" (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/kazulog/.ssh/id_ed25519
Your public key has been saved in /home/kazulog/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:TacHzXz0/ATZw/mJpyrO5/5UTf6VseuQMZNq6LagEF8 kazulog@client
The key's randomart image is:
+--[ED25519 256]--+
| +o.|
| + ..B.|
| o = o.O|
| o + o.*B|
| . ES o .=o+=|
| o . ....* +|
| . . . . o.+ ..|
| . . +o.o. o |
| . o=*o.. . |
+----[SHA256]-----+
kazulog@sv1:~$This creates the private key id_ed25519 and the public key id_ed25519.pub. Never distribute the private key; keep it on the client.
Copying the Public Key to the Server
ssh-copy-id appends the public key to ~/.ssh/authorized_keys on the server. It needs a working password login.
ssh-copy-id [NAME]@[IP]kazulog@sv1:~$ ssh-copy-id taro@localhost
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/kazulog/.ssh/id_ed25519.pub"
The authenticity of host 'localhost (::1)' can't be established.
ED25519 key fingerprint is: SHA256:vDLOqQp6GgOzTjPqeoR46WuIvZDNbZwA6yCtoqLsv/A
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
taro@localhost's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'taro@localhost'"
and check to make sure that only the key(s) you wanted were added.
kazulog@sv1:~$ sudo ls -la /home/taro/.ssh/
total 12
drwx------ 2 taro taro 4096 Sep 10 14:50 .
drwxr-x--- 4 taro taro 4096 Sep 10 14:50 ..
-rw------- 1 taro taro 96 Sep 10 14:50 authorized_keys
kazulog@sv1:~$ sudo cat /home/taro/.ssh/authorized_keys
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEMqxsy1w3cy075/v+S2ltHfVQOMZIYrE7yBHCwiqhUT kazulog@clientThe .ssh directory must be mode 700 and authorized_keys mode 600. ssh-copy-id sets these permissions for you.
Where ssh-copy-id is not available, append the public key manually.
cat ~/.ssh/id_ed25519.pub | ssh [NAME]@[IP] "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"Connecting with the Key
Once the key is in place, you connect without typing a password. The log records Accepted publickey.
kazulog@sv1:~$ ssh taro@localhost
Welcome to Ubuntu 26.04 LTS (GNU/Linux 7.0.0-30-generic x86_64)
* Documentation: https://docs.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Thu Sep 10 14:50:35 JST 2026
System load: 0.12 Processes: 108
Usage of /: 3.6% of 60.93GB Users logged in: 0
Memory usage: 12% IPv4 address for ens2: 10.19.12.11
Swap usage: 0%
Expanded Security Maintenance for Applications is not enabled.
96 updates can be applied immediately.
87 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
Last login: Thu Sep 10 14:48:26 2026 from 10.100.3.1
taro@sv1:~$ whoami
taro
taro@sv1:~$ exit
logout
Connection to localhost closed.
kazulog@sv1:~$ sudo journalctl -u ssh --no-pager --since '2 min ago' | grep -E 'taro' | tail -3
Sep 10 14:50:44 sv1 sshd-session[5095]: Accepted publickey for taro from ::1 port 35702 ssh2: ED25519 SHA256:TacHzXz0/ATZw/mJpyrO5/5UTf6VseuQMZNq6LagEF8
Sep 10 14:50:44 sv1 sshd-session[5095]: pam_unix(sshd:session): session opened for user taro(uid=1001) by taro(uid=0)
Sep 10 14:50:44 sv1 sshd-session[5095]: pam_unix(sshd:session): session closed for user taroHow the Configuration Files Are Organised
The SSH server is configured in /etc/ssh/sshd_config, but do not edit that file: add a .conf file under /etc/ssh/sshd_config.d/ instead. An Include near the top of sshd_config reads them.
kazulog@sv1:~$ grep -n 'Include' /etc/ssh/sshd_config
24:Include /etc/ssh/sshd_config.d/*.conf
kazulog@sv1:~$ ls -l /etc/ssh/sshd_config.d/
total 8
-rw------- 1 root root 27 Sep 10 13:30 50-cloud-init.conf
-rw-r--r-- 1 root root 26 Aug 24 01:26 60-cloudimg-settings.conf
kazulog@sv1:~$ sudo head -100 /etc/ssh/sshd_config.d/50-cloud-init.conf /etc/ssh/sshd_config.d/60-cloudimg-settings.conf
==> /etc/ssh/sshd_config.d/50-cloud-init.conf <==
PasswordAuthentication yes
==> /etc/ssh/sshd_config.d/60-cloudimg-settings.conf <==
PasswordAuthentication no
kazulog@sv1:~$Include reads the files in alphabetical order, so a new file has to sort before the existing ones. In the example above, 50-cloud-init.conf sets PasswordAuthentication yes, so writing no in a file starting with 99- has no effect.Check the effective value with sshd -T. Adding a file is not enough on its own, so always verify with this command.
sudo sshd -T | grep -i [KEYWORD]Disabling Password Authentication
To allow public key authentication only, disable password authentication. Create the file, check the syntax with sshd -t, then apply it with systemctl reload ssh.
echo 'PasswordAuthentication no' | sudo tee /etc/ssh/sshd_config.d/10-hardening.conf
sudo sshd -t
sudo systemctl reload sshkazulog@sv1:~$ sudo grep -H PasswordAuthentication /etc/ssh/sshd_config.d/*.conf
/etc/ssh/sshd_config.d/10-hardening.conf:PasswordAuthentication no
/etc/ssh/sshd_config.d/50-cloud-init.conf:PasswordAuthentication yes
/etc/ssh/sshd_config.d/60-cloudimg-settings.conf:PasswordAuthentication no
kazulog@sv1:~$ sudo sshd -T | grep -i passwordauthentication
passwordauthentication nopasswordauthentication no confirms it is in effect. A client that offers only a password is now rejected before it is even prompted.
taro@10.19.12.11: Permission denied (publickey).Users whose public key is installed continue to connect as before.
kazulog@sv1:~$ ssh taro@localhost whoami
taro
kazulog@sv1:~$Forbidding root Logins
The default value of PermitRootLogin is prohibit-password. Password logins as root are refused, but root can still log in with a public key if one is installed. Set it to no to forbid root logins entirely.
printf 'PasswordAuthentication no\nPermitRootLogin no\n' | sudo tee /etc/ssh/sshd_config.d/10-hardening.conf
sudo sshd -t
sudo systemctl reload sshkazulog@sv1:~$ sudo sshd -T | grep -i permitrootlogin
permitrootlogin prohibit-password
kazulog@sv1:~$With no, even a valid key is refused with Permission denied, and the log records ROOT LOGIN REFUSED.
Note that root has no password on Ubuntu, so password logins as root are impossible even with the default prohibit-password. The root account is covered in Creating Users and Granting sudo Privileges on Ubuntu 26.04 LTS Server.
Changing the Port Number
Because of socket activation, writing Port and running systemctl reload ssh does not change the listening port. Only the output of sshd -T changes; the server still listens on 22.
kazulog@sv1:~$ echo 'Port 2222' | sudo tee -a /etc/ssh/sshd_config.d/10-hardening.conf
Port 2222
kazulog@sv1:~$ sudo sshd -t && sudo systemctl reload ssh
kazulog@sv1:~$ sudo sshd -T | grep -i '^port'
port 2222
kazulog@sv1:~$ ss -tlnp | grep -E ':22|:2222'
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:*
LISTEN 0 4096 [::]:22 [::]:*
kazulog@sv1:~$ ssh -p 2222 taro@localhost whoami
ssh: connect to host localhost port 2222: Connection refused
kazulog@sv1:~$The listening port is owned by ssh.socket. A systemd generator (sshd-socket-generator) turns the Port in sshd_config into the socket’s configuration, so applying the change means re-running the generator with daemon-reload and restarting ssh.socket.
sudo systemctl daemon-reload
sudo systemctl restart ssh.socketkazulog@sv1:~$ sudo systemctl daemon-reload
kazulog@sv1:~$ sudo systemctl restart ssh.socket
kazulog@sv1:~$ ss -tlnp | grep -E ':22 |:2222'
LISTEN 0 4096 0.0.0.0:2222 0.0.0.0:*
LISTEN 0 4096 [::]:2222 [::]:*
kazulog@sv1:~$ cat /run/systemd/generator/ssh.socket.d/addresses.conf
# Automatically generated by sshd-socket-generator
[Socket]
ListenStream=
ListenStream=0.0.0.0:2222
ListenStream=[::]:2222
kazulog@sv1:~$ ssh -p 2222 taro@localhost whoami
taross now shows port 2222 and ssh -p 2222 connects. To revert, delete the Port line and run daemon-reload and restart again.
kazulog@sv1:~$ sudo sed -i '/^Port 2222/d' /etc/ssh/sshd_config.d/10-hardening.conf
kazulog@sv1:~$ sudo cat /etc/ssh/sshd_config.d/10-hardening.conf
PasswordAuthentication no
PermitRootLogin no
kazulog@sv1:~$ sudo systemctl daemon-reload && sudo systemctl restart ssh.socket
kazulog@sv1:~$ ss -tlnp | grep -E ':22 |:2222'
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:*
LISTEN 0 4096 [::]:22 [::]:* Checking the Authentication Log
Successful and failed logins are visible with journalctl -u ssh.
| Log entry | Meaning |
|---|---|
Accepted publickey for [NAME] | Logged in with public key authentication |
Accepted password for [NAME] | Logged in with password authentication |
Failed password for [NAME] | Wrong password |
Connection closed by authenticating user | Disconnected without authenticating |
ROOT LOGIN REFUSED | A root login was refused by PermitRootLogin no |
sudo journalctl -u ssh --since "10 min ago"Test Environment and Session Logs
The examples were captured on Ubuntu 26.04 LTS Server (OpenSSH 10.2p1) running on CML. The session log of each step can be downloaded below. Files marked _console were captured on the console (while SSH was unavailable) and _mac on the client.
| Step | Session log |
|---|---|
| Before installation (not installed) | log |
| Connecting from the client while not installed | log |
| Installing openssh-server | log |
| Settings right after installation | log |
| Checking the SSH server status | log |
| Password login (client side) | log |
| Password login (server log) | log |
| Creating a key pair | log |
| Copying the public key (ssh-copy-id) | log |
| Logging in with the key | log |
| Disabling password authentication | log |
| Checking the drop-in read order | log |
| Password authentication refused (client side) | log |
| Forbidding root logins | log |
| Port change (reload only) | log |
| Port change (daemon-reload and restart) | log |
| Reverting the port number | log |
Reference
Ubuntu Server documentation: OpenSSH server
Related articles
- Changing the Hostname on Ubuntu 26.04 LTS Server
- Ubuntu 26.04 LTS Server System Optimization with apt update/upgrade
- Changing the Timezone to Japan Standard Time (JST) on Ubuntu 26.04 LTS Server
- Creating Users and Granting sudo Privileges on Ubuntu 26.04 LTS Server
- Configuring the SSH Server on Ubuntu 26.04 LTS Server
- Managing Services with systemctl and Reading Logs with journalctl on Ubuntu 26.04 LTS Server
- Configuring Automatic Updates on Ubuntu 26.04 LTS Server (unattended-upgrades)
- How to Install the Latest neovim from the Official Site on Ubuntu 26.04 LTS Server
- Configuring the Network on Ubuntu 26.04 LTS Server