SSH is an encrypted remote-access protocol: a shell on the server, file
copying and traffic tunneling through a single port. The scenario of this
article: the server web1.example.com, user admin.
Installing the server#
sudo apt update
sudo apt install openssh-server
The client (openssh-client) is already installed on Ubuntu; on machines
without a server role — client only.
Server configuration#
The configuration lives in /etc/ssh/sshd_config. A basic safe set:
Port 22
# Root login over SSH is forbidden
PermitRootLogin no
# Key-based authentication
PubkeyAuthentication yes
# Keys live here
AuthorizedKeysFile .ssh/authorized_keys
# Password login — turn off once the keys are debugged
PasswordAuthentication no
# Verbose logging of connection attempts
LogLevel VERBOSE
Syntax check and restart:
sudo sshd -t
sudo systemctl restart ssh
sshd -tchecks the config before the restart — that way you won't cut yourself off because of a typo. On Ubuntu/Debian the service is calledssh; in the RHEL family —sshd.
Enable
PasswordAuthentication noonly after the keys already work: otherwise you'll be locked out of server login (the last resort — the hoster's console/VNC).
Key generation#
The modern type — Ed25519 (short keys, fast verification):
ssh-keygen -t ed25519 -C "admin@laptop"
The keys appear in ~/.ssh/: the private id_ed25519 (never leaves the
machine) and the public id_ed25519.pub (this is what you scatter over the
servers).
RSA (
ssh-keygen -t rsa -b 4096) — for very old servers where Ed25519 is not yet implemented.
ssh checks the permissions on ~/.ssh and the private key strictly:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
Copying the key to the server#
ssh-copy-id admin@web1.example.com
# A non-standard server port
ssh-copy-id -p 2222 admin@web1.example.com
The public key will be appended to the server's authorized_keys — password
login will no longer be needed.
Connecting#
ssh admin@web1.example.com
# A non-standard port
ssh -p 2222 admin@web1.example.com
# A one-off command, without an interactive shell
ssh admin@web1.example.com "df -h /"
# Debugging connection problems (-vv — more verbose)
ssh -v admin@web1.example.com
The ~/.ssh/config file#
Host aliases: set it up once, then short ssh web1 calls without
names/ports/keys:
# ~/.ssh/config
Host web1
HostName web1.example.com
User admin
IdentityFile ~/.ssh/id_ed25519
Host db1
HostName db1.example.com
User admin
Port 2222
IdentityFile ~/.ssh/id_ed25519
ssh web1
scp backup.sh web1:/opt/
All options from config apply to scp and ssh-copy-id too — a host with
an alias behaves like a regular name. The file must be readable only by you:
chmod 600 ~/.ssh/config.
For logging in through an intermediate server (bastion) — the
ProxyJumpoption:ssh -J bastion web1orProxyJump bastionin theHost web1block.
Copying files: scp#
# A file to the server
scp deploy.sh admin@web1.example.com:/tmp/
# A file from the server to here
scp admin@web1.example.com:/var/log/app.log .
# A whole directory (recursively)
scp -r ./assets admin@web1.example.com:/var/www/
# The port uses a capital -P (in ssh it's a lowercase -p!)
scp -P 2222 file.txt admin@web1.example.com:~
# Preserve file times and permissions (-p)
scp -p admin@web1.example.com:/etc/nginx/nginx.conf .
Starting with OpenSSH 9.0 the default protocol is SFTP; for very old servers add
-O(the old SCP protocol mode). For large directories and repeated syncsrsync -avz …is more convenient — it resumes and re-transfers only what changed.
The SSH agent#
The agent keeps decrypted keys in memory — the key passphrase is asked once per session:
eval $(ssh-agent)
ssh-add ~/.ssh/id_ed25519
On desktops (macOS, GNOME) the agent is usually already running — just
ssh-addis enough. On macOS you can keep the key in the Keychain:ssh-add --apple-use-keychain.
Tunnels#
# Local forwarding: localhost:5432 → the DB on the server side
ssh -L 5432:localhost:5432 admin@db1.example.com
# Reverse: a server port leads to a service on your local machine
# (show a colleague your local development from the server)
ssh -R 8080:localhost:3000 admin@web1.example.com
# A SOCKS proxy on localhost:1080 — browser traffic through the server
ssh -D 1080 admin@web1.example.com
A tunnel lives as long as the ssh connection lives; run it with
-N(no shell) and-Tif you only need the tunnel:ssh -N -L 5432:localhost:5432 admin@db1.example.com.