$linuxjunkies
>

SSH: The Complete Guide

Master SSH from first login to port forwarding: key-based auth, client config files, the SSH agent, and local/remote tunnels on any Linux distribution.

BeginnerUbuntuDebianFedoraArch10 min readUpdated June 7, 2026

Before you start

  • A Linux machine to act as SSH server with a user account
  • Network connectivity between client and server
  • sudo or root access on the server to install packages and edit sshd_config

SSH (Secure Shell) is the backbone of remote Linux administration. Whether you're logging into a VPS, tunneling traffic, or automating deployments, understanding SSH deeply pays dividends every day. This guide covers everything from a first connection to agent forwarding and local port tunnels—using OpenSSH, which ships on every major Linux distribution and macOS.

How SSH Works in One Paragraph

SSH opens an encrypted TCP connection (default port 22) between a client and a server. Authentication happens in two stages: first the server proves its identity to the client via a host key, then the client authenticates to the server via a password or a cryptographic key pair. All subsequent traffic—your shell session, file transfers, tunneled ports—flows through that encrypted channel.

Installing OpenSSH

Most desktop and server installs already include the OpenSSH client. The server daemon (sshd) needs a separate package and must be enabled explicitly.

Debian / Ubuntu

sudo apt update
sudo apt install openssh-client openssh-server

Fedora / RHEL / Rocky

sudo dnf install openssh-clients openssh-server

Arch Linux

sudo pacman -S openssh

Enable and start the daemon with systemd on any of them:

sudo systemctl enable --now sshd

Making Your First Connection

The basic syntax is ssh user@host. The host can be an IP address, a hostname, or a name defined in your SSH config file (covered later).

ssh [email protected]

On first connect you'll see a host-key fingerprint prompt:

The authenticity of host '192.168.1.50' can't be established.
ED25519 key fingerprint is SHA256:abc123...
Are you sure you want to continue connecting (yes/no/[fingerprint])?

Type yes to accept and store the fingerprint in ~/.ssh/known_hosts. Future connections compare against this stored value—if it changes unexpectedly, SSH will refuse to connect and warn you of a potential man-in-the-middle attack. That warning is serious; investigate before overriding.

Useful flags for the ssh command:

  • -p 2222 — connect to a non-default port
  • -i ~/.ssh/mykey — specify a private key file
  • -v / -vv / -vvv — verbose output, invaluable for debugging
  • -X — forward X11 (use -Y for trusted forwarding; note Wayland sessions don't benefit from X forwarding unless XWayland is running)

Key-Based Authentication

Passwords are convenient but weak. Key-based authentication is faster, more secure, and required by many servers. You generate a key pair: the private key stays on your machine, the public key goes on the server.

Generate a Key Pair

Ed25519 is the current best-practice algorithm—fast, small keys, strong security. RSA 4096 is an acceptable alternative for compatibility with older systems.

ssh-keygen -t ed25519 -C "alice@laptop"

Accept the default path (~/.ssh/id_ed25519) or specify one. Always set a passphrase—the SSH agent (covered below) means you only type it once per session.

Copy the Public Key to the Server

ssh-copy-id is the easiest method. It appends your public key to ~/.ssh/authorized_keys on the remote host and sets correct permissions.

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

If ssh-copy-id isn't available, do it manually:

cat ~/.ssh/id_ed25519.pub | ssh [email protected] \
  'mkdir -p ~/.ssh && chmod 700 ~/.ssh && \
   cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'

Harden the Server: Disable Password Auth

Once key auth works, disable password login on the server. Edit /etc/ssh/sshd_config:

sudo nano /etc/ssh/sshd_config

Set or confirm these lines:

PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin no

Then reload the daemon. Do not close your existing SSH session until you've confirmed key auth works in a second terminal.

sudo systemctl reload sshd

The SSH Client Config File

Typing long hostnames and options on every connection is tedious. ~/.ssh/config lets you define named aliases with all their options. Create or edit it (permissions must be 600):

touch ~/.ssh/config && chmod 600 ~/.ssh/config

A practical example with multiple hosts:

Host myvps
    HostName 203.0.113.42
    User alice
    Port 2222
    IdentityFile ~/.ssh/id_ed25519

Host homeserver
    HostName 192.168.1.50
    User alice
    IdentityFile ~/.ssh/id_ed25519

Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3
    AddKeysToAgent yes

The Host * block applies to all connections. ServerAliveInterval keeps idle connections from dropping. Now you connect with just:

ssh myvps

The SSH Agent

The SSH agent holds your decrypted private keys in memory for the duration of your login session. You enter the passphrase once; every subsequent ssh invocation picks the key from the agent automatically.

Start the Agent and Add a Key

On most desktop environments (GNOME, KDE) a graphical agent starts automatically. On a headless server or a minimal session, start it manually:

eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

To see what keys are currently loaded:

ssh-add -l

With AddKeysToAgent yes in your config file, the key is added automatically on first use—no manual ssh-add needed.

Agent Forwarding

Agent forwarding lets a remote server use your local agent to authenticate to a third server—useful for git operations or hopping through a bastion host without storing private keys on intermediate machines.

ssh -A [email protected]

Or in your config:

Host bastion
    HostName bastion.example.com
    User alice
    ForwardAgent yes

Only enable agent forwarding to hosts you fully trust. A compromised remote root can hijack your agent socket and authenticate as you to other servers.

Port Forwarding (SSH Tunnels)

SSH can forward TCP ports through the encrypted connection. This is useful for accessing remote services that aren't exposed publicly, or for bypassing restrictive firewalls.

Local Port Forwarding

Bind a port on your local machine that forwards traffic to a destination reachable from the SSH server. Classic use case: access a remote database that only listens on localhost.

ssh -L 5432:localhost:5432 alice@myvps

Now connecting to localhost:5432 on your machine reaches the PostgreSQL instance on myvps. The syntax is -L local_port:destination_host:destination_port. The destination host is resolved from the SSH server's perspective, so you can also reach hosts on the remote LAN:

ssh -L 8080:internal-webserver:80 alice@myvps

Remote Port Forwarding

The reverse: a port on the remote server forwards back to a host reachable from your local machine. Handy for temporarily exposing a local dev server.

ssh -R 9000:localhost:3000 alice@myvps

Traffic hitting myvps:9000 now reaches localhost:3000 on your machine. The server must have GatewayPorts yes in sshd_config for external clients (not just localhost) to use the remote port.

Dynamic (SOCKS) Forwarding

Opens a local SOCKS5 proxy that tunnels all traffic through the SSH connection—a lightweight ad-hoc VPN.

ssh -D 1080 -N alice@myvps

The -N flag tells SSH not to open a shell—just hold the tunnel open. Point your browser or application at SOCKS5 proxy localhost:1080.

Verification

After setting up key auth and your config, run through these checks:

# Confirm key auth works
ssh -v myvps 2>&1 | grep -E "Authentications|publickey"

# Check sshd is listening on the expected port
sudo ss -tlnp | grep sshd

# Review auth log for recent logins (Debian/Ubuntu)
sudo journalctl -u ssh --since "1 hour ago"

# Fedora/RHEL/Arch use sshd as the unit name
sudo journalctl -u sshd --since "1 hour ago"

Troubleshooting

  • Permission denied (publickey) — Wrong permissions are the most common cause. The remote ~/.ssh directory must be 700, authorized_keys must be 600, and the home directory must not be world-writable. Run ssh -vvv for detail.
  • Connection refusedsshd isn't running (systemctl status sshd), or a firewall is blocking port 22. On Fedora/RHEL: sudo firewall-cmd --add-service=ssh --permanent && sudo firewall-cmd --reload. On Ubuntu with ufw: sudo ufw allow ssh.
  • Host key changed warning — If you rebuilt the server and the fingerprint legitimately changed, remove the old entry: ssh-keygen -R 192.168.1.50. Never ignore this warning on production systems without verification.
  • Tunnel drops after idle period — Add ServerAliveInterval 60 and ServerAliveCountMax 3 to your ~/.ssh/config Host * block, or use -o ServerAliveInterval=60 on the command line.
  • Agent forwarding not working — Confirm ssh-add -l shows your key locally, and that the remote sshd_config has AllowAgentForwarding yes (it's the default, but some hardened configs disable it).
tested on:Ubuntu 24.04Fedora 40Debian 12Arch rolling

Frequently asked questions

What's the difference between Ed25519 and RSA keys, and which should I use?
Ed25519 is an elliptic-curve algorithm that produces smaller keys, connects faster, and is considered more secure than RSA at equivalent strength. Use Ed25519 for all new keys. Use RSA 4096 only if you must connect to very old servers (OpenSSH older than 6.5, released 2014) that don't support Ed25519.
Is it safe to enable agent forwarding?
Only to hosts you fully trust. While agent forwarding doesn't expose your private key, a remote user with root access can use your agent socket to authenticate to other servers as you for as long as the connection is open. For bastion-host patterns, consider ProxyJump (-J) as a safer alternative.
How do I jump through a bastion host to reach an internal server?
Use the ProxyJump option: ssh -J alice@bastion alice@internal-server. In your config file, add ProxyJump bastion under the target host block. This is safer than agent forwarding because SSH handles the hop directly without exposing your agent on the bastion.
Why does SSH disconnect after my session sits idle?
Network firewalls and NAT devices often kill idle TCP connections. Fix it client-side by adding ServerAliveInterval 60 and ServerAliveCountMax 3 to the Host * block in ~/.ssh/config. This sends a keepalive packet every 60 seconds and drops the connection after 3 missed responses.
Can I use SSH keys with sudo or other privilege escalation on the remote server?
SSH keys authenticate you to the SSH daemon only—they play no role in sudo, su, or local privilege escalation, which rely on PAM and local credentials. You still need your sudo password on the remote system unless you've configured passwordless sudo (NOPASSWD in sudoers), which carries its own security tradeoffs.

Related guides