Specify a Private SSH Key for Git, and Show or Change the Remote URL

Git does not choose an SSH key itself. It runs ssh, and ssh picks the key. So when you need a work key for one repository and a personal key for another, you tell ssh which key to use: for one command, for one repository, or for one host name. The host name is part of the remote URL, so the key and the URL are often one problem. This page covers both: how to choose the key, and how to show and change the URL a repository uses.
TLDR
| Question | Short answer |
|---|---|
| Use a key for one command | GIT_SSH_COMMAND='ssh -i ~/.ssh/work_ed25519 -o IdentitiesOnly=yes' git fetch |
| Use a key for one repository | git config core.sshCommand 'ssh -i ~/.ssh/work_ed25519 -o IdentitiesOnly=yes' |
| Use a key for a host or an account | A Host alias in ~/.ssh/config with IdentityFile and IdentitiesOnly yes |
Why IdentitiesOnly? |
Without it, ssh can offer a key from the agent first, and you log in as the wrong account |
| Which URL was this repository cloned from? | git remote get-url origin, or git remote -v for all remotes |
| Change the remote URL | git remote set-url origin <new-url> |
Prerequisites
- Git and OpenSSH. The commands work on Linux, macOS and Git for Windows. The tests on this page used Git 2.39 and OpenSSH 9.2.
- An SSH key that the hosting service knows. If you need a new one, see create and test the key below.
How Do I Specify a Private SSH Key for Git?
Choose by scope:
- One command: set
GIT_SSH_COMMANDin the environment. - One repository: set
core.sshCommandin the repository config. - One host or account: add a
Hostentry to~/.ssh/config, and use that host name in the remote URL.
In all three, add IdentitiesOnly. The section below explains why.
Use a key for one command with GIT_SSH_COMMAND
GIT_SSH_COMMAND='ssh -i ~/.ssh/work_ed25519 -o IdentitiesOnly=yes' \
git clone [email protected]:acme/api.git
Git runs this command instead of plain ssh. The variable applies only to that one command. export it to use it for the rest of the shell session, and unset GIT_SSH_COMMAND to stop.
GIT_SSH_COMMAND wins over core.sshCommand. In a test with both set, Git ran the command from the environment variable.
Use a key for one repository with core.sshCommand
cd ~/work/api
git config core.sshCommand 'ssh -i ~/.ssh/work_ed25519 -o IdentitiesOnly=yes'
Every fetch, pull and push over SSH in this repository now uses that key. HTTPS remotes do not use SSH, so the setting does not apply to them. To set it at clone time, pass it with -c. Git writes it into the config of the new repository before the first fetch:
git clone -c core.sshCommand='ssh -i ~/.ssh/work_ed25519 -o IdentitiesOnly=yes' \
[email protected]:acme/api.git
Check or remove the setting with git config --get core.sshCommand and git config --unset core.sshCommand.
Use a key for all repositories in a folder
Put the setting in a separate file, and include it only for repositories below ~/work/:
# ~/.gitconfig
[includeIf "gitdir:~/work/"]
path = ~/.gitconfig-work
# ~/.gitconfig-work
[core]
sshCommand = ssh -i ~/.ssh/work_ed25519 -o IdentitiesOnly=yes
[user]
email = [email protected]
Git 2.36 and later can also match on the remote URL instead of the folder. This rule applies to every repository with a remote in the acme organization, wherever you cloned it:
[includeIf "hasconfig:remote.*.url:[email protected]:acme/**"]
path = ~/.gitconfig-work
In a test, a repository with the remote [email protected]:acme/app.git got the work core.sshCommand, and a repository with [email protected]:me/dotfiles.git did not. git config --show-origin core.sshCommand shows which file set the value.
Use a key for a host with ~/.ssh/config
This is the best choice when you have two accounts on the same service. Give each account its own host alias:
# ~/.ssh/config
Host github.com-work
HostName github.com
User git
IdentityFile ~/.ssh/work_ed25519
IdentitiesOnly yes
Host github.com-personal
HostName github.com
User git
IdentityFile ~/.ssh/personal_ed25519
IdentitiesOnly yes
Then use the alias in place of github.com in the URL:
git clone [email protected]:acme/api.git
git remote set-url origin [email protected]:acme/api.git # for an existing clone
ssh -G prints the settings that ssh will use for a host, without a connection. Use it to check your config:
ssh -G github.com-work | grep -E '^(hostname|user|identityfile|identitiesonly) '
user git
hostname github.com
identitiesonly yes
identityfile ~/.ssh/work_ed25519
If you have only one key for each service, you do not need aliases. Use the real host names in the Host lines (Host github.com, Host gitlab.com), and the normal URLs keep working.
Why do I need IdentitiesOnly?
Because ssh can offer other keys before the one you named. When an SSH agent is running, OpenSSH tries keys in this order:
- Keys in the agent that are also named in your config or on the command line.
- All other keys in the agent.
- Key files from your config or the command line that are not in the agent.
IdentitiesOnly yes (or -o IdentitiesOnly=yes) removes the second group. It does not remove keys that your ~/.ssh/config names for the same host, for example in a Host * block. ssh -G <host> | grep identityfile lists every key file that ssh will try.
This matters on GitHub and GitLab, because the server accepts the first key that belongs to any account. If your personal key is in the agent and your work key is not, ssh -i ~/.ssh/work_ed25519 can log you in as your personal account. The push then fails with an error that looks like a URL problem:
ERROR: Repository not found.
fatal: Could not read from remote repository.
ssh -vT [email protected] shows each key that ssh offers, in order.
How Do I Create and Test an SSH Key for Git?
Create an Ed25519 key, add the public key to your account, and test the connection:
ssh-keygen -t ed25519 -C "[email protected]" -f ~/.ssh/work_ed25519
cat ~/.ssh/work_ed25519.pub # paste this into the SSH keys page of your account
ssh -T [email protected]
Hi jane-acme! You've successfully authenticated, but GitHub does not provide shell access.
The name in the greeting is the account that the key belongs to. If it is the wrong account, check IdentitiesOnly.
- Use Ed25519 for new keys. GitHub is removing SHA-1
ssh-rsasignatures, and new RSA keys need at least 3072 bits. GitHub Is Removing ssh-rsa Signatures shows how to test an existing key. sshrefuses a private key that other users can read (Permissions 0644 ... are too open). Fix it withchmod 600 ~/.ssh/work_ed25519andchmod 700 ~/.ssh.- Give the key a passphrase, and load it once a session with
ssh-add ~/.ssh/work_ed25519.ssh-add -llists the keys in the agent.
The remotes chapter of the Git guide covers SSH and HTTPS authentication, and the SSH hardening checklist covers the server side.
How Do I Use a Deploy Key in a Script or CI?
Use a key that can read only that one repository (a deploy key), and check the host key of the server. Do not turn off host key checks with StrictHostKeyChecking=no: then anyone who can intercept the connection can pretend to be the server.
#!/usr/bin/env bash
set -euo pipefail
DEPLOY_KEY="$HOME/.ssh/deploy_api_ed25519"
KNOWN_HOSTS="$HOME/.ssh/known_hosts_github" # created once, see below
export GIT_SSH_COMMAND="ssh -i $DEPLOY_KEY -o IdentitiesOnly=yes -o UserKnownHostsFile=$KNOWN_HOSTS -o StrictHostKeyChecking=yes"
if [ -d /srv/api/.git ]; then
git -C /srv/api fetch origin
git -C /srv/api reset --hard origin/main # the deploy copy holds no local work: this discards any change in it
else
git clone [email protected]:acme/api.git /srv/api
fi
echo "Deployed $(git -C /srv/api rev-parse --short HEAD)"
Create the known hosts file once, and compare the fingerprints with the ones the service publishes, such as GitHub's SSH key fingerprints:
ssh-keyscan -t ed25519 github.com > ~/.ssh/known_hosts_github
ssh-keygen -lf ~/.ssh/known_hosts_github # compare with the published fingerprint
-o StrictHostKeyChecking=accept-new is a middle way: ssh saves the key of an unknown host on the first connection and refuses a changed key later. The first connection is still not checked.
core.sshCommand is a command that Git runs. A config file you did not write, for example in a repository that someone sent you, can use it to run code. A Poisoned .git/config Runs Code on git status tests which commands and copies carry it.
How Do I See Which URL a Repository Was Cloned From?
Run git remote get-url origin. origin is the name git clone gives to the remote you cloned from. git remote -v lists every remote with its fetch and push URL:
git remote get-url origin
# [email protected]:jane/api.git
git remote -v
# origin [email protected]:jane/api.git (fetch)
# origin [email protected]:jane/api.git (push)
# upstream [email protected]:acme/api.git (fetch)
# upstream [email protected]:acme/api.git (push)
In a fork, origin is usually your fork and upstream the original project. How to sync a forked repository shows that setup.
Some details:
git config --get remote.origin.urlprints the raw value.git remote get-urlandgit remote -vprint the URL that Git really uses, afterurl.<base>.insteadOfrules (see below). In a test with aninsteadOfrule, the config showedhttps://github.com/acme/app.git, andgit remote get-urlshowed[email protected]:acme/app.git.- Git does not keep a history of the URL. If someone ran
git remote set-urlafter the clone, the first URL is gone. git remote show originalso lists the remote branches and whatgit pullandgit pushdo. It connects to the remote, so it fails when you are offline. Rungit remote show -n originto skip the connection.- A repository made with
git inithas no remote until you add one withgit remote add origin <url>.
In a script, the repository name is the last part of the URL:
url=$(git remote get-url origin)
name=$(basename -s .git "$url")
echo "$name" # api
How Do I Change the URL of a Git Remote?
Run git remote set-url, then check it:
git remote set-url origin [email protected]:acme/api.git
git remote -v
git ls-remote origin HEAD # proves that the new URL and your credentials work
The common reasons:
- HTTPS to SSH (or back):
git remote set-url origin [email protected]:acme/api.git, orhttps://github.com/acme/api.git. - The repository moved to a new owner, name or host. GitHub redirects the old URL after a rename or transfer, but update your remotes anyway: the redirect stops working if someone creates a new repository with the old name.
- A second account on the same host: use the host alias from
~/.ssh/configin the URL.
Use one URL to fetch and another to push
--push changes only the push URL:
git remote set-url --push origin [email protected]:acme/api.git
git remote -v
origin https://github.com/acme/api.git (fetch)
origin [email protected]:acme/api.git (push)
Change many URLs at once
Rewrite a URL prefix for every repository with url.<base>.insteadOf. This rule sends every https://github.com/ URL over SSH, and the remotes themselves do not change:
git config --global url."[email protected]:".insteadOf "https://github.com/"
To change the stored URLs in many clones, loop over them. This loop moves SSH remotes from the old-org organization to new-org:
for repo in ~/work/*/; do
old=$(git -C "$repo" remote get-url origin) || continue
new=${old/github.com:old-org\//github.com:new-org/}
[ "$old" != "$new" ] && git -C "$repo" remote set-url origin "$new" && echo "$repo -> $new"
done
Add, rename and remove remotes
git remote add upstream https://github.com/acme/api.git # a second remote
git remote rename origin old-origin # rename
git remote remove old-origin # delete the remote and its remote-tracking branches
Fix credential errors after the change
An SSH URL needs an SSH key on your account (see above). An HTTPS URL on GitHub needs a personal access token or a credential manager, not your password. Use a credential helper that stores the token safely: Git Credential Manager, osxkeychain on macOS, or cache on Linux. Do not use credential.helper store: it writes the token in plain text to ~/.git-credentials.
To make Git forget a saved HTTPS credential, so that it asks again:
printf 'protocol=https\nhost=github.com\n\n' | git credential reject
Related Resources
- Introduction to Git: Remotes, Push, Fetch and Pull
- GitHub Is Removing ssh-rsa Signatures
- How to push a new branch and set the upstream
- SSH hardening checklist
- Sign Git commits with an SSH key, from the Cryptography Essentials guide
- Git concepts simulator: see how remotes and remote-tracking branches relate
- Test yourself: Git Command Quiz
Try it hands-on
Run the commands from this article in the browser. Nothing to install.
Git Concepts Simulator
Practice Git in an interactive browser terminal with a visual repository model. Learn the working directory, staging area, local commits, branch pointers, remotes, fast-forward merges, fetch, and rebase.
Git Merge Conflict Simulator
Resolve Git merge conflicts in a browser lab. Read the conflict markers and the three index stages, edit the file to what it should be, take a side with --ours or --theirs, see why they swap during a rebase, back out with git merge --abort, and catch a clean merge that breaks the build.
We earn commissions when you shop through the links below.
Bitrise
Managed runners for GitHub Actions
Bitrise Build Hub runs your GitHub Actions jobs on M4 Macs and AMD Linux machines. Change runs-on and keep your workflows, secrets and logs on GitHub.
Atomsized
AWS platform engineering and GitOps
Design and automation for reliable AWS and Kubernetes platforms, safer delivery workflows, and preview and UAT environments your engineers can understand and own.
DigitalOcean
Cloud infrastructure for developers
Simple, reliable cloud computing designed for developers
DevDojo
Developer community & tools
Join a community of developers sharing knowledge and tools
SMTPfast
Developer-first email API
Send transactional and marketing email through a clean REST API. Detailed logs, webhooks, and embeddable signup forms in one dashboard.
QuizAPI
Developer-first quiz platform
Build, generate, and embed quizzes with a powerful REST API. AI-powered question generation and live multiplayer.
Want to support DevOps Daily and reach thousands of developers?
Become a SponsorFound an issue?
Related Posts
Also worth your time on this topic
GitHub Is Removing ssh-rsa Signatures: Find What Breaks Before the November 4 Brownout
GitHub removes the ssh-rsa signature type and diffie-hellman-group-exchange-sha256 on January 13, 2027, with brownouts on November 4 and December 9. Learn which clients and keys are at risk, which debug lines to read, and how to rotate keys before the first brownout.
SSH Basics and Key Authentication
How does SSH key authentication work? How do you set it up?
junior
Securing Your Linux Server: Essential First Steps
Harden a fresh Ubuntu 24.04 server by creating a non-root user, locking down SSH, configuring a firewall with UFW, and setting up fail2ban to block brute-force attacks.
45 minutes