Set up SSH server

Install the `openssh-server` package using `dnf`, enable the `sshd` service to start on boot, and ensure port 22 is allowed through the firewall.

You need remote access, but the connection is refused

You just finished setting up a Fedora server and need to manage it from your laptop. You type ssh user@hostname and the terminal returns ssh: connect to host hostname port 22: Connection refused. The machine is on, the network is fine, but the remote door won't open. This is the default state on Fedora. The distribution ships with the SSH client so you can connect to other systems, but it does not install the SSH server. You need to install the daemon, open the firewall, and verify the service is listening.

What's actually happening

SSH operates on three distinct layers. The openssh-server package provides the sshd daemon that listens for incoming connections and handles authentication. The firewalld service inspects network packets and drops traffic unless you explicitly allow the SSH service. SELinux monitors the sshd process and the port it binds to, ensuring the service only interacts with authorized resources.

The sshd daemon runs as a super-server. It listens on a socket and forks a new process for each connection. If the configuration file contains a syntax error, the daemon refuses to start, and the fork never happens. The firewall sits between the network interface and the socket. If the firewall blocks port 22, the packet never reaches the daemon. SELinux sits inside the kernel and checks the context of the port and the process. If the context is wrong, the kernel denies the bind or the connection.

The error message tells you which layer stopped the request. Connection refused means the port is closed or the service is down. Connection timed out means the firewall dropped the packet. Permission denied means the service accepted the connection but rejected your credentials.

Run systemctl status sshd immediately after installation. If the service fails to start, the logs will show the error before you waste time checking the firewall.

Install the server and open the firewall

Install the package and enable the service. Fedora uses systemd to manage services. The enable flag creates the symlinks required for boot startup. The --now flag starts the service immediately.

sudo dnf install openssh-server -y
# WHY: Installs the sshd binary, configuration files, and PAM modules.
sudo systemctl enable --now sshd
# WHY: Creates symlinks for boot startup and starts the service immediately.

The firewall blocks incoming SSH connections by default. Fedora uses firewalld with zones. The public zone is the default for most interfaces and allows only essential services. You must add SSH to the allowed services.

sudo firewall-cmd --permanent --add-service=ssh
# WHY: Adds the SSH service definition to the persistent firewall configuration.
sudo firewall-cmd --reload
# WHY: Reloads the firewall to apply persistent rules to the active runtime state.

firewall-cmd --reload is the standard practice after every rule change. The runtime configuration and persistent configuration diverge if you skip this step. Changes made with --permanent do not affect the active firewall until you reload.

Check the active zone with firewall-cmd --get-active-zones. Rules apply to zones, not interfaces. If your interface is in the wrong zone, the rule never matches.

Verify the service is listening

Confirm the daemon is running and the kernel is listening on the correct port. Use systemctl status to check the service state and recent logs. Use ss to inspect the socket table.

systemctl status sshd
# WHY: Displays the service state, main PID, and recent journal log lines.
ss -tlnp | grep :22
# WHY: Shows kernel-level listening sockets and confirms sshd owns port 22.

systemctl status shows recent log lines and state in one view. Always check status before restarting a service. If the output shows active (running), the daemon is ready. The ss output should show sshd listening on 0.0.0.0:22 or *:22.

Verify the listener before you close your local session. A silent failure in sshd_config leaves you with no way to debug remotely.

Harden the configuration with keys

Password authentication works, but key-based authentication is more secure and convenient. Keys eliminate brute-force attacks and allow you to manage access without changing passwords. Generate a key pair on your client machine and copy the public key to the server.

ssh-keygen -t ed25519 -C "your_email@example.com"
# WHY: Generates a modern Ed25519 key pair with a comment for identification.
ssh-copy-id user@hostname
# WHY: Appends your public key to the remote authorized_keys file with correct permissions.

The ssh-copy-id command handles the directory creation and permission setting. SSH is strict about permissions. If ~/.ssh or ~/.ssh/authorized_keys are world-writable, sshd ignores the keys and falls back to passwords.

Edit the server configuration to enforce keys and disable root login. The configuration file lives in /etc/ssh/sshd_config.

Config files in /etc/ are user-modified. Files in /usr/lib/ ship with the package. Edit /etc/. Never edit /usr/lib/. Changes in /usr/lib/ get overwritten on package updates.

# /etc/ssh/sshd_config
PermitRootLogin no
# WHY: Prevents direct root login, forcing users to authenticate as a regular user first.
PasswordAuthentication no
# WHY: Disables password logins, requiring key-based authentication for all users.

Restart the service to apply the changes.

sudo systemctl restart sshd
# WHY: Reloads the configuration file and restarts the daemon.

Test the connection in a second terminal before closing the first. A syntax error in sshd_config kills the service and locks you out. If the second terminal connects successfully, you can close the first session.

Handle SELinux and non-standard ports

Fedora's SELinux policy allows SSH on port 22 by default. The port is labeled with the ssh_port_t type, and the sshd process has the correct context. You do not need to change SELinux booleans for a standard setup.

If you change the SSH port, you must update the firewall and SELinux. SELinux will block sshd from binding to an unlabeled port. Use semanage to add the port label.

sudo semanage port -a -t ssh_port_t -p tcp 2222
# WHY: Adds the custom port to the SELinux policy store with the correct type.
sudo firewall-cmd --permanent --add-port=2222/tcp
# WHY: Opens the custom port in the firewall.
sudo firewall-cmd --reload
# WHY: Applies the firewall change to the runtime.

SELinux denials appear in journalctl -t setroubleshoot with a one-line summary. Read those before disabling SELinux. Disabling SELinux removes a layer of protection and is rarely the correct fix.

Run semanage port -l | grep ssh to verify the label. If the port is missing, sshd will fail to start or connections will be dropped silently by the kernel.

Common pitfalls and error messages

The error message points to the root cause. Match the symptom to the fix.

  • ssh: connect to host hostname port 22: Connection refused: The service is not running or listening on the wrong port. Check systemctl status sshd and ss -tlnp.
  • ssh: connect to host hostname port 22: Connection timed out: The firewall is blocking the connection. Check firewall-cmd --list-all and verify the zone.
  • Permission denied (publickey,password): Authentication failed. Check ~/.ssh/authorized_keys permissions and the sshd_config settings.
  • Received disconnect from hostname port 22: 2: Too many authentication failures: The client is trying too many keys. Use ssh -v to see which key is failing and remove unused keys from the agent.

Read the actual error before guessing. Connection refused means the port is closed. Connection timed out means the packet never arrived.

When to use this vs alternatives

Use port 22 when you want zero-configuration SELinux and standard firewall rules. Use a custom port when you want to reduce noise from automated scanners and accept the overhead of managing SELinux port labels. Use key-only authentication when you want to eliminate brute-force password attacks and manage access via authorized_keys. Use password authentication only when you cannot deploy keys and you have a strong password policy in place.

Read the error log before restarting. journalctl -xeu sshd shows the exact reason the service failed.

Where to go next