How to Fix "Connection Refused" or "Permission Denied" SSH Errors on Fedora

Fix Fedora SSH connection refused or permission denied errors by starting the sshd service and correcting key file permissions.

You type ssh user@remote-host and the terminal immediately spits back Connection refused. Or maybe you get Permission denied (publickey). You know the server is up. You know your key exists. The terminal just won't let you in. This usually means the SSH daemon isn't listening, the firewall is blocking port 22, or your key permissions are too loose for OpenSSH to trust them.

What's actually happening

SSH splits the problem into two distinct layers. The network layer handles routing and port access. The authentication layer handles keys and passwords. When you see Connection refused, the network layer failed. The remote machine actively told your client to go away, or the firewall dropped the packet before it reached the daemon. When you see Permission denied, the network layer succeeded. The daemon received your request, checked your credentials, and rejected them.

OpenSSH enforces strict file permissions for a reason. If your private key is readable by other users, the daemon assumes it could have been compromised and refuses to use it. Think of the private key like a physical house key. You would not leave it on a park bench where anyone could copy it. OpenSSH treats world-readable keys the same way. Fedora ships with sshd disabled on Workstation images to reduce attack surface. The service must be enabled explicitly. The firewall defaults to blocking incoming connections on non-standard ports. SELinux watches file context changes in real time. All three layers must align before the tunnel opens.

Check the service state before guessing. A stopped daemon explains every connection refusal.

The fix

First, confirm whether the SSH daemon is actually running on the target machine. You need to enable and start it explicitly so it survives reboots.

# Check the current state of the SSH daemon
systemctl status sshd
# Enable the service so it starts automatically on boot
sudo systemctl enable sshd
# Start the daemon immediately without rebooting
sudo systemctl start sshd

The systemctl status command shows recent log lines and the active state in one view. Always check status before restarting. A failed unit will just fail again if you do not read the journal first.

Next, fix key permissions. OpenSSH refuses to use private keys with overly permissive modes. The .ssh directory and the key file itself must be locked down to your user account.

# Restrict the private key to owner read/write only
chmod 600 ~/.ssh/id_rsa
# Ensure the .ssh directory itself is not group or world accessible
chmod 700 ~/.ssh
# Verify the ownership matches your current user
chown $USER:$USER ~/.ssh ~/.ssh/id_rsa

Config files in /etc/ are user-modified. Files in /usr/lib/ ship with the package. SSH configuration lives in /etc/ssh/sshd_config. Never edit files under /usr/lib/ssh/. Manual edits drift during package updates.

Now, handle the SSH agent. The agent holds your decrypted keys in memory so you do not type passphrases repeatedly. It runs as a background process and communicates with the client through a Unix socket.

# Start a new agent process and export its environment variables
eval $(ssh-agent)
# Add your private key to the running agent session
ssh-add ~/.ssh/id_rsa
# List loaded keys to confirm the agent accepted it
ssh-add -l

The journalctl -xe command reads better than journalctl alone. The x flag adds explanatory text and the e flag jumps to the end. Most sysadmins type journalctl -xeu sshd muscle-memory style when debugging daemon failures.

Run sudo firewall-cmd --reload after every rule change. Otherwise the runtime config and the persistent config diverge.

Verify it worked

Run a verbose connection attempt to see exactly where the handshake succeeds or fails. Verbose output traces the TCP connection, the key exchange, and the authentication steps.

# Enable verbose output to trace the connection lifecycle
ssh -v user@remote-host
# If verbose output is too noisy, use double v for protocol-level details
ssh -vv user@remote-host

Look for Authentication succeeded or debug1: Next authentication method: publickey. If you see those lines, the tunnel is open. Close the test session and run your normal command. Reboot before you debug. Half the time the symptom is gone after a clean service restart.

Common pitfalls and what the error looks like

The ssh client will refuse to proceed and print Permissions 0644 for '/home/user/.ssh/id_rsa' are too open. The client enforces this locally before sending anything to the server. Fix the permissions first.

If you see ssh: connect to host remote-host port 22: Connection refused, the remote machine has no process listening on port 22. This usually means sshd is stopped, or it is bound to a different port. Check /etc/ssh/sshd_config for the Port directive.

When the firewall blocks the connection, you typically get a timeout instead of an immediate refusal. Fedora uses firewalld by default. The runtime configuration and persistent configuration must match. Run sudo firewall-cmd --reload after every rule change. Otherwise the firewall drops packets silently.

SELinux denials show up in journalctl -t setroubleshoot with a one-line summary. Read those before disabling SELinux. A common denial occurs when you move keys into a non-standard directory without the correct SELinux context. Restore the context with restorecon -Rv ~/.ssh.

Fedora's release cadence is six months. The N-2 release goes EOL when N+1 ships. Plan upgrades on that cycle. An outdated openssh package on the server side can reject modern key algorithms from a newer client. Run sudo dnf upgrade --refresh on the target machine to pull in security patches and algorithm updates. The --refresh flag forces metadata reloads and prevents stale cache conflicts.

Trust the package manager. Manual file edits drift, snapshots stay.

When to use this vs alternatives

Use ssh -v when you need to trace where the connection fails without flooding the terminal. Use ssh -vv when you need to inspect the exact cryptographic exchange and key negotiation. Use journalctl -xeu sshd when the daemon fails to start or drops connections immediately after boot. Use firewall-cmd --list-all when packets are timing out instead of refusing. Use chmod 600 and chmod 700 when OpenSSH rejects your keys due to overly permissive file modes. Stay on the default port 22 if you are behind a corporate firewall that only allows standard SSH traffic. Use ssh-copy-id when you need to deploy a public key to a remote machine that still accepts password authentication. Use systemctl restart sshd when you change /etc/ssh/sshd_config and need the daemon to pick up the new rules.

Run journalctl first. Read the actual error before guessing.

Where to go next