GitLab 03 🦊 Managing SSH Keys and Personal Access Tokens (PATs)
GitLab supports two primary methods for authenticating Git operations and API requests: SSH keys and Personal Access Tokens (PATs). SSH keys are the preferred method for Git operations because they provide a secure, passwordless workflow after the initial setup. PATs are the mechanism for authenticating against the GitLab API, and they serve as the password substitute when cloning over HTTPS, since GitLab no longer supports account passwords for Git over HTTPS . Understanding the distinction is critical: SSH keys authenticate the machine to the Git server, while PATs authenticate an application or script to the GitLab API and the HTTPS Git endpoint.
Key point: One SSH key authenticates you to every project and group your account can access. You do not need a separate key for each project . PATs are scoped tokens with expiration dates. In GitLab 16.0 and later, new PATs must have an expiry date, defaulting to 365 days if not specified .
Why SSH keys and PATs exist
The password elimination problem. GitLab removed support for account passwords over HTTPS for Git operations. A developer who attempts to git clone over HTTPS with their account password will fail. The two replacements are SSH keys and PATs. SSH keys are the recommended path for interactive development because they are configured once and then work silently . PATs are the fallback when SSH is blocked by a firewall or when the workflow requires HTTPS .
The key management problem. An SSH key pair consists of a public key and a private key. The public key is uploaded to GitLab. The private key stays on the local machine and is never transmitted. The public key cannot be used to derive the private key, so uploading it is safe . The private key must be protected; if it is compromised, an attacker can authenticate as the user.
The token scoping problem. A PAT is not a master key. It is created with specific scopes (api, read_user, read_repository, write_repository, etc.). A token scoped only to read_repository cannot push code. This limits the damage if the token is leaked. The fine-grained PAT model, generally available in GitLab 19.2, goes further: the token is scoped to specific resources and permissions, and the token’s permissions are intersected with the user’s own permissions at request time .
The expiration problem. Tokens that never expire are a security liability. GitLab 16.0 made expiration mandatory for new PATs. The default is 365 days. GitLab sends email notifications at 7 days, 30 days, and 60 days before expiration . The expiration calendar (/-/user_settings/personal_access_tokens.ics) can be subscribed to for advance warning.
The trade-off. SSH keys are more convenient for daily Git operations but require key management (generation, rotation, revocation). PATs are more flexible for automation and API access but require scope selection and expiration management. A developer typically uses both: SSH for Git, PATs for API scripts and CI pipelines.
a. Generating and adding an SSH key
The first step is checking for an existing key pair. The .ssh/ directory in the home folder may already contain id_ed25519 or id_rsa. If a key pair exists, it can be reused . If not, a new pair is generated with ssh-keygen.
# Generate an ED25519 key (preferred)
ssh-keygen -t ed25519 -C "your_email@example.com"
# For systems that require RSA
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
The ED25519 algorithm is preferred because it is more secure and performant than RSA. RSA keys, if used, should be at least 4096 bits . The -C flag adds a comment (typically the email address) to the public key, which helps identify it later.
The public key is copied to GitLab. The private key stays on the local machine. The public key is in ~/.ssh/id_ed25519.pub.
# Display the public key
cat ~/.ssh/id_ed25519.pub
The public key is added to GitLab through the user settings: Edit profile → SSH Keys → Add new key. The key is pasted into the form, and an expiration date can be set. After adding, the connection is verified with ssh -T git@gitlab.com .
# Verify the connection
ssh -T git@gitlab.com
# Expected output: Welcome to GitLab, @username!
For self-managed GitLab, the hostname is the instance’s domain (e.g., git@gitlab.example.com) rather than gitlab.com .
b. Creating and using a Personal Access Token
A PAT is created from the user settings: Edit profile → Access → Personal access tokens. The form requires a name, an expiration date, and at least one scope. The token value is displayed once and cannot be retrieved after the page is refreshed .
The scopes determine what the token can do. The api scope grants full access to the API. The read_repository and write_repository scopes are sufficient for Git operations over HTTPS. The read_user scope allows reading user information. For automation that only needs to read code, read_repository is the minimum .
A PAT can be used to clone a repository over HTTPS when SSH is not available. The token is used as the password.
# Clone using PAT (saves token in bash history)
git clone https://<username>:<personal_token>@gitlab.com/group/project.git
# Safer: avoid saving token in history
git clone https://<username>@gitlab.com/group/project.git
# When prompted for password, enter the personal token
The username in the clone command can be any non-empty string. It does not have to match the actual username . For CI/CD pipelines, the token is passed as an environment variable or stored in a CI/CD variable, never hardcoded in a script.
The PAT is also used for API requests. The token is passed in the PRIVATE-TOKEN header.
# API request with PAT
curl --header "PRIVATE-TOKEN: <your_access_token>" \
"https://gitlab.com/api/v4/projects"
c. Rotation, revocation, and fine-grained tokens
A PAT can be rotated, which generates a new token value while preserving the token’s name, scopes, and expiration date. Tools that rely on the old token stop working until they are updated with the new value. Rotation is the correct response to a suspected leak that does not require revoking the token’s permissions entirely .
Revocation immediately invalidates the token. GitLab retains the token for audit purposes; it is not permanently deleted. A revoked token cannot be used, and the tools that relied on it will fail until a new token is created .
The fine-grained PAT model, generally available in GitLab 19.2, replaces the coarse scopes (api, read_repository) with resource-specific permissions. A token can be scoped to a specific project and granted only read_repository on that project’s repository. The token’s permissions are checked against the user’s permissions on every request: the token can only perform an action if both the token and the user are authorized . This is the strongest form of PAT scoping and the recommended model for automation that accesses a limited set of resources.
Complete Example Session
# ============================================
# PART 1: CHECK FOR EXISTING SSH KEYS
# ============================================
ls ~/.ssh/
# If id_ed25519 and id_ed25519.pub exist, skip to PART 3.
# ============================================
# PART 2: GENERATE A NEW SSH KEY
# ============================================
ssh-keygen -t ed25519 -C "your_email@example.com"
# Press Enter for default path and no passphrase.
# ============================================
# PART 3: ADD THE PUBLIC KEY TO GITLAB
# ============================================
cat ~/.ssh/id_ed25519.pub
# Copy the output.
# GitLab → Edit profile → SSH Keys → Add new key
# Paste the key and save.
# ============================================
# PART 4: VERIFY THE SSH CONNECTION
# ============================================
ssh -T git@gitlab.com
# Expected: Welcome to GitLab, @username!
# ============================================
# PART 5: CLONE USING SSH
# ============================================
git clone git@gitlab.com:group/project.git
# No password prompt.
# ============================================
# PART 6: CREATE A PERSONAL ACCESS TOKEN
# ============================================
# GitLab → Edit profile → Access → Personal access tokens
# Name: ci-read-token
# Expiration: 30 days
# Scopes: read_repository
# Generate token
# Copy the token value immediately.
# ============================================
# PART 7: CLONE USING PAT (HTTPS)
# ============================================
git clone https://username@gitlab.com/group/project.git
# When prompted for password, enter the PAT.
# ============================================
# PART 8: USE PAT FOR API
# ============================================
curl --header "PRIVATE-TOKEN: <token>" \
"https://gitlab.com/api/v4/projects?membership=true"
# ============================================
# PART 9: ROTATE OR REVOKE
# ============================================
# Rotate: token page → vertical ellipsis → Rotate
# Revoke: token page → vertical ellipsis → Revoke
The nine parts trace the authentication lifecycle: key check, key generation, public key upload, SSH verification, SSH clone, PAT creation, HTTPS clone with PAT, API request with PAT, and rotation/revocation.
Quick Reference
SSH Key Types
| Algorithm | Preferred | Notes |
|---|---|---|
| ED25519 | Yes | More secure and performant |
| RSA | No | Use at least 4096 bits |
| ECDSA | No | Security concerns |
PAT Scopes
| Scope | Purpose |
|---|---|
api | Full API access |
read_user | Read user information |
read_repository | Clone/pull over HTTPS |
write_repository | Push over HTTPS |
PAT Lifecycle
| Action | Effect |
|---|---|
| Rotate | New token value, same scopes/expiry |
| Revoke | Immediate invalidation |
| Expire | Automatic at midnight UTC on expiry date |
SSH vs PAT
| Aspect | SSH Key | PAT |
|---|---|---|
| Git operations | Yes | Yes (HTTPS) |
| API requests | No | Yes |
| Passwordless | Yes (after setup) | No (token as password) |
| Expiration | Optional | Mandatory since 16.0 |
Best Practices
✅ Do This:
ssh-keygen -t ed25519 -C "email@example.com"
<!-- Use ED25519 for new keys --> # ✅
ssh -T git@gitlab.com
<!-- Verify SSH connection after adding key --> # ✅
# Set expiration on PATs
<!-- Default is 365 days if not specified --> # ✅
❌ Don’t Do This:
# Share the private key
<!-- Private key must never leave the local machine --> # ❌
# Create a PAT with the "api" scope for CI read-only
<!-- Use read_repository instead --> # ❌
# Hardcode PAT in a script
<!-- Use CI/CD variables or environment variables --> # ❌
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| SSH connection refused | Key not added to GitLab | Add public key in SSH Keys settings |
| Permission denied (publickey) | Wrong key or not loaded | ssh-add ~/.ssh/id_ed25519 |
| HTTPS clone fails | Using account password | Use PAT as password |
| PAT not working | Expired or wrong scope | Check expiration and scopes |
| Token leaked in history | Cloned with token in URL | Use the prompt-based method |
Real-World Examples
1. Generate ED25519 Key
ssh-keygen -t ed25519 -C "email@example.com"
2. Generate RSA Key
ssh-keygen -t rsa -b 4096 -C "email@example.com"
3. Display Public Key
cat ~/.ssh/id_ed25519.pub
4. Test SSH
ssh -T git@gitlab.com
5. Clone via SSH
git clone git@gitlab.com:group/project.git
6. Clone via HTTPS with PAT
git clone https://username@gitlab.com/group/project.git
7. API with PAT
curl --header "PRIVATE-TOKEN: <token>" https://gitlab.com/api/v4/user
8. Rotate PAT
Token page → vertical ellipsis → Rotate
9. Revoke PAT
Token page → vertical ellipsis → Revoke
10. Fine-grained PAT
Generate token → Fine-grained token → select resources and permissions
Visual
SSH Authentication Flow
┌──────────────────────────────────────────────┐
│ Local machine │
│ ~/.ssh/id_ed25519 (private) │
│ ~/.ssh/id_ed25519.pub (public) │
└──────────────────┬───────────────────────────┘
│
│ public key uploaded
▼
┌──────────────────────────────────────────────┐
│ GitLab (SSH Keys settings) │
│ Stores public key │
└──────────────────┬───────────────────────────┘
│
│ git push/pull
▼
┌──────────────────────────────────────────────┐
│ SSH handshake │
│ GitLab verifies signature with public key │
│ Access granted without password │
└──────────────────────────────────────────────┘
PAT Authentication Flow
┌──────────────────────────────────────────────┐
│ PAT created with scopes and expiry │
│ Value displayed once │
└──────────────────┬───────────────────────────┘
│
│ used as password or header
▼
┌──────────────────────────────────────────────┐
│ Git over HTTPS │
│ or API request │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ GitLab validates token │
│ Checks scopes, expiry, user permissions │
│ Access granted or denied │
└──────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Preferred SSH key | ED25519 |
| RSA key size | ≥ 4096 bits |
| SSH verification | ssh -T git@gitlab.com |
| PAT location | Edit profile → Access → Personal access tokens |
| PAT expiry default | 365 days |
| PAT clone password | Token value |
| Fine-grained PAT | Generally available in 19.2 |
| Rotation | Preserves scopes, new value |
Key takeaways:
- SSH keys are the preferred method for Git operations. One key pair authenticates to every project and group the user can access. ED25519 is the recommended algorithm .
- PATs replace account passwords for HTTPS. GitLab no longer supports passwords over HTTPS. A PAT is used as the password in the clone prompt or in the
PRIVATE-TOKENheader for API calls . - PATs have mandatory expiration. Since GitLab 16.0, new PATs must have an expiry date, defaulting to 365 days. Notifications are sent before expiration .
- Fine-grained PATs provide resource-level scoping. Available in GitLab 19.2, they allow a token to be limited to specific projects and specific permissions, and the token’s permissions are intersected with the user’s permissions .
- The private key is never transmitted. Only the public key is uploaded to GitLab. The private key stays on the local machine and must be protected .
Remember: SSH keys authenticate the machine for Git operations. PATs authenticate scripts and API clients. The SSH key is configured once and works silently. The PAT is created with scopes and an expiry date and must be rotated or revoked when compromised. The public key is safe to share. The private key and the PAT value are secrets.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!