SSH dotfiles: unlocking efficiency
Managing dozens of SSH connections means remembering complex hostnames, multiple keys, and elaborate commands you copy from text files. The .ssh/config file transforms this chaos into memorable aliases that map mental shortcuts to complete configurations, reducing cognitive load so you can focus on actual work rather than SSH incantations.I used to keep a text file with SSH commands. Each entry was a small novel—hostname, username, port number, identity file path, sometimes a ProxyJump directive for servers behind bastions. I'd copy the command, paste it into the terminal, inevitably get the path wrong, curse, fix it, try again. For deployment servers, I'd type the same 80-character command multiple times daily. The cognitive overhead wasn't the typing—it was remembering which key went with which server, which port, which username.
Then someone showed me their SSH config file. They typed ssh prod and connected to a production server that would have taken me three lines to reach. They typed ssh db and opened a secure tunnel to a remote database. No copy-pasting. No remembering. Just aliases that mapped mental shortcuts to complex configurations.
What is SSH
SSH (Secure Shell) is the network protocol that secures remote server access through strong encryption. Every developer and sysadmin uses it daily—connecting to servers, deploying code, managing infrastructure, accessing remote resources. The protocol itself is straightforward: authenticate, establish encrypted channel, execute commands remotely.
The complexity comes from managing dozens or hundreds of these connections. Different servers require different keys. Some sit behind bastion hosts. Others need port forwarding for database access. Each connection carries its own hostname, username, port, and identity file. Without organisation, this becomes dozens of commands to remember or a text file to copy from.
The .ssh/config file transforms this chaos into simplicity. It's a dotfile that maps memorable aliases to complete SSH configurations, turning labyrinthine commands into single-word shortcuts. This isn't convenience for its own sake—it's reducing cognitive load so you can focus on the actual work rather than SSH incantations.
Specifying identity files for different servers
The first pain point most people encounter is managing multiple SSH keys. You have one key for GitHub, another for your company's servers, a third for AWS instances, perhaps a fourth for client infrastructure. SSH will try keys in sequence, but this creates delays, authentication failures, and the occasional lockout when you've exceeded failed attempt limits.
The IdentityFile directive solves this by mapping specific keys to specific servers. Each host gets exactly the key it expects, every time, automatically. No manual specification. No trying wrong keys. The configuration handles it.
Host server # Alias for the remote server.
HostName server.mycompany.com # The hostname or IP address of the remote server.
User myusername # The username for the remote server.
IdentityFile ~/.ssh/my_id_rsa # The private key for authentication.
Now connecting requires just the alias:
ssh server
Instead of ssh -i ~/.ssh/my_id_rsa myusername@server.mycompany.com, you type two words. The configuration remembers everything else—the hostname, the username, which key to use. You've offloaded that mental overhead to the config file where it belongs.
Enabling agent forwarding
A common workflow involves SSH-ing to a server, then from that server connecting to another—perhaps deploying code from a Git repository that requires authentication, or accessing an internal server from a bastion host. The naive approach copies your private key to the intermediate server. This works, but it's a security disaster waiting to happen. Your private key now lives on a server you don't fully control, creating an additional point of vulnerability.
Agent forwarding solves this elegantly. Your key stays on your local machine. When the remote server needs to authenticate, it forwards the request back to your local SSH agent, which handles authentication without ever exposing the private key. If the remote server gets compromised, the attacker gains no access to your keys—they've evaporated the moment your SSH session closes.
Host deploy-server # Alias for the remote deployment server.
HostName deploy.mycompany.com # The hostname or IP address of the remote deployment server.
ForwardAgent yes # SSH client is instructed to forward the authentication request from the remote server back to your local machine
When you connect from deploy-server to another server requiring your SSH key, the authentication check forwards back to your local machine where your SSH agent handles it. No keys copied. No additional vulnerability.
Setting up port forwarding
Database access creates its own set of problems. Production databases shouldn't be exposed to the internet—that's basic security. But you need to connect to them for debugging, migrations, or administrative work. The standard solution involves opening database ports to specific IP addresses, configuring firewall rules, managing security groups, and hoping you didn't create a hole an attacker can exploit.
SSH port forwarding provides a better approach. You create an encrypted tunnel through SSH, forwarding a local port to the remote database port. Your local tools connect to localhost, but the connection routes through the SSH tunnel to the remote database. The database never needs public exposure. All traffic flows through the encrypted SSH connection. Your development tools work as if the database were local.
Host db
HostName remote.example.com # The hostname or IP address of the remote server where the database is hosted
User remote-user # The user account on the remote server you will log in as
LocalForward 3306 127.0.0.1:3306 # Bind the local port 3306 to the remote port 3306
IdentityFile ~/.ssh/id_rsa # The path to your private key for authentication
ServerAliveInterval 120 # Keep the SSH tunnel alive by sending a packet every 120 seconds
ServerAliveCountMax 3 # Number of server alive messages which may be sent without ssh receiving any messages back from the server.
Open the tunnel with the -N flag, which tells SSH you're not running commands—just establishing the port forward:
ssh -N db
The terminal appears to hang. That's expected—the tunnel is open and listening. Your database tools now connect to localhost:3306, and traffic routes securely through the SSH tunnel to the remote database. The ServerAliveInterval settings keep the tunnel alive even during periods of inactivity, preventing annoying disconnections mid-query.
SSH alias for a Git repository
Multiple Git hosting providers create their own friction. GitHub uses one URL format and key. GitLab uses another. Bitbucket uses a third. When you're cloning repositories, the full SSH path becomes tedious to type or remember: git clone git@github.com:organisation/repository.git versus git clone git@gitlab.com:organisation/repository.git. Get the hostname wrong, and authentication fails. Use the wrong key, and you're locked out.
SSH aliases reduce this to manageable shortcuts. Instead of remembering full paths and hostnames, you define an alias like gh for GitHub, gl for GitLab, bb for Bitbucket. The config handles the rest—correct hostname, correct user, correct key.
Host gh # Alias for the remote Git repository.
HostName github.com # The hostname or IP address of the Git server.
User git # The username for the Git repository.
Port 22 # SSH port number, if it's not the default port (22).
IdentityFile ~/.ssh/github_id_rsa # The private key for authentication.
Cloning becomes trivial:
git clone ssh://gh/path/to/repo.git
The alias handles hostname and authentication. You focus on the repository path. Switch between providers by changing the alias prefix—gh for GitHub, gl for GitLab. The muscle memory stays consistent even as the underlying infrastructure changes.
SSH alias for a bastion (or any other) server
AWS hostnames are cartoonishly long: ec2-192-168-0-1.us-east-1.compute.amazonaws.com. Try typing that accurately multiple times per day. Add in the username, the specific PEM file for that instance, perhaps a non-standard port, and you're looking at commands that span multiple lines or require copy-pasting from documentation.
SSH aliases collapse this to something memorable. ssh bastion instead of the full incantation. The config file remembers the hostname, username, and key. You remember one word.
Host bastion # Alias for the bastion server.
HostName ec2-192-168-0-1.us-east-1.compute.amazonaws.com # The hostname or IP address of the bastion server.
User myuser # The username on the bastion server.
IdentityFile ~/.ssh/bastion.pem # The private key to authenticate to the bastion server.
Connection becomes:
ssh bastion
Two words. The entire AWS hostname, username, and PEM file abstracted away. This pattern applies to any server—production instances, staging environments, database servers, monitoring hosts. Each gets a memorable alias. The config handles complexity.
SSH alias for a server behind a jump host
Internal infrastructure commonly sits behind bastion hosts for security—no direct internet exposure, all access funnelled through a hardened jump server. The traditional approach requires two SSH commands: connect to the bastion, then from there connect to the internal server. You're managing two sessions, two authentications, often with different keys for each hop.
The ProxyJump directive eliminates this friction. You specify the bastion as a jump host in your config, and SSH handles the two-hop connection transparently. You type ssh www and SSH automatically connects through the bastion to reach the internal server. One command. One authentication on your end. The complexity remains in the config where it belongs.
Host www # Alias for the internal web server.
HostName 169.254.1.1 # The hostname or IP address of the web server.
User webadmin # The username on the web server.
IdentityFile ~/.ssh/web_id_rsa # The private key to authenticate to the web server.
ProxyJump bastion # Specifies the bastion server as a jump host.
Connect directly to the internal server via the bastion:
ssh www
SSH establishes the connection to the bastion, authenticates, then immediately establishes the second connection to the internal server—all transparently. You see one authentication prompt. The rest happens behind the scenes. Your internal infrastructure remains protected whilst remaining accessible.
The .ssh/config file transforms SSH from a tool you fight with into infrastructure that stays out of your way. Complex hostnames become memorable aliases. Multiple keys map automatically to the correct servers. Agent forwarding eliminates the need to copy private keys to remote systems. Port forwarding provides secure access to services that shouldn't face the internet. ProxyJump configurations collapse multi-hop connections into single commands.
None of these techniques are complicated individually. Collectively, they represent the difference between wrestling with SSH daily and having SSH simply work. The cognitive overhead vanishes. The text file of commands you used to copy from becomes unnecessary. The risk of using the wrong key or connecting to the wrong server disappears.
Start with the servers you access most frequently. Create aliases for them. Add identity files. Configure forwarding where needed. Each configuration compounds the benefit. Within a week, you'll wonder how you managed without it. Within a month, working on a system without your SSH config will feel like working on someone else's machine—technically possible, but unnecessarily frustrating.
Published on:
Updated on:
Reading time:
7 min read
Article counts:
38 paragraphs, 1,369 words
Topics
TL;DR
The .ssh/config file eliminates SSH complexity through five core patterns. IdentityFile directives map specific keys to specific servers, preventing authentication delays and lockouts from trying wrong keys. ForwardAgent enables secure multi-hop authentication without copying private keys to intermediate servers—keys stay local whilst forwarding authentication requests. LocalForward creates encrypted tunnels for database access, allowing local tools to connect via localhost whilst traffic routes securely through SSH. Git repository aliases collapse provider-specific URLs into consistent shortcuts (gh for GitHub, gl for GitLab). ProxyJump configurations handle bastion hosts transparently, connecting to internal servers with single commands instead of managing two-hop sessions manually. These techniques compound—complex AWS hostnames become two-word commands, multiple authentication steps collapse into one, and the cognitive overhead of remembering connection details vanishes. Start with frequently accessed servers, add configurations incrementally, and within weeks SSH simply works instead of requiring constant attention.