
The part that does the most for me is not a feature with a marketing name, it is the client config file. My ~/.ssh/config is where the actual time savings live. I give each host an alias, set its User, Port, IdentityFile, and ProxyJump once, lean on wildcard Host patterns for whole environments, and use Match blocks for the cases that need conditional settings. After that, reaching a box is "ssh prod-db" and nothing more. I keep that file in version control and carry it between machines, so a new laptop is productive the moment I clone it. No other piece of my toolchain pays back the setup effort the way this one file does.
I would give up a lot before I gave up ProxyJump. Most of what I administer sits behind a bastion, and before the -J flag existed I was stringing ProxyCommand together with netcat or opening two terminals and hopping by hand. Now a single line in the host entry routes me through the jump host and the target behaves as if it were on my desk. When the path is two or three hops deep, ProxyJump chains them without any of the old fiddling, and because it lives in the config it is the same every time.
Connection multiplexing gets overlooked until you watch it work. With ControlMaster, ControlPath, and ControlPersist set, the first SSH connection to a host opens a master socket and every session after that rides the existing connection instead of negotiating a new one. For anything that fires a burst of short connections, Ansible runs being the obvious case, the difference is large. Repeated scp and rsync to the same host stop paying the handshake tax. I set it once in the config and forget it is there, which is exactly what I want from plumbing.
Key handling through ssh-agent turns key-based auth from a chore into something I forget I am doing. I generate an ed25519 key with ssh-keygen, push it out with ssh-copy-id, load it into the agent once at login, and then move across the fleet without typing a passphrase again. Agent forwarding extends that to a jump host when I need it, though I am deliberate about where I forward an agent and usually prefer ProxyJump for the safer path. The friction of proving who I am to dozens of machines simply goes away.
The server side deserves as much credit as the client, and it is easy to forget that OpenSSH is both. sshd_config is where I lock a host down: key-only auth with PasswordAuthentication off, PermitRootLogin set to no, AllowGroups to gate who even gets to the login prompt, and Match blocks to apply per-user or per-group policy. For accounts that should only move files, a ChrootDirectory plus an sftp-only setup keeps them in their lane. Having the client and the daemon come from the same project, with the same key formats and the same conventions, means there is one mental model for both ends of the connection.
Port forwarding is the quiet workhorse. Local forwarding through a bastion lets my desktop tools connect to localhost as though a private-subnet database were sitting next to me, remote forwarding covers the reverse case, and the dynamic SOCKS option gives me a quick proxy into a segment when I need more than a single port. All three can be declared in the saved host entry with LocalForward or DynamicForward, so a tunnel comes up with the session and disappears when I close it, no extra software involved.
File transfer reusing the same channel is one of those things I would miss badly if it were gone. scp, sftp, and rsync over SSH all ride the authentication and encryption I have already set up, so there is no separate FTP daemon to stand up or trust. scp now speaks the SFTP protocol underneath, which cleaned up a lot of the old edge cases. Pulling a log bundle or syncing a directory is the same credentials and the same host alias I use for an interactive session.
On security, the defaults are conservative in the way I want from something this central. Modern key types and ciphers like ed25519 and ChaCha20-Poly1305 are the norm, the host key fingerprint prompt on first connect is the small ritual that catches you reaching the wrong machine, and the recent move to hybrid post-quantum key agreement by default in the 10.0 release means traffic captured today is protected against decryption later without me changing a single setting. The OpenBSD lineage shows in choices like splitting authentication into a separate sshd-auth process to shrink the pre-auth attack surface. None of this is loud, and that is the point: the project tends toward the careful option rather than the flashy one.
And it is already there. On every Linux server, on macOS, on the BSDs, OpenSSH is the SSH that ships with the system, free under a permissive license, with no account to create and nothing phoning home. For the common case there is nothing to install at all, which for a tool this fundamental to a working day is worth more than any single feature. Review collected by and hosted on G2.com.
The diagnostics are terse to the point of being unhelpful when something goes wrong. "Permission denied (publickey)" is the message I have stared at more than any other, and on its own it tells me nothing about which key was offered, why the server refused it, or whether the real cause was a file permission, an AllowGroups rule, or the wrong username. The fix is to run with -vvv on the client and turn LogLevel up on the server and read auth.log, and after enough years that is reflex, but a newcomer hits a wall here with no obvious way over it. Better default error messages would save a lot of people a bad afternoon.
Tightening defaults across releases is the right thing to do and it still bites at the worst time. When a version drops or de-prioritizes an older algorithm, the removal of DSA in 10.0 being the clean example, alongside earlier moves away from ssh-rsa with SHA-1 and changes to the moduli fallback, I periodically find I cannot connect to a legacy switch or an old appliance until I explicitly re-enable the algorithm it speaks. The security reasoning is sound, but discovering it mid-task when a box that worked last month now refuses the connection is a recurring annoyance. My workaround is a narrowly scoped Host block that re-enables the old HostKeyAlgorithms or PubkeyAcceptedAlgorithms for that single device and nothing else.
There is no non-interactive password authentication, by design, and it trips up scripting against hosts that only accept a password. When I inherit an estate where some machine cannot take a key yet, automating against it means reaching for sshpass, which is ugly and which I do not want anywhere near a real pipeline, or stopping to fix the host so it accepts keys. The right answer is keys everywhere and I push for that, but the gap between "should be" and "is" is where this one lives.
The Windows story has improved a great deal and is still the weakest part. The Win32 port is built into Windows now and genuinely useful, but it trails the OpenBSD release, the agent service and connection multiplexing do not always behave the way they do on Unix, and I still run into path and permission quirks that I never see elsewhere. When the work is Windows-first I often end up doing my SSH from inside WSL just to get the upstream behavior, which works but is a detour.
There is no session management or interface of any kind, which is a deliberate choice and occasionally an inconvenience anyway. Everything lives in the config file and in whatever terminal multiplexer I am running. Once you are fluent that is fine, arguably better than a GUI, but onboarding someone to a new environment means teaching them ~/.ssh/config from a blank page rather than handing them a tidy list of saved connections. A well-commented config in a shared repository is the closest thing to a fix, and it is what my team uses, but it is a convention we have to maintain rather than something the tool provides. Review collected by and hosted on G2.com.