Git Course 0%

SSH keys

Intermediate Git CLI GitLab UI GitHub UI ≈ 14 min

Before this lesson

What you will learn

  • How to generate a modern key pair and what the two files are
  • How to upload the public half and test that it works
  • What the agent and a passphrase do, and how to manage two accounts

After this lesson you can

  • I can set up SSH access on a new machine and diagnose "Permission denied (publickey)

Why this matters

An SSH key is the closest thing to "log in once and forget about it". Generating one takes thirty seconds, but the two files it makes have very different rules: one is public and meant to be pasted into websites, the other is the thing that is your identity. Mixing them up is the mistake this lesson exists to prevent.

Generate the pair

Terminal
$ ssh-keygen -t ed25519 -C "ana@northwind-trails.example"
Generating public/private ed25519 key pair.
Your identification has been saved in /Users/you/.ssh/id_ed25519
Your public key has been saved in /Users/you/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:YMp4KTlbeWOy2ELLvIbfcMl2e0oRPmBhrggl5ND78rg ana@northwind-trails.example

Three prompts appear: the file location (press Enter for the default) and a passphrase, twice. -t ed25519 picks the modern key type, which is short and fast; -C adds a comment, conventionally your email, so you can recognize the key in a list later.

Two files result, with deliberately different permissions:

Terminal
$ ls -l ~/.ssh
-rw-------@ id_ed25519
-rw-r--r--@ id_ed25519.pub
File What it is Rules
id_ed25519 The private key. Proves you are you. Never leaves your computer. Never committed, never pasted anywhere, never emailed. Readable only by you (-rw-------).
id_ed25519.pub The public key. A lock that only your private key opens. Safe to paste into GitLab, GitHub, anywhere. Publishing it gives nobody access.

The passphrase

An empty passphrase means anyone with your laptop's files has your key. A passphrase encrypts the private key on disk, so a stolen file is useless without it, and the SSH agent remembers it for the session so you type it at most once a day. Use one on any machine you carry.

Upload the public half

Terminal
$ cat ~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKOn7GbsU2ijtkxOAIdg0TSJ… ana@northwind-trails.example

Copy the whole line, from ssh-ed25519 to the comment, and paste it:

  • GitLab: avatar → Edit profileSSH keysAdd new key. Give it a title naming the machine ("work laptop"), and optionally an expiry date.
  • GitHub: avatar → SettingsSSH and GPG keysNew SSH key. Title, key type Authentication Key, paste, Add SSH key.

On macOS, pbcopy < ~/.ssh/id_ed25519.pub copies it to the clipboard; on Windows Git Bash, clip < ~/.ssh/id_ed25519.pub.

Test it

Terminal
$ ssh -T git@gitlab.com

The first connection asks you to confirm the server's fingerprint; type yes. A working key answers with a greeting by name (Welcome to GitLab, @ana!, or on GitHub Hi ana! You've successfully authenticated…) and then closes, because these servers do not give you a shell. That greeting is the whole test.

Then use the SSH URL:

Terminal
$ git clone git@gitlab.com:northwind-trails/trailguide.git

Or switch an existing clone:

Terminal
$ git remote set-url origin git@gitlab.com:northwind-trails/trailguide.git

The agent

The SSH agent holds your decrypted private key in memory so the passphrase is asked once per session. macOS and most Linux desktops start it automatically. If you are asked for the passphrase on every push:

Terminal
$ eval "$(ssh-agent -s)"
$ ssh-add ~/.ssh/id_ed25519

On macOS, ssh-add --apple-use-keychain ~/.ssh/id_ed25519 stores the passphrase in the Keychain so it survives restarts, and a matching entry in ~/.ssh/config makes it automatic:

~/.ssh/config
Host gitlab.com
  IdentityFile ~/.ssh/id_ed25519
  UseKeychain yes
  AddKeysToAgent yes

Two accounts on one machine

Work and personal accounts on the same host need one key each and a ~/.ssh/config that distinguishes them by a made-up host alias:

~/.ssh/config
Host gitlab-work
  HostName gitlab.com
  User git
  IdentityFile ~/.ssh/id_ed25519_work

Host gitlab-personal
  HostName gitlab.com
  User git
  IdentityFile ~/.ssh/id_ed25519_personal

Then clone with the alias in place of the host: git clone git@gitlab-work:northwind-trails/trailguide.git. Existing clones are repointed with git remote set-url. Pair this with a per-repository user.email (lesson 3.4) so the commits carry the right identity too.

How to do it

Terminal
$ ssh-keygen -t ed25519 -C "you@example.com"
$ cat ~/.ssh/id_ed25519.pub          # copy this, paste on the website
$ ssh -T git@gitlab.com               # expect a greeting by name
$ git remote set-url origin git@gitlab.com:group/project.git

Common mistakes

  • Permission denied (publickey). The most common message. Run ssh -T git@gitlab.com to test in isolation, then check: is the public key uploaded, is the private key present in ~/.ssh, is the agent running (ssh-add -l), and is the remote URL really SSH?
  • Uploading the private key. Delete the pair and start again; see the warning above.
  • Host key verification failed. The server's fingerprint changed or you answered no at the first prompt. Reconnect and confirm, after checking the platform's published fingerprints if you are cautious.
  • A key that works in the terminal but not in the IDE. The IDE's environment lacks the agent; use the terminal to ssh-add, or switch IntelliJ to its built-in SSH.
  • Wrong permissions. SSH refuses to use a private key that other users can read: chmod 600 ~/.ssh/id_ed25519.

Try it yourself

Goal: create a key pair, inspect both files, and understand which is which — without uploading anything.

  1. Generate a practice pair in a scratch location, so your real key is untouched: ssh-keygen -t ed25519 -C "practice" -f ~/practice_key -N "".
  2. Run ls -l ~/practice_key* and compare the permissions of the two files.
  3. Run cat ~/practice_key.pub and note that it is one line starting ssh-ed25519.
  4. Run head -2 ~/practice_key and note that it says OPENSSH PRIVATE KEY.
  5. Delete both: rm ~/practice_key ~/practice_key.pub.

Expected result: the private key is -rw------- (only you) and the public key -rw-r--r-- (anyone); the public one is a single short line, the private one is a block beginning -----BEGIN OPENSSH PRIVATE KEY-----.

Show solution

The permissions are the operating system enforcing the distinction, and the file contents make it unmistakable: if what you are about to paste begins with -----BEGIN, stop, because that is the private half. -N "" in step 1 means "no passphrase", which is fine for a throwaway key and not what you want for a real one.

Check yourself

1. Which file do you paste into GitLab or GitHub?
2. ssh -T git@gitlab.com answers Welcome to GitLab, @ana! and then disconnects. What does that mean?
3. Your push fails with Permission denied (publickey). Which check is not useful?

Key terms

Clone Push Remote