For a long time, my relationship with remote servers was… messy. As a Backend Engineer, I’m constantly jumping between dev environments, staging clusters, and production databases. But for the longest time, my “workflow” was just a series of Ctrl+R searches in my shell history, hoping to find that one specific IP address or long-forgotten identity file flag.
It was “SSH Spaghetti.” It worked, but it was friction-heavy. This is the story of how I moved past that chaos and turned my ~/.ssh/config into a core part of my engineering identity.
The Breaking Point: The “Two-Step” Dance
The moment I realized I needed a better way was when our team moved behind a Bastion host (Jump Box). Suddenly, every time I wanted to check a log or run a quick query on an internal DB, I had to:
- SSH into the Bastion.
- Authenticate again.
- SSH from the Bastion to the internal server.
- Copy-paste data back and forth like a digital courier.
It was exhausting. It broke my flow. I started avoiding small tasks because the “setup cost” of just getting into the server was too high. That’s when I started looking for a way to make the infrastructure transparent.
Discovery: The Hidden “Engine”
I had always known the ~/.ssh/config file existed for simple aliases (e.g., Host dev), but I didn’t realize it was a full-blown configuration engine.
Solving the Bastion Headache
When I discovered ProxyJump, it felt like magic. I could define my Bastion and my internal servers once, and suddenly I was back to a single command: ssh internal-db. SSH was handling the tunneling, the identity forwarding, and the multi-hop authentication behind the scenes.
The friction was gone. I wasn’t “logging into two boxes” anymore; I was just “going to work.”
The “Aha!” Moment: Instant Connections
The second major shift in my journey was discovering Multiplexing (via ControlMaster).
As someone who often has three terminal windows open for the same server—one for htop, one for logs, and one for a shell—I was used to the 2-3 second delay of the SSH handshake every time I opened a new tab. It doesn’t sound like much, but multiplied by 50 times a day, it adds up to a lot of “micro-waiting.”
Setting up a socket-based connection reuse changed everything. The first connection took its usual time, but the second and third were instantaneous. It made the remote server feel like it was running locally on my machine.
The Refinement: Integration with My NixOS/Dotfiles
As I went deeper into the NixOS ecosystem (which I’ve written about before), my SSH config stopped being a static file and started being a “living document.”
I began version-controlling it in my private dotfiles repo. I added global defaults like ServerAliveInterval to stop those annoying “Broken Pipe” disconnects during my morning coffee breaks. I set up AddKeysToAgent so I only had to type my long, complex passphrase once per reboot.
My SSH config became an extension of my NixOS philosophy: Reproducible, reliable, and frictionless.
Why It Matters
We often focus on the “big” tools—the frameworks, the languages, the databases. But as engineers, we live in the “small” tools. The way we move between servers is the “commute” of our workday.
By taking the time to refine my SSH config, I didn’t just save a few seconds here and there; I removed the mental overhead of “getting there.” Now, whether I’m jumping through a triple-bastion setup or just checking a dev box, the experience is identical. Mastering the small tools makes the big work easier.
My Current Philosophy
| Stage | My Old Way (Spaghetti) | My New Way (Refined) |
|---|---|---|
| Identity | -i ~/.ssh/key_name | Automatic per-host IdentityFile |
| Jumping | Manual two-step SSH | Transparent ProxyJump |
| Speed | 3-second handshakes | Instant Multiplexing |
| Persistence | Frequent “Broken Pipes” | ServerAlive Keep-alives |