ssh: secure remote login

Updated November 2026

ssh (Secure Shell) connects you to another machine over an encrypted channel. You can log in, run single commands, copy files and tunnel other connections through it. OpenSSH is installed on virtually every Unix and Linux system, and on Windows 10 and 11.

ssh [options] [user@]host [command]

Log in and run commands

ssh anna@server.example.com            # log in
ssh -p 2222 anna@server.example.com    # SSH on another port
ssh server uptime                      # run one command and return
ssh server 'df -h; free -h'            # several commands (quote them)
ssh -t server sudo systemctl restart nginx   # -t: give the remote command a terminal (needed for sudo prompts)

The first time you connect, ssh shows the server's key fingerprint and asks you to confirm it. Compare it with the fingerprint from the server's administrator (ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub on the server) before typing yes.

Log in with keys instead of passwords

ssh-keygen -t ed25519 -C "anna@laptop"   # create a key pair; choose a passphrase
ssh-copy-id anna@server.example.com      # install the public key on the server
ssh anna@server.example.com              # now logs in with the key

The private key (~/.ssh/id_ed25519) never leaves your computer; keep it at mode 600 and protect it with a passphrase. The public key (.pub) is appended to ~/.ssh/authorized_keys on the server. Once keys work, an administrator can turn off password logins entirely, as described in the security guide.

eval "$(ssh-agent)"        # start an agent (most desktops already run one)
ssh-add                    # type the passphrase once per session
ssh-add -l                 # keys loaded in the agent

The client config file

Save typing with ~/.ssh/config:

Host web1
    HostName 203.0.113.10
    User deploy
    Port 2222
    IdentityFile ~/.ssh/id_ed25519

Host db1
    HostName 10.0.0.21
    User anna
    ProxyJump web1             # reach db1 through web1

Host *
    ServerAliveInterval 60     # keep idle connections open through firewalls

Now ssh web1 and ssh db1 just work, as do scp, rsync and git with those names.

Copy files

scp report.pdf web1:/tmp/                 # copy to a server
scp web1:/var/log/nginx/error.log .       # copy from a server
scp -r site/ web1:/var/www/               # a directory
sftp web1                                 # interactive file transfer (get, put, ls, cd)

For large or repeated copies use rsync, which only sends changes.

Tunnels and port forwarding

ssh -L 5433:localhost:5432 db1      # local port 5433 reaches PostgreSQL on db1
ssh -L 8080:intranet.lan:80 web1    # reach an internal website through web1
ssh -R 9000:localhost:3000 web1     # let web1 reach port 3000 on your machine
ssh -D 1080 web1                    # SOCKS proxy through web1
ssh -N -f -L 5433:localhost:5432 db1   # tunnel only, in the background

Database and admin ports can then stay closed to the internet: you reach them through SSH.

Common errors

Permission denied (publickey)
The server does not accept your key. Check that the right key is offered (ssh -v), that ~/.ssh is mode 700 and authorized_keys mode 600 on the server and owned by the user, and the server log: journalctl -u ssh (-u sshd on RHEL).
REMOTE HOST IDENTIFICATION HAS CHANGED
The server's host key differs from the one you saved. That happens after a reinstall, but it can also mean someone is intercepting the connection. Verify with the administrator, then remove the old key: ssh-keygen -R hostname.
Connection refused / timed out
Refused: nothing listens on that port (is sshd running?). Timed out: a firewall drops the traffic, or the address is wrong.
Connection closes after a few minutes idle
A firewall or NAT drops idle connections. Set ServerAliveInterval 60 as above. For long-running work, use tmux so nothing is lost when the connection drops.

ssh -v (or -vvv) shows each step of the connection and is the best debugging tool.