Arch Linux

Install and Enable SSH Server on Arch Linux, Manjaro and EndeavourOS

The SSH server on Arch Linux is the openssh package and its sshd.service unit, and neither is set up for you. A base install does not include OpenSSH, and installing the package does not start or enable the service either, so a fresh machine refuses every connection until you do both. Manjaro and EndeavourOS use the same package: our Manjaro container got the same unit files and the same 99-archlinux.conf drop-in, with OpenSSH 10.5 against Arch’s 10.6 on test day.

Original content from computingforgeeks.com - post 63949

You need a normal user in the wheel group with working sudo before you start, because the hardening step blocks root logins. A fresh pacstrap system has only root and no sudo package, so create that user first if you haven’t (our post-install checklist covers it). Below we install and enable sshd, read the defaults Arch ships, switch to key-only login with a drop-in file, move the port and open the firewall. Every command ran on Arch Linux with OpenSSH 10.6 in October 2026, on a fresh pacstrap container and on a full VM.

Install OpenSSH

Check first whether the server is already there. The Arch cloud image our VM came from had it installed and running, while a plain pacstrap install and the Manjaro base image did not have it at all:

systemctl status sshd

On our fresh install systemd didn’t know the unit at all, which means the package is missing:

Unit sshd.service could not be found.

Install the package. It brings libedit as its only dependency on a base system, and pacman runs its hooks without touching the service:

sudo pacman -S openssh

The whole transaction is two packages and about 7 MiB, so it is safe to run on any machine, including one where you are unsure whether OpenSSH is already present:

Packages (2) libedit-20260512_3.1-1  openssh-10.6p1-2
Total Installed Size:  7.03 MiB

Right after the install, systemctl is-enabled sshd printed disabled and is-active printed inactive. Arch never enables a service from a package, so this step is always yours, and it is also why a remote machine you just installed can’t be reached even though the package is there. Manjaro ships the same sshd.service file, so expect the same there.

Enable and start sshd

enable --now does both jobs in one command: it starts sshd and makes it start at every boot:

sudo systemctl enable --now sshd

Check the status. You want active (running) and the listener line in the process list, which shows how many unauthenticated connections sshd is juggling (0 of 10-100 startups on an idle server):

systemctl status sshd

Our new server reported active (running) 273 ms after the start:

● sshd.service - OpenSSH Daemon
     Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; preset: disabled)
     Active: active (running) since Fri 2026-10-16 21:30:39 UTC; 273ms ago
 Invocation: 5d0f0cdbb1e94d5ca22f327c6b209b29
       Docs: man:sshd(8)
             man:sshd_config(5)
   Main PID: 195 (sshd)
      Tasks: 1 (limit: 9492)
     Memory: 1.3M (peak: 2M)
        CPU: 16ms
     CGroup: /system.slice/sshd.service
             └─195 "sshd: /usr/bin/sshd -D [listener] 0 of 10-100 startups"

sshd listens on every IPv4 and IPv6 address on port 22 by default. Confirm the sockets with sudo, because ss can only name processes that belong to other users when it runs as root:

sudo ss -tlnp | grep sshd

Both listeners belong to the same process. If you only see 0.0.0.0:22, IPv6 is disabled on the machine, which is fine as long as your clients connect over IPv4:

LISTEN 0      128          0.0.0.0:22        0.0.0.0:*    users:(("sshd",pid=195,fd=6))
LISTEN 0      128             [::]:22           [::]:*    users:(("sshd",pid=195,fd=7))

The first start also creates the host keys, because sshd.service pulls in sshdgenkeys.service. You’ll find four key pairs in /etc/ssh: ECDSA, Ed25519, RSA and a new ssh_host_mldsa44_ed25519_key. That last one is a hybrid post-quantum host key, which OpenSSH 10.6 and later generate alongside the classic ones, so a Manjaro machine still on 10.5 shows only three pairs. Our own OpenSSH 10.6 client still recorded the Ed25519 key in known_hosts on first connect, so existing client setups carry on as before.

Check the defaults Arch ships

Arch keeps the upstream sshd_config almost empty and puts its own settings in a drop-in. The main file has only three active lines, and the first one decides how every change you make later behaves:

grep -vE '^\s*(#|$)' /etc/ssh/sshd_config

Include comes before everything else, which is why drop-in files win over this file:

Include /etc/ssh/sshd_config.d/*.conf
AuthorizedKeysFile	.ssh/authorized_keys
Subsystem	sftp	/usr/lib/ssh/sftp-server

Arch’s own drop-in is 99-archlinux.conf. It turns off keyboard-interactive login, turns on PAM and stops sshd from printing the message of the day, which PAM already does. The other file in the directory, 20-systemd-userdb.conf, comes from systemd and lets sshd read keys stored in user records:

# sshd_config defaults on Arch Linux
KbdInteractiveAuthentication no
UsePAM yes
PrintMotd no

The file list does not tell you what sshd actually runs with, because sshd merges every file and fills in its compiled defaults for anything left unset. sshd -T prints that final merged config, so it is the command to trust whenever a setting seems to be ignored:

sudo sshd -T | grep -iE '^(port|permitrootlogin|passwordauthentication|pubkeyauthentication|kbdinteractiveauthentication|maxauthtries|x11forwarding) '

On a fresh install, passwords work for every user and root can log in with a key only:

Port 22
MaxAuthTries 6
PermitRootLogin prohibit-password
PubkeyAuthentication yes
PasswordAuthentication yes
KbdInteractiveAuthentication no
X11Forwarding no

That is usable on a LAN, but on a server reachable from the internet every account with a password becomes a target for guessing bots, and MaxAuthTries 6 gives each connection six tries. Root is also allowed in with a key, which is harmless until someone copies a key into /root/.ssh and forgets about it. The next two steps move you to keys and then turn passwords off, so neither door stays open.

Set up key-based login

Run these on the machine you connect from, not on the server. The key pair stays on your client, and only the public half goes to the server. If you already have a key in ~/.ssh, skip the first command:

ssh-keygen -t ed25519

ssh-copy-id logs in with your password one last time and appends the public key to ~/.ssh/authorized_keys on the server. Replace the user and address with yours:

ssh-copy-id -i ~/.ssh/id_ed25519.pub [email protected]

It asks for the account password once, copies the key and ends by asking you to test it. If it complains that no identities were found, the first command did not run or saved the key somewhere else:

Now try logging into the machine, with: "ssh -i /home/josphat/.ssh/id_ed25519 '[email protected]'"
and check to make sure that only the key(s) you wanted were added.

Test it with BatchMode, which forbids password prompts. If this command logs you in, the key works on its own:

ssh -o BatchMode=yes [email protected] 'echo key login OK'

Do not carry on until it prints key login OK. The next step turns passwords off, and a key that doesn’t work at that point locks you out of a remote server, so any problem here is cheaper to fix now. Our guide to SSH keys on Linux Mint covers passphrases and ssh-agent if you want them; the commands are the same on Arch.

Disable password login with a drop-in file

Put your changes in a new file under /etc/ssh/sshd_config.d/ rather than editing sshd_config. pacman ships sshd_config as a config file, so an upgrade with upstream changes leaves a .pacnew next to your edited copy, while a drop-in of your own is never touched. You also see every local change in one place:

sudo vim /etc/ssh/sshd_config.d/10-hardening.conf

Add these four lines. Put nothing else in this file yet, so a later problem is easy to trace back to one change:

PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
MaxAuthTries 3

Each line changes one default from the table above:

  • PermitRootLogin no blocks root, even with a key
  • PasswordAuthentication no leaves public keys as the only way in
  • KbdInteractiveAuthentication no repeats Arch’s default, so the file stands alone
  • MaxAuthTries 3 drops a connection after three failed attempts instead of six

MaxAuthTries counts every key your client offers, so if your ssh-agent holds more than three keys you can hit “Too many authentication failures” before the right one is tried. Add IdentitiesOnly yes and the right IdentityFile for this host in your client’s ~/.ssh/config. If root is still your only login on this machine, leave out PermitRootLogin no until your sudo user works, or you lock yourself out on the next connection.

Check the syntax before you reload, because a typo in any file stops sshd from starting:

sudo sshd -t

No output means the config is valid. Then reload sshd. Arch’s unit uses KillMode=process, so neither a reload nor a restart kills your open session, and a reload re-reads the config in place while keeping the same PID:

sudo systemctl reload sshd

Keep your current session open anyway and test from a second terminal, so a mistake can still be fixed from a working shell. A password-only attempt should now be refused before you are even asked for a password:

ssh -o PubkeyAuthentication=no [email protected] true

sshd now offers only public key authentication, and the error lists the methods it would accept in brackets, so (publickey) alone confirms passwords are gone:

[email protected]: Permission denied (publickey).

Your key login keeps working, which we confirmed on both the test container and the VM. sshd -T shows the new values:

sshd -T showing password login disabled and key login working on Arch Linux

In the capture, PasswordAuthentication no and PermitRootLogin no come from the drop-in, the password-only login is refused, and the key login goes through. The sshd -T values are what the next connection gets, so it is the check to rerun after any edit in the drop-in directory, before you close your working session.

Why sshd_config can’t undo a drop-in

sshd uses the first value it finds for each setting and ignores later ones. Because Include sits on the first line of sshd_config, every drop-in is read before anything in the main file, so settings at the bottom of sshd_config lose to the drop-ins. We tested both directions with PasswordAuthentication:

Where we added PasswordAuthentication yesEffective value with 10-hardening.conf saying no
End of /etc/ssh/sshd_configno, the drop-in wins
New drop-in 05-override.confyes, it sorts before 10-

Drop-ins are read in name order, so a lower number wins. That is why the hardening file starts with 10-: it beats Arch’s 99-archlinux.conf and any 50- file another tool drops in later. If a setting does not take effect, sshd -T shows you the value that won, and grep -r over /etc/ssh/sshd_config.d/ shows you which file set it.

Change the SSH port

Moving sshd off port 22 does not make it more secure, because a port scan finds the new one in seconds, but it does cut the noise in your logs from bots that only try 22. Put the port in its own drop-in so you can revert it by deleting one file:

echo 'Port 2222' | sudo tee /etc/ssh/sshd_config.d/10-port.conf

Validate and reload as before. sshd re-reads its config on reload and moves its listener, so you don’t need a restart:

sudo sshd -t && sudo systemctl reload sshd
sudo ss -tlnp | grep sshd

The listeners moved to 2222 without a restart, and the listener PID stayed 195:

LISTEN 0      128          0.0.0.0:2222      0.0.0.0:*    users:(("sshd",pid=195,fd=6))
LISTEN 0      128             [::]:2222         [::]:*    users:(("sshd",pid=195,fd=7))

Clients now need -p 2222, or a Port 2222 line for this host in their ~/.ssh/config, which our SSH config file guide explains. Connecting on the old port fails with ssh: connect to host 10.0.1.50 port 22: Connection refused. If ufw already runs on the server, run sudo ufw allow 2222/tcp before you reload, or your next connection won’t get through, then delete the old rule with sudo ufw delete allow 22/tcp once the new port works.

Open the port in the firewall

Arch does not ship a firewall. On our VM the nftables ruleset was empty and neither nftables.service nor iptables.service was enabled, so sshd was reachable the moment it started. If you add ufw, allow SSH before you enable it. ufw’s default policy drops all new incoming connections, so your open session survives but every new SSH connection would time out:

sudo pacman -S ufw
sudo ufw allow 22/tcp
sudo ufw enable

Use 2222/tcp instead if you moved the port. ufw enable warns that the command may disrupt existing SSH connections and asks you to confirm with y. Don’t enable iptables.service alongside it, because the two load conflicting rules. ufw confirms the rule for both IPv4 and IPv6:

sudo ufw status verbose

Incoming traffic is now denied except for SSH. Check that the default line says deny (incoming) and that your SSH port appears in both the IPv4 and the (v6) rows:

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    Anywhere                  
22/tcp (v6)                ALLOW IN    Anywhere (v6)             

Do not trust the message ufw enable prints. It says “Firewall is active and enabled on system startup”, but on Arch the systemd unit stays disabled, so the rules are gone after a reboot until you enable it yourself:

sudo systemctl enable ufw

systemctl is-enabled ufw printed enabled only after that command. We checked it on purpose, because a firewall that silently vanishes after a reboot is worse than none at all: you believe a port is closed when it is open, and nothing in the ufw output tells you otherwise. For the rest of ufw’s syntax, our ufw commands reference covers rate limiting and per-address rules.

Fix common sshd errors

Each of these came up while we tested, and each one names its cause if you look in the right place.

sshd fails with “Address already in use”

systemctl enable --now sshd returned “Job for sshd.service failed” on a machine where another sshd already held port 22. The status only says the start failed, so read the journal for the real error:

sudo journalctl -u sshd -o cat | grep -E 'Bind|fatal'

The bind lines name the port and both address families, and fatal: Cannot bind any address is what makes systemd mark the start as failed:

error: Bind to port 22 on 0.0.0.0 failed: Address already in use.
error: Bind to port 22 on :: failed: Address already in use.
fatal: Cannot bind any address.

Run sudo ss -tlnp 'sport = :22' to see which process holds the port, then stop it or move one of the two to another port. We hit it because a container shared the network of a host that already ran sshd, and the same thing happens with a second sshd instance or a Docker container publishing port 22. sshd’s unit restarts on failure, and after five failed starts within 10 seconds systemd stops retrying with start-limit-hit, so run sudo systemctl reset-failed sshd before you start it again.

“Permission denied (publickey)”

This means sshd accepts only keys and none of the keys you offered is in the server’s authorized_keys, which is exactly what our password-only test triggered on purpose. Connect with ssh -v and look for the Offering public key lines to see which keys the client tried. If the right key is offered and still refused, check the permissions on the server, because sshd ignores an authorized_keys file that other users can write to. chmod 700 ~/.ssh and chmod 600 ~/.ssh/authorized_keys fix that.

“Connection refused”

Nothing listens on that port. Either sshd isn’t running (systemctl is-active sshd), or it moved to another port and the client still uses 22, as in our port test above. ufw drops blocked packets instead of rejecting them, so a firewall problem shows up as a timeout, while “refused” points at sshd itself. Start with sudo ss -tlnp | grep sshd on the server, because it answers both questions in one line.

“Bad configuration option”

sshd -t names the file and line. We fed it a misspelled Prot 2222 and it stopped there:

/tmp/bad.conf: line 1: Bad configuration option: Prot
/tmp/bad.conf: terminating, 1 bad configuration options

Run sshd -t after every change, because the same typo in a real drop-in makes sshd refuse to start on its next restart, and Arch restarts sshd by itself after every openssh upgrade through a pacman hook. A bad file you forgot about can take the server off the network during a routine pacman -Syu.

For a server that’s reachable from the internet, add Fail2ban after key-only login to ban addresses that keep trying. If you’re still setting the machine up, the Arch Linux post-install checklist covers users, sudo and updates, and our Fedora SSH server guide shows the same setup on a firewalld system.

Keep reading

UFW Firewall Commands with Examples on Ubuntu 26.04 / 24.04 Security UFW Firewall Commands with Examples on Ubuntu 26.04 / 24.04 PHP2Shell (CVE-2026-109618, CVE-2026-109619): Check and Patch PHP Php PHP2Shell (CVE-2026-109618, CVE-2026-109619): Check and Patch PHP PHP2Shell on End-of-Life PHP 8.1, 8.0 and 7.4 (WHMCS, ionCube) Php PHP2Shell on End-of-Life PHP 8.1, 8.0 and 7.4 (WHMCS, ionCube) Best Pentesting Hardware for Kali Linux in 2026 Security Best Pentesting Hardware for Kali Linux in 2026 APT CVE-2026-109518: Check and Patch Debian and Ubuntu Debian APT CVE-2026-109518: Check and Patch Debian and Ubuntu Top 5 Tools for Scanning Linux Malware Security Top 5 Tools for Scanning Linux Malware

Leave a Comment

Press ESC to close