There is a tiny moment of tension in every SSH setup: two nearly identical filenames sit beside each other, and a form asks you to paste “the key.” This is the moment worth getting right. The file ending in .pub is meant to travel to GitLab. The private file beside it is the proof of identity and stays with you.

Once that boundary is clear, the rest is a calm chain of trust: inspect what already exists, create a key only when needed, let an agent remember its passphrase, add the public half to the intended account, verify GitLab’s server identity, then test which account answered.

Two files, two very different promises

  • id_ed25519: the private key. Keep it on the device, protect it with filesystem permissions and preferably a passphrase, and never paste it into GitLab.

  • id_ed25519.pub: the public key. Add its complete one-line contents to GitLab.

  • The optional comment at the end helps humans identify the key; it is not used as your GitLab login.

  • The fingerprint is a compact identifier derived from a key. Compare fingerprints when names alone are ambiguous.

Look before generating another identity

Terminalbash
ssh -V
find ~/.ssh -maxdepth 1 -type f \( -name '*.pub' -o -name 'config' \) -print
OpenSSH_9.6p1 Ubuntu-3ubuntu13.18, OpenSSL 3.0.13
/home/you/.ssh/id_ed25519.pub
/home/you/.ssh/config

Why this inspection is deliberately narrow

  • ssh -V writes its version to standard error on many OpenSSH builds; the displayed version is from the local Ubuntu validation environment.

  • find is limited to ~/.ssh, one level deep, and names ending in .pub; it does not print secret file contents.

  • An existing public key may already be suitable, but first learn which account, device, and purpose it belongs to.

  • A public key must uniquely map to one GitLab user. For a new device, a separate pair limits the damage if that device is lost.

Create an ED25519 key with a name you will recognize

Terminalbash
ssh-keygen -t ed25519 -C "work-laptop-2026" -f ~/.ssh/gitlab_work_ed25519
Generating public/private ed25519 key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/you/.ssh/gitlab_work_ed25519
Your public key has been saved in /home/you/.ssh/gitlab_work_ed25519.pub

Risk level: caution. Review the command before running it.

The prompts are part of the security design

  • GitLab recommends ED25519 for most systems. Some FIPS environments may require a supported alternative such as RSA with at least 4096 bits.

  • -C stores a human-readable comment in the public key; use a device or purpose label that remains helpful after staff changes.

  • -f chooses explicit filenames and avoids overwriting a default key. If the path already exists, stop and inspect it rather than confirming replacement.

  • A passphrase protects the private key at rest. It is not sent to GitLab; ssh-agent can cache the unlocked identity for a session.

  • This command creates credential files, so it is marked caution. The article’s validation used a disposable path and an empty test-only passphrase, never a production key.

Check the fingerprint without exposing the private key

Terminalbash
ssh-keygen -lf ~/.ssh/gitlab_work_ed25519.pub
awk '{print $1, $3}' ~/.ssh/gitlab_work_ed25519.pub
256 SHA256:5uOudge50nktZjc1pNyE7trIskSoZdRo0TGJLV40My4 work-laptop-2026 (ED25519)
ssh-ed25519 work-laptop-2026

Keep the sample fingerprint in the sample

  • ssh-keygen -l prints a fingerprint; -f selects the public-key file.

  • The SHA256 value shown here belongs to the disposable documentation key and will not match yours.

  • The awk command prints only the first and third public-key fields—the algorithm and comment—while omitting the encoded key body.

  • GitLab later shows the uploaded public fingerprint, giving you a safe value to compare.

Let the SSH agent hold the unlocked key

Terminalbash
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/gitlab_work_ed25519
ssh-add -l
Agent pid 12345
Enter passphrase for /home/you/.ssh/gitlab_work_ed25519:
Identity added: /home/you/.ssh/gitlab_work_ed25519 (work-laptop-2026)
256 SHA256:YOUR_FINGERPRINT work-laptop-2026 (ED25519)

The agent remembers access, not ownership

  • ssh-agent creates a process and exports connection variables into the current shell through eval.

  • ssh-add asks for the local passphrase and loads the private key into that agent; it does not upload the private key.

  • ssh-add -l lists fingerprints of loaded identities, which is safer than displaying their contents.

  • Desktop environments often manage an agent already. Starting a second one may work only in that terminal, so inspect your platform’s keychain integration when keys disappear after reboot.

Paste only the public line into GitLab

Terminalbash
cat ~/.ssh/gitlab_work_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... work-laptop-2026

A safe copy has three recognizable parts

  • Copy the complete single line: algorithm, full encoded public key, and optional comment.

  • The real line is much longer than the shortened example. Never paste the literal ellipsis into GitLab.

  • Confirm the filename ends in .pub before copying.

  • Clipboard contents can be read by other applications on some systems; paste promptly and replace sensitive clipboard content afterward when your environment warrants it.

In GitLab, open your avatar → Edit profile → Access → SSH keys → Add new key. Paste the public line, give it a device-specific title such as “ThinkPad T14 — work,” choose a usage type, and set an expiration date that matches your organization’s key-rotation policy.

  • Choose Authentication when the key should only access Git over SSH.

  • Choose Signing when it should only verify SSH-signed commits or tags.

  • Authentication & Signing permits both and is GitLab’s default; use it only when you intend both roles.

  • GitLab checks uploaded keys against known compromised keys and rejects a known-compromised public key.

  • One account-level authentication key can access all projects the account is authorized to use; it does not grant new project membership.

Trust the server before asking it to trust you

On the first connection, SSH may show a host fingerprint. That fingerprint identifies the GitLab server—not your personal key. Compare it through a trusted channel before accepting it. GitLab.com publishes its current SSH host-key fingerprints; a self-managed instance exposes them on its instance-configuration help page or through your administrator.

Ask GitLab which account answered

Terminalbash
ssh -T git@gitlab.com
Welcome to GitLab, @your-username!

Read the username, not just the welcome

  • -T disables pseudo-terminal allocation; GitLab uses SSH for Git transport rather than a general shell session.

  • The SSH username is normally git, while the welcome message identifies the GitLab account bound to the accepted public key.

  • A successful authentication test does not prove that account has permission to a particular project.

  • For a self-managed instance, replace the hostname and confirm whether its administrator changed the default SSH username or port.

Make one repository use one explicit identity

~/.ssh/configtext
Host gitlab-work
  HostName gitlab.com
  User git
  IdentityFile ~/.ssh/gitlab_work_ed25519
  IdentitiesOnly yes

Give a work identity an SSH host alias when several GitLab keys share one machine.

The alias becomes part of the remote URL

  • Host gitlab-work defines a local alias; DNS still resolves the real HostName gitlab.com.

  • IdentityFile points to the private key locally. Never commit this configuration if it exposes personal paths or infrastructure details.

  • IdentitiesOnly yes tells SSH to offer the configured identities instead of every key an agent happens to hold.

  • Use a remote such as git@gitlab-work:YOUR_NAMESPACE/project.git, then verify it with git remote -v.

  • For several accounts on one GitLab instance, GitLab recommends host aliases because a public key can map to only one user.

When GitLab still says no

  • `Permission denied (publickey)`: run ssh-add -l, confirm the key appears in the intended GitLab account, and check which hostname the remote uses.

  • SSH asks for `git@gitlab.com`’s password: public-key authentication did not succeed. GitLab is not asking for your web password; inspect the key and agent setup.

  • Too many authentication failures: an agent may be offering many identities. Use IdentitiesOnly yes and an explicit IdentityFile.

  • Wrong GitLab username welcomes you: another account owns the offered public key. Use distinct keys and host aliases.

  • Private key permissions rejected: GitLab troubleshooting guidance recommends mode 600 for a private key and 700 for ~/.ssh on Unix-like systems.

  • Key expired: an expired GitLab SSH key can no longer authenticate or sign. Generate and add a replacement rather than changing your system clock.

  • Host identification changed: verify the new server fingerprint with GitLab or your administrator before editing known_hosts.

Terminalbash
ssh -Tv git@gitlab.com

Verbose output belongs in a private diagnostic window

  • -v reports configuration selection, host negotiation, and identities offered; repeat it (-vv or -vvv) only when more detail is necessary.

  • Look for the effective hostname, identity filenames, “Offering public key,” and GitLab’s accept or reject response.

  • Debug output can expose usernames, local paths, hostnames, network addresses, and fingerprints. Redact it before sharing.

  • This command diagnoses a connection; it does not change keys or GitLab settings.

Retire keys with the same care used to create them

Review the GitLab SSH-key list periodically. Titles, fingerprints, creation dates, last-used dates, and expiry dates help identify stale devices. Revoke a compromised key promptly; remove keys for retired machines; generate separate replacements rather than copying one private key casually between laptops.

The reassuring part of SSH is not that passwords disappear. It is that the most powerful half of the credential never needs to cross the network. Keep that private half private, verify both identities in the conversation, and the small ritual becomes dependable.

Sources and the next useful step