Full syntax
ssh [options] [user@]host [command]
ssh user@example.com
ssh -p 2222 user@example.com
ssh -i ~/.ssh/id_ed25519 user@example.com
ssh user@example.com "uptime"
ssh -J jumphost user@internal-server
The first form opens an interactive shell. The second connects to a non-standard port. The third specifies a private key. The fourth runs a single remote command and exits — useful for scripts and automation. The fifth connects through a jump host without agent forwarding exposure.
Essential flags
| Flag | Meaning | Example |
|---|---|---|
-p | Port number | ssh -p 2222 user@host |
-i | Identity file (private key) | ssh -i ~/.ssh/id_ed25519 user@host |
-L | Local port forward | ssh -L 8080:internal:80 user@host |
-R | Remote port forward | ssh -R 9090:localhost:3000 user@host |
-D | Dynamic SOCKS5 proxy | ssh -D 1080 user@host |
-N | No remote shell (tunnel only) | ssh -N -L 5432:db:5432 user@host |
-f | Background after authentication | Combine with -N for persistent tunnels |
-J | Jump host (ProxyJump) | ssh -J jump user@internal |
-v / -vvv | Verbose debug output | Shows each negotiation step; use -vvv for maximum detail |
~/.ssh/config format
The config file eliminates repetitive typing and makes connection settings explicit and version-controllable. A well-structured config also prevents subtle mistakes like connecting with the wrong key or wrong username.
Host prod
HostName server.example.com
User deploy
Port 2222
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 60
ServerAliveCountMax 3
Host *.internal
ProxyJump bastion.example.com
User admin
IdentityFile ~/.ssh/id_ed25519
Host *
ControlMaster auto
ControlPath ~/.ssh/cm-%r@%h:%p
ControlPersist 10m
ServerAliveInterval 60 sends a keepalive every 60 seconds to prevent idle disconnects. ControlMaster auto with ControlPersist 10m enables connection multiplexing — subsequent ssh, scp, and sftp calls to the same host reuse the existing TCP connection, making them near-instant.
SCP and SFTP
SCP (Secure Copy) copies files over SSH. Basic usage: scp file.txt user@host:/remote/path/ to upload, scp user@host:/remote/file.txt ./ to download. Use -r for directories and -P (capital P, unlike ssh's lowercase) for non-standard ports. Note: SCP is deprecated in recent OpenSSH releases in favor of SFTP-based file transfer, though the scp command still works by using SFTP internally on modern servers.
SFTP provides an interactive file transfer session: sftp user@host. Inside the session: ls and cd navigate the remote filesystem, put file uploads, get file downloads, mput *.log uploads multiple files, mget *.csv downloads multiple files. SFTP is the standard for managed file transfer where scp's simplicity is insufficient.
SSH key generation
Generate an Ed25519 key pair: ssh-keygen -t ed25519 -C "your@email.com". The -C comment helps identify the key in authorized_keys files. Accept the default path (~/.ssh/id_ed25519) or specify a custom path for keys used with different services. Set a strong passphrase — it encrypts the private key file at rest so it is useless if stolen without the passphrase. Copy the public key to a server: ssh-copy-id user@host, which appends it to ~/.ssh/authorized_keys with correct permissions. Verify the key works before disabling password auth.
SSH agent
The SSH agent holds decrypted private keys in memory so you enter the passphrase once per session rather than per connection. Start the agent: eval $(ssh-agent). Add a key: ssh-add ~/.ssh/id_ed25519. The agent socket path is stored in SSH_AUTH_SOCK — SSH and SCP read this environment variable automatically to find the agent. On macOS, the system keychain integrates with the agent; on Linux, most desktop environments start an agent at login. For headless servers, use a persistent agent tool or configure ControlMaster multiplexing instead.
Debugging connection failures
Run ssh -vvv user@host to see every step: DNS resolution, TCP connection, SSH version exchange, algorithm negotiation, host key check, and authentication attempts. Common errors and their causes:
- Permission denied (publickey): The server did not accept any offered key. Check that the public key is in
~/.ssh/authorized_keyson the server with permissions 600, and that the file's parent directory has permissions 700. Use-vto see which key was offered. - Host key verification failed: The stored fingerprint in
~/.ssh/known_hostsdoes not match what the server presented. The server may have been rebuilt — verify the fingerprint out-of-band (cloud console, datacenter KVM), then remove the old entry withssh-keygen -R hostname. - Connection refused: Nothing is listening on that port, or a firewall is sending a TCP RST. Verify the SSH service is running and the firewall allows port 22 (or your custom port) from your source IP.
- Connection timed out: The TCP SYN is being dropped by a firewall — no RST, just silence. Check security group rules, iptables, or network ACLs between you and the server.
- Slow login after authentication: Often caused by the server performing a reverse DNS lookup on your IP. Add
UseDNS noto sshd_config to eliminate the delay.
Security Habits
- Prefer key authentication over passwords.
- Protect private keys with a passphrase.
- Do not ignore host key warnings casually.
- Use a firewall so SSH is reachable only where needed.
- Disable root login on internet-facing servers when possible.
Frequently Asked Questions
What does ssh do?
It opens an encrypted remote login session or runs a command on a remote machine.
What port does SSH use?
TCP port 22 by default.
Is SSH safer than Telnet?
Yes. SSH encrypts traffic and authenticates the server. Telnet is plaintext.