Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild
Back to Blog Explainer

SSH Flags Worth Knowing: The Ones You Will Actually Use

Sean

Platform Writer

Aug 26, 2026
8 min read

The flags you will reach for repeatedly are -i for a key file, -p for a port, -v for debugging, -L and -R for tunnels, -J for jump hosts, and -o for anything else. Everything worth using regularly belongs in ~/.ssh/config rather than being typed each time.

SSH Flags Worth Knowing: The Ones You Will Actually Use

man ssh lists an intimidating number of options. In practice a small set covers nearly everything, and the rest are worth knowing exist so you can look them up when the situation arrives.

Table of contents

The everyday flags

ssh -i ~/.ssh/deploy_key user@host      # use a specific private key
ssh -p 2222 user@host                   # non-standard port
ssh -l username host                    # username as a flag
ssh -v user@host                        # verbose (-vv, -vvv for more)
ssh -q user@host                        # quiet: suppress warnings
ssh -t user@host 'sudo systemctl status nginx'   # force a TTY
ssh -N -f user@host                     # no command, background

-t is the one people miss. Running a remote command that expects a terminal — sudo with a password prompt, top, an editor — fails without it because SSH does not allocate a pseudo-terminal for non-interactive commands.

-v is the diagnostic. Three levels, and the useful lines are usually near the end:

ssh -vvv user@host 2>&1 | grep -E "Offering|Authentications that can continue|debug1: Next"

Offering public key followed by a rejection means the server received your key and does not have it authorised. No offering line at all means your client never presented one, which is a local problem.

-o sets any config-file option inline, which is what you use for anything without a dedicated flag:

ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new user@host
ssh -o IdentitiesOnly=yes -i ~/.ssh/specific_key user@host

IdentitiesOnly=yes is worth knowing: without it, your agent offers every key it holds, and a server with a low MaxAuthTries disconnects you before reaching the right one. That produces a confusing “too many authentication failures” against a key you know is correct.

Tunnels

Three forwarding modes, and the direction is what people get backwards.

-L local forward — a port on your machine reaches a service through the remote host. This is how you reach a database that only listens on the server’s localhost:

ssh -L 5432:localhost:5432 user@dbserver
# now connect a local client to localhost:5432

Read it as: bind port 5432 here, and forward to localhost:5432 as seen from the remote host. The middle hostname is resolved on the far side, which is the part that confuses people:

ssh -L 8080:internal-app.local:80 user@bastion
# reach internal-app.local -- a name only the bastion can resolve

-R remote forward — a port on the remote host reaches a service on yours. For exposing a local development server to something on the network:

ssh -R 8080:localhost:3000 user@server

By default -R binds only to the remote’s loopback. Reaching it from elsewhere requires GatewayPorts yes in the server’s config.

-D dynamic forward — a local SOCKS proxy routing traffic through the remote host:

ssh -D 1080 -N -q user@host
# point a browser's SOCKS5 proxy at localhost:1080

Combine with -N (no remote command) and -f (background) for a tunnel that just sits there.

Jump hosts

-J connects through a bastion in one command, and it replaced a genuinely awkward older idiom:

ssh -J user@bastion user@internal-server
ssh -J user@bastion1,user@bastion2 user@target    # chained

The important property: your key authenticates to the final host directly, and the traffic is encrypted end to end. The bastion forwards a stream it cannot read.

The old approach was -o ProxyCommand with a nested ssh, and the even older one was agent forwarding — which is worth avoiding. -A forwards your agent to the remote host, and anyone with root there can use your agent socket to authenticate as you elsewhere. -J gives you the same convenience without that exposure.

In ~/.ssh/config, so you never type it:

Host bastion
  HostName bastion.example.com
  User jump

Host internal-*
  ProxyJump bastion
  User deploy
  IdentityFile ~/.ssh/internal_key

Now ssh internal-db01 routes through the bastion automatically, with the right user and key.

Staying connected and connecting faster

Sessions dropping while idle is a NAT or firewall timeout, not an SSH fault. Keepalives fix it:

Host *
  ServerAliveInterval 60
  ServerAliveCountMax 3

That sends a keepalive every 60 seconds and gives up after three unanswered — so a genuinely dead connection still fails, rather than hanging indefinitely.

Connection multiplexing makes repeated connections to the same host nearly instant by reusing one authenticated channel:

Host *
  ControlMaster auto
  ControlPath ~/.ssh/sockets/%r@%h:%p
  ControlPersist 10m
mkdir -p ~/.ssh/sockets

The first connection authenticates normally; subsequent ones reuse the socket and skip the handshake entirely. This is very noticeable with tools that open many short SSH sessions, such as Ansible or a deploy script running a series of commands.

Manage an existing master connection with -O:

ssh -O check user@host
ssh -O exit user@host

Flags worth being careful with

-o StrictHostKeyChecking=no disables host key verification, which is the protection against a machine-in-the-middle attack. It appears in a lot of CI scripts because it makes an error go away.

The better option for automation, available since OpenSSH 7.6:

ssh -o StrictHostKeyChecking=accept-new user@host

That accepts a key the first time and still refuses if a known key later changes — which is the case that actually matters. Best of all is pre-seeding the known host:

ssh-keyscan -H host >> ~/.ssh/known_hosts

-A agent forwarding — as above, avoid it. Use -J instead.

-o PasswordAuthentication=yes in a script means a password is being supplied from somewhere, which usually means it is in the script.

And the one to know when a host key legitimately changes, after a rebuild:

ssh-keygen -R hostname          # remove the stale entry
ssh-keygen -R 203.0.113.42      # and by IP if present

Editing known_hosts by hand is error-prone because entries may be hashed. -R handles both forms.

How this fits the rest of the stack

The useful lesson in SSH’s flag set is that almost none of it should be typed twice. ~/.ssh/config turns a memorised incantation into a host alias, and it is the difference between a deploy someone can run and a deploy that lives in one person’s shell history.

That generalises past SSH. Anything required to ship that exists only on one machine is a step nobody else can reproduce. RunxBuild deploys services and static sites from a connected GitHub repository — no key to distribute, no bastion to configure, and no SSH surface to keep hardened — with build and runtime logs in the dashboard instead of behind a session. The RunxBuild hosting calculator shows what a service, database and storage come to together.

Useful related references:

FAQ

What is the difference between ssh -L and ssh -R?

-L forwards a local port through the remote host, letting you reach a service the server can see. -R forwards a remote port back to your machine, exposing something local to the server. The middle hostname in both is resolved from the remote side, which is the part most often misread.

How do I specify a private key with ssh?

ssh -i ~/.ssh/keyname user@host. Add -o IdentitiesOnly=yes so your agent does not offer every other key first — without it, a server with a low MaxAuthTries can disconnect you before reaching the one you specified.

What does ssh -t do?

It forces allocation of a pseudo-terminal, which remote commands expecting an interactive terminal need. Without it, sudo password prompts, editors and full-screen tools fail when run as a remote command.

Is ssh -A safe to use?

Not on hosts you do not fully trust. Agent forwarding exposes your agent socket on the remote machine, and anyone with root there can use it to authenticate as you elsewhere. Use -J for jump hosts instead — it keeps the connection end-to-end encrypted and never exposes your agent.

How do I stop SSH sessions from dropping when idle?

Add ServerAliveInterval 60 and ServerAliveCountMax 3 under Host * in ~/.ssh/config. That keeps the connection alive through NAT and firewall timeouts while still failing promptly if the connection is genuinely dead.

#ssh flag#ssh#linux#command line#networking