ssh: secure remote login
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.
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~/.sshis mode 700 andauthorized_keysmode 600 on the server and owned by the user, and the server log:journalctl -u ssh(-u sshdon 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
sshdrunning?). 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 60as above. For long-running work, usetmuxso nothing is lost when the connection drops.
ssh -v (or -vvv) shows each step of the connection and is the best debugging tool.