SSH Cheatsheet - SSH Connection & Configuration Command Reference

For developers and ops who want passwordless login, hop through bastion hosts into the internal network, or map a remote port to localhost for debugging. The value of SSH lies in reusing keys and config: pin frequently used Hosts, jump hosts, and port forwards into ~/.ssh/config instead of retyping long argument sets each time. By the end you can generate and correctly permission a key pair for passwordless login, hop through bastions with ProxyJump, and expose a remote service on localhost with -L for debugging.

SysOps·21 commands·Last updated 2026-07-21
sshSecretsTunnelslinux

Typical Use Case

Remote login and file transfer, passwordless key-based login, port forwarding (tunnels), and troubleshooting connection timeouts, permission-too-open rejections, and host key change alerts.

Connection & Auth 5

ssh user@host
Login on default port 22, omits user for current username
ssh -p 2222 user@host
Specify port when server uses non-default port
ssh -i ~/.ssh/id_ed25519 user@host
Specify private key, common with multiple keys
ssh -vvv user@host
Verbose debug output for troubleshooting auth failures
ssh -o ConnectTimeout=5 user@host
Set connection timeout to prevent hanging

Key Management 5

ssh-keygen -t ed25519 -C "tom@example.com"
Generate ed25519 key, shorter and faster than RSA
ssh-keygen -t rsa -b 4096 -C "tom@example.com"
Generate RSA 4096-bit key, compatible with older servers
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host
Copy public key to server for passwordless login
ssh-keygen -lf ~/.ssh/id_ed25519.pub
View key fingerprint and length, verify server host key
eval $(ssh-agent) && ssh-add ~/.ssh/id_ed25519
Start agent and load private key, avoid repeated passwords

Config File ~/.ssh/config 6

Host prod
Define host alias, then ssh prod connects directly
HostName 1.2.3.4
Actual IP or domain to connect to
User deploy
Default login user, saves typing user@host each time
IdentityFile ~/.ssh/id_ed25519
Specify private key for this host
ProxyJump bastion
Connect through a jump host, replaces ssh -t ssh -t multi-hop
LocalForward 8080 127.0.0.1:80
Auto-setup local port forwarding on login

Port Forwarding & Proxy 5

ssh -L 8080:127.0.0.1:80 user@host
Local port forwarding, access local:8080 = remote:80
ssh -R 9090:127.0.0.1:80 user@host
Remote port forwarding, expose local service remotely
ssh -D 1080 user@host
Dynamic SOCKS5 proxy, all traffic goes through remote
ssh -N -L 8080:127.0.0.1:80 user@host
Forward only, no command execution, for background tunnels
ssh -J bastion user@internal
Direct connection through jump host, equivalent to ProxyJump

Parameter matrix

ParameterEffectExample
-iPrivate key file to authenticate with.ssh -i ~/.ssh/id_ed25519 user@host
-pPort to connect to; the default is 22.ssh -p 2222 user@host
-LLocal port forwarding tunnel.ssh -L 8080:127.0.0.1:80 user@host
-RRemote port forwarding tunnel.ssh -R 9000:localhost:3000 user@host
-NOpen the tunnel only, with no remote shell attached.ssh -N -L 5432:localhost:5432 db@host
-fRun the connection in the background.ssh -fN -L 8080:localhost:80 user@host
scp -rCopy a directory tree recursively.scp -r ./app user@host:/srv/app
StrictHostKeyCheckingChoose how strictly host fingerprints are verified.ssh -o StrictHostKeyChecking=no user@host
ProxyJumpReach the target through a jump host.ssh -J jump@bastion user@host
IdentitiesOnlyOffer only the named key, so a pile of keys cannot get you rejected.ssh -o IdentitiesOnly=yes -i key user@host

Common pitfalls

SymptomKey-based login fails with Permission denied (publickey).

CauseThe private key is readable by other users (mode 644), authorized_keys has the wrong ownership, or PubkeyAuthentication is off on the server.

Fixchmod 600 the ~/.ssh/id_* keys and 700 the .ssh directory; on the server confirm PubkeyAuthentication yes and the AuthorizedKeysFile path.

SymptomThe connection hangs for a long time and then times out.

CauseThe network is unreachable, a firewall drops port 22, or UseDNS makes the reverse lookup stall.

FixConfirm reachability with ping or telnet host 22, and set UseDNS no plus GSSAPIAuthentication no on the server to speed up the handshake.

SymptomWARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!

CauseThe host was rebuilt or moved to a new address, so its fingerprint no longer matches known_hosts — which is also exactly what a man in the middle looks like.

FixOnce you have confirmed the change is legitimate, drop the stale record with ssh-keygen -R host and reconnect. If the change is unexpected, verify the host before going any further.

Symptomscp or ssh is refused because the client offers too many keys.

Causessh tries each key in turn until the server hits MaxAuthTries and drops the connection.

FixPass -o IdentitiesOnly=yes with a single -i, or bind an IdentityFile per host in ~/.ssh/config.

SymptomThe tunnel is established but the forwarded port still refuses connections.

CauseThe forward target is not reachable as localhost on the far side, or BindAddress is restricted.

FixRemember that the remote half of -L local:remoteHost:remotePort is resolved on the server itself, and check the GatewayPorts setting.

SymptomDirect root login is refused.

CausePermitRootLogin is set to no, which is the recommended default.

FixLog in as an ordinary user and escalate with sudo. If root really is required, use PermitRootLogin prohibit-password with key authentication only.

Troubleshooting

  1. 1Trace the connection attempt in detail

    ssh -v user@host

    The verbose output tells you whether the attempt dies at authentication, stalls in the handshake, or is refused outright, which is the stage to look at.

  2. 2Check that the port is actually reachable

    nc -zv host 22

    Separates a blocked network path from a service that is simply not listening.

  3. 3Drop a stale host key and reconnect

    ssh-keygen -R host

    Run this only after confirming that the change of host is a legitimate one.

  4. 4Verify that passwordless login works

    ssh -o BatchMode=yes user@host echo ok

    BatchMode never prompts for a password, so a clean run proves key authentication is genuinely in place.

Command Examples

Set up passwordless login with an ed25519 key in three steps

ssh-keygen -t ed25519 -C "tom@example.com" ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host ssh user@host

Keys are generated locally and only the public half is pushed to the server; the private key never leaves your machine. When passwordless login fails, retrace this chain to find which step was skipped.

Output

Generating public/private ed25519 key pair.
Your identification has been saved in /home/tom/.ssh/id_ed25519
Number of key(s) added: 1.

Verify the host key fingerprint before trusting a new server

ssh-keygen -lf ~/.ssh/id_ed25519.pub

The first field is the key length and type, the second is the SHA256 fingerprint, and the comment follows it. Compare that fingerprint character by character with the value the server admin gives you, and do not log in if it differs.

Output

256 SHA256:8kQ1OQ0pZ3mLpXv2Rb7nT4cYdE6sA1fH5gJ9uI0oLk tom@example.com (ED25519)

Turn on verbose debugging when authentication fails

ssh -vvv -o ConnectTimeout=5 user@host

-vvv prints the negotiated key algorithms, which of your public keys the server accepted, and exactly which authentication step stalled. Adding -o ConnectTimeout=5 prevents an unreachable host from hanging forever.

Output

debug1: Authenticated to host (22).

Reach an internal host through a bastion in one command

ssh -J bastion user@internal

Equivalent to writing ProxyJump bastion in ~/.ssh/config, and it removes the two nested sessions (log into the bastion, then ssh again) plus the risk that the inner session lacks your key file.

Keep a tunnel running in the background for remote debugging

ssh -N -L 8080:127.0.0.1:80 user@host

-N opens the tunnel without running a remote command, so run it under & or inside tmux. Local port 8080 is then remote port 80, and pointing your frontend at 8080 reaches the remote backend.

Common Pitfalls

  • ssh-copy-id appends to authorized_keys and creates the file if missing; running it repeatedly piles up duplicate keys and slows login and audits.
  • Forwarding your agent with ssh -A, or copying a private key onto a server, means one leaked key compromises every host it can reach; use ssh-agent plus ssh-add instead.
  • If ~/.ssh/config is not mode 600 ssh silently refuses to read it and fails with "Permissions too open" — a confusing message that rarely points at permissions.
  • ssh -J differs from nested ssh -t: when the outer tunnel drops, inner sessions die with it and interactive programs receive SIGHUP, so long jobs need nohup or screen.
  • Answering yes at the first host-key prompt writes that fingerprint permanently into known_hosts; unless you verified it with the admin first, you have just waived MITM protection.
  • Moving off port 22 with ssh -p changes nothing about authentication strength, and every hardcoded port in ssh-copy-id scripts and automation must be updated in lockstep or bulk access breaks.

Tips

  • Use ssh-copy-id for passwordless login — manually editing authorized_keys is error-prone with permissions and format.
  • ~/.ssh directory must be 700, private keys must be 600 — SSH refuses keys with loose permissions.
  • Add -N -f to run port forwarding in the background, but remember to stop with ssh -O exit or kill to avoid lingering processes.

FAQ

I configured key-based login but the server still asks for a password. How do I narrow it down?

Check three things in order. On the server, run sshd -T | grep authorizedkeysfile to confirm the authorized keys path actually matches where you dropped the file — some distros expect .ssh/authorized_keys2 or a different home. Then verify ownership and permissions: .ssh must be 700 and authorized_keys 600, and sshd silently ignores keys that are group- or world-readable. Finally run ssh -vvv user@host on the client and look for an "Offering public key" line. If it never appears, the client never picked up the private key — usually the agent has no key loaded, or IdentityFile in ~/.ssh/config points at the wrong path.

Why is my Host alias in ssh_config being ignored?

Almost always it is indentation. Keywords such as Host, HostName, User and IdentityFile must start at column zero — no leading spaces or tabs — while their values may be indented. Second, ssh uses the first matching Host block, so wildcard aliases have to sit above specific hosts or they will never be reached. Verify with ssh -G alias, which prints the fully resolved configuration, instead of repeatedly attempting connections.

When should I use local forwarding versus a dynamic proxy?

It depends on whether the destination is known. ssh -L 8080:127.0.0.1:80 is local forwarding with a fixed target — ideal for reaching one known remote service, here mapping remote port 80 to local 8080. ssh -D 1080 is dynamic forwarding: it opens a SOCKS5 proxy and the client picks the destination per request, which suits browsing multiple remote sites or temporarily routing all egress through the far end. Adding -N to either turns it into a pure tunnel with no command execution, which is how you run it in the background.

The connection just drops with no error message. How do I find the cause?

Run ssh -vvv and read the last few lines before the drop. If it stops at kex_exchange_identification, the session never reached authentication — usually sshd is down, the port is firewalled, or MaxStartups is rate-limiting you. If it disconnects immediately after auth succeeds, the remote user shell is probably exiting with a non-zero status from a startup script, or a LocalCommand failed. Two targeted probes help: force IPv4 with -o AddressFamily=inet in case name resolution returns an unreachable AAAA record, and add -o ServerAliveInterval=15 to rule out idle timeouts.

Should I use ed25519 or RSA keys?

Use ed25519 for anything new — keys are shorter, handshakes are faster, and modern OpenSSH (7.2+) supports it everywhere. RSA 4096 is worth keeping for two cases: legacy appliances or embedded devices that only speak RSA, and old clients that predate ed25519 support. Be aware that RSA below 3072 bits is rejected outright by the newer default policies shipped with several distros, so if you go RSA, go 4096. When both key types exist, the client tries ed25519 first; use IdentityFile inside a Host block in ~/.ssh/config to pin a specific key.

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.

Maintained by LaoHand

Publicly updated on Jul 21, 2026, continuously proofread against official docs.

Contact Us

Wrong command or description? Send us corrections, business inquiries or product feedback by email.

Contact Us