Connecting
Basic Connection
ssh user@host # connect to host ssh -p 2222 user@host # custom port ssh user@host command # run remote command ssh -t user@host "top" # force TTY allocation
Connection Flags
-p portConnect to specific port
-i keyUse specific identity (private key)
-tForce pseudo-terminal allocation
-v / -vv / -vvvVerbose debugging (increasing detail)
-qQuiet mode (suppress warnings)
-NNo remote command (for tunnels)
-fGo to background before command
-J jumpJump host (ProxyJump)
Key Management
Generate Keys
ssh-keygen -t ed25519 -C "you@example.com" ssh-keygen -t rsa -b 4096 -C "you@example.com" ssh-keygen -t ed25519 -f ~/.ssh/mykey ssh-keygen -p -f ~/.ssh/id_ed25519 # change passphrase
Deploy Public Key
ssh-copy-id user@host ssh-copy-id -i ~/.ssh/mykey.pub user@host # Manual: append .pub to remote authorized_keys cat ~/.ssh/id_ed25519.pub | ssh user@host \ "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
Key Files
~/.ssh/id_ed25519Private key (keep secret)
~/.ssh/id_ed25519.pubPublic key (share freely)
~/.ssh/authorized_keysRemote: accepted public keys
~/.ssh/known_hostsKnown host fingerprints
Config File
~/.ssh/config Basics
Host myserver HostName 192.168.1.100 User deploy Port 2222 IdentityFile ~/.ssh/deploy_key # Then connect with just: # ssh myserver
Useful Config Options
Host * ServerAliveInterval 60 ServerAliveCountMax 3 AddKeysToAgent yes IdentitiesOnly yes Host bastion HostName bastion.example.com User admin
Config Directives
HostAlias pattern for the entry
HostNameActual hostname or IP
UserLogin username
PortRemote port (default 22)
IdentityFilePath to private key
ProxyJumpJump through another host
ServerAliveIntervalKeep-alive interval (seconds)
IdentitiesOnlyOnly use specified keys
Port Forwarding
Local Forwarding (-L)
# Access remote port 5432 via local port 5432 ssh -L 5432:localhost:5432 user@host # Access remote-db:3306 through ssh host ssh -L 3306:remote-db:3306 user@host # Bind to all interfaces ssh -L 0.0.0.0:8080:localhost:80 user@host
Remote Forwarding (-R)
# Expose local port 3000 on remote port 8080 ssh -R 8080:localhost:3000 user@host # Allow remote connections from any interface ssh -R 0.0.0.0:8080:localhost:3000 user@host
Dynamic Forwarding (-D)
# SOCKS5 proxy on local port 1080 ssh -D 1080 user@host # Background SOCKS proxy ssh -D 1080 -fN user@host
SCP & SFTP
SCP (Secure Copy)
scp file.txt user@host:/remote/path/ scp user@host:/remote/file.txt ./local/ scp -r dir/ user@host:/remote/path/ scp -P 2222 file.txt user@host:/path/
SFTP (Interactive Transfer)
sftp user@host # Inside sftp session: # put local.txt — upload file # get remote.txt — download file # ls / lcd / cd — list / change directory
Transfer Flags
-rRecursive (copy directories)
-P portSpecify port (SCP uses -P, not -p)
-CEnable compression
-l limitBandwidth limit in Kbit/s
-i keyUse specific identity file
Agent Forwarding
SSH Agent
eval "$(ssh-agent -s)" # start agent ssh-add ~/.ssh/id_ed25519 # add key to agent ssh-add -l # list loaded keys ssh-add -D # remove all keys
Forwarding Agent
ssh -A user@host # forward agent # Or in ~/.ssh/config: # Host myserver # ForwardAgent yes
Agent Notes
Agent forwarding lets the remote host use your local keys without copying them. Use only with trusted hosts. Prefer ProxyJump over agent forwarding when possible.
Tunnels
Persistent Tunnel
# Background tunnel that stays open ssh -fNT -L 5432:localhost:5432 user@host # Auto-reconnecting tunnel (with autossh) autossh -M 0 -fNT -L 5432:localhost:5432 user@host
Jump Hosts / Bastion
ssh -J bastion user@internal-host ssh -J user1@hop1,user2@hop2 user@target # Config equivalent: # Host internal # HostName 10.0.0.5 # ProxyJump bastion
Tunnel Management
-fBackground after authentication
-NNo remote command
-TDisable pseudo-terminal
~.Kill stuck SSH session (escape)
~COpen command line for forwarding
~#List forwarded connections
Troubleshooting
Debugging Connection
ssh -vvv user@host # max verbosity ssh -G user@host # dump config (dry run) ssh-keyscan host # fetch host keys ssh-keygen -R host # remove from known_hosts
Common Issues
Permission deniedWrong key, user, or ~/.ssh perms (700/600)
Host key changedssh-keygen -R host, then reconnect
Connection timed outCheck firewall, port, and host reachability
Too many auth failuresUse -i to specify key or IdentitiesOnly
Broken pipeAdd ServerAliveInterval to config
File Permissions
chmod 700 ~/.ssh chmod 600 ~/.ssh/id_ed25519 # private key chmod 644 ~/.ssh/id_ed25519.pub # public key chmod 600 ~/.ssh/authorized_keys chmod 644 ~/.ssh/known_hosts
Security Best Practices
Server Hardening
PasswordAuthentication noDisable password login
PermitRootLogin noDisable root SSH access
AllowUsers deployWhitelist allowed users
Port 2222Non-default port (avoid scanners)
MaxAuthTries 3Limit auth attempts
Key Practices
Prefer Ed25519 keys (smaller, faster, more secure). Always set a passphrase on private keys. Use ssh-agent to avoid retyping passphrases. Rotate keys periodically; revoke unused keys. Use IdentitiesOnly to control which key is offered.
Multiplexing
Connection Sharing
# In ~/.ssh/config Host * ControlMaster auto ControlPath ~/.ssh/sockets/%r@%h-%p ControlPersist 600 # Create socket directory mkdir -p ~/.ssh/sockets
Multiplexing Benefits
Reuses a single TCP connection for multiple SSH sessions to the same host. Eliminates repeated handshakes — faster connects and lower overhead. ControlPersist keeps the master alive (seconds).
Escape Sequences
SSH Escape Commands
~.Terminate connection (kill stuck session)
~^ZSuspend SSH session
~COpen command line (add forwarding)
~#List forwarded connections
~&Background SSH (waiting for connections)
~?Show escape help
~~Send literal tilde