I Moved All My SSH Keys Into 1Password. Here's How.
My SSH keys were a mess.
Five key pairs scattered across ~/.ssh/, three of them actively used, two left over from jobs I no longer work at. A config file with host aliases I wrote months ago and never documented. Private keys sitting as plain files on disk, protected by nothing more than filesystem permissions.
It worked. It had worked for years. But "it works" is not the same as "it's good," and every time I set up a new machine or onboarded onto a new team, I spent an hour copying key files around, triple-checking permissions, and praying I didn't accidentally overwrite the wrong one.
So I moved everything into 1Password.
Why Now
Two things happened at the same time.
First, I started a new role that required a separate GitHub account. That meant a third active SSH key, a third host alias in my SSH config, and a third mental mapping of "which key goes where." The complexity wasn't unmanageable, but it was trending in the wrong direction.
Second, I was already using 1Password for everything else. Passwords, API tokens, 2FA codes, secure notes. My SSH keys were the last piece of sensitive data living as raw files on my filesystem. The inconsistency bothered me.
The final push: when setting up the new role's SSH key, I used 1Password to generate it. The key was born inside the vault. The private key never touched my disk. Setting up that one key through 1Password's SSH agent was so clean that it made the other two keys, sitting as files in ~/.ssh/, feel like relics.
What 1Password's SSH Agent Actually Does
If you already understand SSH key pairs and agents, skip ahead. If not, here's the short version.
SSH authentication works with key pairs: a private key (secret) and a public key (shared). When you connect to GitHub, your machine proves it holds the private key without actually sending it. The public key is registered on your GitHub account. GitHub checks the math and lets you in.
Normally, those private keys live as files in ~/.ssh/. An SSH agent holds them in memory so you don't have to type passphrases repeatedly. On macOS, the built-in ssh-agent does this, optionally storing passphrases in Keychain.
1Password replaces that agent entirely. Instead of files on disk, your private keys live inside your 1Password vault. When SSH needs to sign something, it asks the 1Password agent, which prompts you for biometric auth (Touch ID on Mac), then signs. The private key never leaves the vault. It never exists as a file on your filesystem.
The mechanism:
- 1Password exposes a Unix socket that speaks the SSH agent protocol
- Your
~/.ssh/configtells SSH to use that socket viaIdentityAgent - You keep
.pubfiles locally so SSH knows which key to request from the agent - 1Password handles the rest
The Multi-Key Problem
Here's where it gets interesting if you work across multiple GitHub accounts.
GitHub enforces a strict rule: one SSH public key per account. You cannot register the same key on two accounts. If you have a personal GitHub and a work GitHub, you need separate keys.
The classic solution is SSH host aliases. All your repos point to github.com, but you define fake hostnames in your SSH config:
Host github.com
IdentityFile ~/.ssh/personal-key
Host github.com-work
HostName github.com
IdentityFile ~/.ssh/work-key
Then your git remotes use the alias:
# Personal repo
git@github.com:myuser/my-project.git
# Work repo
git@github.com-work:company/project.git
SSH sees the hostname, picks the right key, and GitHub authenticates you as the right account. This works perfectly well with traditional key files. Moving to 1Password doesn't change the pattern; it just changes where the private key lives.
The Setup
Here's what my ~/.ssh/config looks like after migration:
Host github.com
HostName github.com
User git
IdentityAgent "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock"
IdentityFile ~/.ssh/personal-key.pub
IdentitiesOnly yes
Host github.com-work
HostName github.com
User git
IdentityAgent "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock"
IdentityFile ~/.ssh/work-key.pub
IdentitiesOnly yes
Three things to notice:
IdentityAgent points to 1Password's Unix socket. This tells SSH "don't use the system agent, talk to 1Password instead."
IdentityFile points to .pub files, not private keys. SSH uses the public key to tell the agent which private key to sign with. The private key stays in 1Password.
IdentitiesOnly yes prevents SSH from trying other keys. Without this, SSH might offer every key the agent has, which defeats the purpose of per-project routing.
The old config had AddKeysToAgent yes and UseKeychain yes, both of which are for macOS's native keychain integration. Those get removed since 1Password handles that role now.
Per-Project Git Identity
SSH key routing handles authentication: which GitHub account you connect as. But git also needs to know your name, email, and signing key for commits. If you use different identities for personal and work projects, git's includeIf directive handles this cleanly.
In ~/.gitconfig:
[user]
name = Your Name
email = personal@email.com
signingkey = ssh-ed25519 AAAA...your-personal-key...
[gpg]
format = ssh
[gpg "ssh"]
program = "/Applications/1Password.app/Contents/MacOS/op-ssh-sign"
[commit]
gpgsign = true
[includeIf "gitdir:~/code/work/"]
path = ~/.gitconfig-work
And in ~/.gitconfig-work:
[user]
name = Your Name
email = you@company.com
signingkey = ssh-rsa AAAA...your-work-key...
Any repo under ~/code/work/ automatically uses the work identity. Everything else uses the default. The signing key references the same key that's in 1Password, so op-ssh-sign can find it and prompt for Touch ID.
Commit Signing for Free
This was an unexpected bonus.
1Password ships a binary called op-ssh-sign that acts as a drop-in replacement for GPG when signing git commits. Once you point gpg.ssh.program at it, every commit gets signed via 1Password. Touch ID prompt, signed commit, green "Verified" badge on GitHub.
I had GPG-based commit signing before. It worked, but GPG key management is its own circle of hell: key expiration, subkeys, the gpg-agent, remembering to export your public key. SSH-based signing through 1Password collapses all of that into something that just works.
You do need to add your SSH public key to GitHub as a signing key (separate from the authentication key). GitHub's SSH key settings have two sections: "Authentication keys" and "Signing keys." Add the same public key to both.
The Migration Process
If you're doing this yourself, here's the sequence that worked:
1. Import existing keys into 1Password. Open 1Password, create a new SSH Key item, choose "Import," and select your private key file. Do this for each key. 1Password derives the public key automatically.
2. Verify the agent serves your keys. After importing, check:
SSH_AUTH_SOCK=~/Library/Group\ Containers/2BUA8C4S2C.com.1password/t/agent.sock ssh-add -l
You should see fingerprints for all imported keys. If a key doesn't appear, toggle the SSH agent off and on in 1Password's Developer settings. The agent caches its key list and sometimes needs a nudge after importing.
3. Update ~/.ssh/config. Add the IdentityAgent line to each host block. Change IdentityFile to point to the .pub file instead of the private key.
4. Move private keys off disk. Once everything works through 1Password, move your private key files to a backup location. The .pub files stay; SSH needs them to identify which key to request from the agent.
5. Test every context. Run ssh -T git@github.com (and each host alias) to verify authentication. Then git fetch in a real repo from each context. Don't skip this.
6. Set up commit signing. Update ~/.gitconfig with the gpg.format, gpg.ssh.program, and user.signingkey entries. Test with a throwaway commit.
Gotchas
A few things that tripped me up:
The agent doesn't hot-reload. If you import a new key, the 1Password SSH agent might not pick it up until you toggle it off and on (Settings > Developer > Use the SSH agent). This is the kind of thing that makes you question your sanity for twenty minutes.
File permissions matter, even for .pub files. SSH is strict. If your .pub file has a private key file sitting next to it (same name without .pub), SSH can get confused about which to load. Clean separation: private keys in 1Password (and backup), public keys in ~/.ssh/.
The agent.toml config file. 1Password uses ~/.config/1Password/ssh/agent.toml to control which keys the agent serves. By default, all keys in your Private vault are enabled. If you use multiple vaults, you may need to add explicit entries.
IdentitiesOnly yes is non-negotiable. Without it, SSH may offer all keys from the agent to the server. With multiple GitHub accounts, offering the wrong key first means authentication succeeds as the wrong user. Silent, confusing, and hard to debug.
Cross-machine sync is automatic. 1Password syncs your vault across devices. The private keys follow you. But you still need to replicate the ~/.ssh/config and .pub files on each machine, and 1Password needs to be installed with the SSH agent enabled.
What Stays Outside 1Password
Not everything should go through the 1Password agent. My Mac-to-Mac SSH connection (I SSH between a MacBook and a Mac Mini on my home network) still uses a traditional key pair. Adding a biometric prompt every time I SSH into my own machine across the room would be friction without benefit. Local network, local key.
The Honest Trade-offs
What's better:
- Private keys never exist as files on disk (for keys generated by 1Password) or can be removed from disk (for imported keys)
- Biometric auth on every SSH operation. Nobody uses your keys without your fingerprint.
- Cross-machine sync through 1Password's vault. No more
scp-ing key files to new machines. - Commit signing works out of the box. No GPG key management.
- One fewer category of secrets to manage manually.
What's worse:
- You depend on 1Password. If 1Password is down, locked, or uninstalled, your SSH stops working. Keep backup keys somewhere safe.
- Touch ID prompts on every git push. Some people will find this annoying. I find it reassuring.
- The agent occasionally needs a restart after importing new keys. Not a dealbreaker, but not the polish you expect from 1Password.
- Debugging is harder. When SSH fails, you're debugging the interaction between SSH, the 1Password agent, and your config file. Three moving parts instead of two.
What doesn't change:
- Git remotes stay the same. No repo URLs need updating.
- The host alias pattern stays the same. You're just changing where the private key lives, not how SSH picks it.
- Performance is identical. The signing happens locally in the 1Password process.
Was It Worth It
Yes. Without hesitation.
The setup took about an hour, most of which was debugging the agent not picking up newly imported keys. Once it worked, it just worked. Every git push triggers a Touch ID prompt, the right key gets used for the right context, and my private keys are no longer sitting in a folder protected by nothing more than Unix permissions.
If you manage multiple SSH keys and already use 1Password, this is one of those changes that makes you wonder why you didn't do it sooner.