SSH Operations Manual
On this page
When a connection fails, locate the failure in networking, host identity, or user authentication. For forwarding, identify the listener and the machine that connects to the target. Once those are clear, choosing a command becomes much easier.
This guide covers OpenSSH clients and Linux servers. ops is an example remote user and server.example.com an example host. server, jump, and internal below are aliases defined in your ~/.ssh/config. Run commands on the machine specified in their comments.
Find your task
| What you need to do | Start here |
|---|---|
| Log in, or save a port, username, and key | Connection and configuration |
| Reach an internal host through a bastion | Jump hosts |
| Set up public-key login or identify the key being used | Three different key files |
| Reach a remote service locally, or expose a local service to the server | Port forwarding |
| Upload, download, or synchronize a directory | File transfer |
| Diagnose timeouts, refused connections, rejected keys, or changed host keys | Diagnose by failure stage |
Connection and configuration
# Local: connect using the default SSH port
Verify the host fingerprint on first connection. ops is the remote account. Choose the explicit form below for non-default settings.
# Local: select port and identity explicitly
-p is the server port, -i the local private key, and IdentitiesOnly limits offered identities. This is an alternative to the previous command.
The configuration below defines server. After saving it, use ssh server; the following command-only invocation is also launched locally.
# Local: run one server command and exit
The remote shell interprets the quoted command. Verify the host and exit status; 255 usually signals an SSH error, while other values may come from the remote command.
Save frequently used hosts in ~/.ssh/config. ssh, scp, and rsync can all reuse these aliases:
Host server
HostName server.example.com
User ops
Port 2222
IdentityFile ~/.ssh/id_ed25519_ops
IdentitiesOnly yes
Host jump
HostName bastion.example.com
User ops
IdentityFile ~/.ssh/id_ed25519_ops
IdentitiesOnly yes
Host internal
HostName 10.0.0.20
User ops
ProxyJump jump
IdentityFile ~/.ssh/id_ed25519_ops
IdentitiesOnly yes
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
Put specific hosts first and general defaults last. Most options use the first value obtained, rather than letting later blocks override earlier ones. Accumulating options such as IdentityFile have their own rules. IdentitiesOnly yes restricts the configured identities attempted, instead of trying every key in the agent. OpenSSH configuration rules
ServerAliveInterval sends SSH-level probes when no data has arrived from the server; enough unanswered probes cause disconnection. This helps detect an unresponsive peer and may keep intermediary connection state alive. It does not reconnect or restore a remote process.
If a setting seems ineffective, inspect the resulting values first:
# Local: evaluate configuration without a session
|
-G displays evaluated client settings after Host/Match processing; inspect ProxyJump and IdentityFile especially.
If this still shows an old IP, port, or wrong user, check the alias spelling, matching Host blocks, and command-line arguments. Editing the server's authorized_keys will not fix that mismatch.
Jump hosts
# Local: use the configured jump host and target
Verify both aliases first with ssh -G internal.
# Local: alternatively specify a jump host once
-J names the jump host; the final argument is the target. This is not a command to run inside the previous remote session.
The path is local machine → bastion → internal host. The local client handles authentication to both hosts; the bastion forwards TCP to the final destination. You do not need to copy your private key to the bastion or enable agent forwarding for -J. Configure each host's port and identity in its own Host block. OpenSSH jump connections
Three different key files
Public-key login involves identity checks in both directions. Read the two paths separately: a changed server identity and a user without login permission are different failures.
| File | Location | Purpose |
|---|---|---|
id_ed25519_ops | Local machine, or used by its agent | User private key used to prove the user's identity |
authorized_keys | Target user's .ssh/ directory on the server | User public keys permitted to log in to that account |
known_hosts | Local machine | Trusted server host keys |
Add a login key
# Local: create a new key pair for this purpose
-f chooses a new filename; cancel if it already exists. -C is a label. The private key stays local; .pub is public.
# Local: install the public key through existing access
Requires an existing authentication method. It targets remote ops’s authorized_keys. Send only .pub; key creation alone does not install it.
# Local: open a separate connection to test the identity
Expect remote ops and retain existing access until the new identity works.
Success should print ops, the remote username. ssh-copy-id requires an existing way to log in; it does not bypass access controls. A key passphrase unlocks the private key locally and is separate from the remote account password. Environments with compliance or compatibility constraints must use a supported key type.
Use an agent to avoid repeated passphrase prompts
# Local: list agent identity fingerprints
Use ssh-add -L separately for full public keys; both are read-only.
# Local: load this key into the agent for one hour
Enter the key passphrase and verify its fingerprint with ssh-add -l. This affects the agent selected by SSH_AUTH_SOCK.
# Local: remove this identity when no longer needed
This is later removal. -D clears every identity in that agent, not just this key.
If the authentication agent cannot be reached, check whether the session has a working SSH_AUTH_SOCK. Desktop sessions often provide an agent already. Run eval "$(ssh-agent -s)" in the current shell only when one is actually missing, rather than starting a new agent in every terminal.
Port forwarding: locate the listener and target
-L and -R are named for the location of the listening port. Both carry bidirectional data through an SSH connection. Arrows below show where new application connections enter, not a restriction on response traffic.
-L listens locally and connects to the target from the server. -R listens on the server and connects from the client. -D offers a local SOCKS listener; the server connects to the target requested by the application. Arrows show where application connections enter; tunnel data flows both ways.
Local forwarding: reach a service on the remote side
# Local terminal 1: keep the local forward running
This occupies the terminal. Check local port conflicts on failure; test while it remains running.
# Local terminal 2: test the HTTP target through the forward
Inspect status/body; close the tunnel with Ctrl-C in terminal 1 after testing.
The two 127.0.0.1 addresses refer to different machines. The first is the local listening address. The second is used by the SSH server to connect to the target, so it refers to the server's own loopback interface. For an internal host reachable from the server, replace the target with db.internal:5432 and point your database client at local port 8080. Target name resolution also occurs on the server side.
Remote forwarding: let the server reach a local service
# Local: create a remote entry for the local port 8000 service
Verify the local service first and keep this terminal running.
# Server: test the remote loopback entry
Run in a separate server session, not on the client running the tunnel.
The first address requests a listener on the server; the local client connects to the second address. This lets the server itself reach your local service. It does not by itself publish that service to the internet.
The server's GatewayPorts controls remote binding: no restricts it to loopback, clientspecified lets the client choose, and yes forces a wildcard bind. Access from other machines requires an explicit administrative decision about binding, forwarding permissions, and inbound access controls. Verify the actual listening address; do not treat yes as a universal fix. Remote binding policy
Dynamic forwarding: let applications request targets through SOCKS
# Local terminal 1: start the SOCKS listener
Keep it running; this is an alternative use, not a requirement to start all three tunnel types.
# Local terminal 2: resolve the target through SOCKS
The h requests proxy-side DNS. Verify the response and stop terminal 1 with Ctrl-C afterward.
Applications must be configured to use SOCKS. Starting -D does not automatically proxy all machine traffic. There is no fixed target in the SSH command; each proxy request supplies one. OpenSSH forwarding options
A listening tunnel does not prove that the target works
-N skips remote command execution; -T disables pseudo-terminal allocation. ExitOnForwardFailure=yes exits if forwarding setup fails, but does not exit merely because a later connection to the target fails. Forwarding failure boundaries
Check these three locations instead of only checking that an SSH process exists:
| Location | What to inspect | What it establishes |
|---|---|---|
| Machine hosting the listener | On Linux, use ss -ltn to find 8080, 9000, or 1080 | Whether an entry point exists and its bind address; use authorized ss -ltnp access to identify the process if necessary |
| Machine connecting to the target | Contact the target directly with its protocol client; here, curl -v http://127.0.0.1:8000/ | Whether the target is reachable from the correct side |
| Application entry point | Make a request through the forwarded port or SOCKS proxy | Whether an actual end-to-end request succeeds |
For -L / -D, the target-connecting side is the server; for -R, it is the local client. administratively prohibited points toward server forwarding policy or key restrictions. With connect failed: Connection refused, inspect the target address and service listener.
File transfer: check direction and directory layout
| Task | Run locally |
|---|---|
| Upload one file | scp report.txt server:/tmp/ |
| Download one file | scp server:/tmp/report.txt ./ |
| Upload a directory | scp -r assets server:/tmp/ |
| Specify an SCP SSH port without an alias | scp -P 2222 report.txt ops@server.example.com:/tmp/ |
The port option is uppercase -P for scp, lowercase -p for ssh. Host aliases avoid repeatedly specifying it.
Directory synchronization: make the expected result explicit
Assume local site/index.html exists, and remote /srv/www/ already exists and is writable by the user:
| Source | Command | Resulting remote file |
|---|---|---|
site/: directory contents | rsync -av site/ server:/srv/www/ | /srv/www/index.html |
site: directory itself | rsync -av site server:/srv/www/ | /srv/www/site/index.html |
The trailing slash determines whether the source directory adds another directory level. rsync path semantics
# Local: preview updates from the source directory contents
Review itemized changes and path nesting. Dry-run does not write files.
# Local: execute the reviewed synchronization scope
Check exit status, repeat the same dry-run for unexpected differences, and test the site. Restoring overwritten remote files requires retained copies.
-a includes recursion and common metadata preservation. Preserving ownership and some other attributes depends on permissions; ACLs, extended attributes, and hard links are not automatically included. --partial allows retries to reuse partial data, not an unconditional append from the last byte. Add -z when compression is useful. These commands retain extra destination files; consider --delete only for an intentional mirror, and preview deletions first.
Diagnose by failure stage
Run ssh -vvv server locally and find the last successful stage. The following are representative log fragments; wording varies by version. When using a jump host, distinguish its logs from those for the final destination.
| Symptom or log | What you know | Where to look next |
|---|---|---|
Could not resolve hostname | No target connection yet | Evaluated HostName from ssh -G, DNS, alias spelling |
Connection timed out / No route to host | TCP has not connected; this does not implicate the public key | Address, route, security group, firewall, host reachability |
Connection refused | The connection was actively refused | Correct host and listening port; active rejection rules are also possible |
Connection established | TCP is connected; login is not complete | Key exchange, host identity, then authentication |
REMOTE HOST IDENTIFICATION HAS CHANGED | The presented host key differs from the stored record | Independently verify server identity, as below |
Offering public key, followed by rejection | That key was attempted but login is not authorized | Remote username, key source, permissions, authentication policy |
Too many authentication failures | Authentication attempts were exhausted | Configured identities; restrict attempts with IdentitiesOnly |
Authenticated to ... | User authentication succeeded | If disconnection follows, inspect the remote shell, forced command, session restrictions, and server logs |
Do not change permissions in bulk for every authentication failure, or start by setting UseDNS no whenever a connection stalls. Locate the failing layer before changing its configuration.
Rejected public key: trace the exact identity
# Local: read the user-key fingerprint
-l prints the fingerprint and -f selects the public file; compare on the target account next.
# Server target user: list authorized fingerprints
Compare with the local public key and verify that this is the remote target user’s home.
# Server target user: inspect path ownership and modes
Inspect ownership and unwanted write permissions; repair only incorrect paths, not the entire home recursively.
For the default file layout, usual modes are 700 for .ssh and 600 for authorized_keys, with correct ownership and no inappropriate write access to the home directory. If the server uses a different AuthorizedKeysFile, centralized key lookup, or certificate authentication, inspect the actual configured source.
For administrative diagnosis, inspect the actual server unit, commonly ssh.service or sshd.service:
# Server administrator: inspect recent authentication rejection
Correlate source/user/time. The two unit names cover distribution differences, not two required daemons.
# Server administrator: validate the server configuration on disk
Success is normally silent. Fix errors before reload. Reload the actual service when needed, retaining the old session while testing a new login.
The first command looks for the rejection reason; on non-systemd systems, inspect the relevant authentication log. The second checks server configuration and basic host-key validity, not whether a particular user can log in. After a configuration change, keep the current session open and verify a new connection before closing it.
Changed host key: verify before updating the record
Use an already trusted console or an independent administrator channel to verify the new fingerprint. For example, inspect the server's Ed25519 host public key from its console:
# Trusted server console: read the host identity fingerprint
This is the host public key, not authorized_keys; match the algorithm shown in the client warning.
Match both the algorithm and fingerprint against the local prompt. If another key type was negotiated, inspect that host public key instead. Once identity and the reason for the change are confirmed, remove the old record for the host and port named in the error:
# Local: remove the old entry after independent verification
Match the exact host/port from the warning; preserve the rest of known_hosts.
# Local: reconnect and verify the new fingerprint
Accept only after matching the trusted channel. Removing an entry does not authenticate the new endpoint.
For default port 22, usually use ssh-keygen -R server.example.com. Account for HostKeyAlias or a custom known-hosts file if configured. Verify the new fingerprint on reconnection. ssh-keyscan collects the key presented by the current network endpoint; it does not independently establish trust. Host-key record operations
Further reading
For complete options, consult the installed version's man ssh, man ssh_config, and man sshd_config. For protocol layers and channel mechanics, read SSH Protocol. This manual keeps common tasks easy to find; the companion article develops the protocol details.