SSH Keys Explained: Private Git Repos vs CI/CD Deployments (The Right Way)
SSH Keys Explained: Private Git Repos vs CI/CD Deployments (The Right Way)
When working with servers, SSH keys solve two very different problems. Most confusion happens because people try to use the same key or same mental model for both.
This guide explains both cases clearly:
- Pulling a private Git repository directly from a server
- Deploying code to a server using CI/CD (GitHub Actions)
Same technology (SSH), different trust models, different keys, different responsibilities.
Mental Model (Before We Touch Commands)
Always ask one simple question:
Who is connecting to whom, and who must trust whom?
| Scenario | Who connects | Who must trust whom |
|---|---|---|
| Server pulls private Git repo | Server β GitHub | GitHub must trust the server |
| CI/CD deploys to server | GitHub Runner β Server | Server must trust GitHub |
Once this clicks, SSH stops being confusing.
1οΈβ£ Pulling a Private Git Repository From the Server
Use case
Your server needs to run:
git pull
on a private GitHub repository.
This means:
- The server is the SSH client
- GitHub is the server being authenticated against
So GitHub must trust the server.
Correct approach
π Generate an SSH key on the server
π Tell GitHub to trust that key
Step 1: Generate SSH key on the server
On the server (as the deploy user, e.g. logiccraft):
ssh-keygen -t ed25519 -C "logiccraft-server"
Press Enter for:
- file location
- passphrase (optional)
This creates:
~/.ssh/id_ed25519 (private key)
~/.ssh/id_ed25519.pub (public key)
The private key never leaves the server.
Step 2: Add the public key to GitHub (Deploy Key)
Copy the public key:
cat ~/.ssh/id_ed25519.pub
On GitHub:
- Repo β Settings
- Deploy Keys
- Add Deploy Key
- Paste the public key
- Enable Read access (or Write if needed)
β GitHub now trusts this server.
Step 3: Test from the server
ssh -T git@github.com
git pull
If it works β youβre done.
Key takeaway (important)
- This key lives on the server
- GitHub trusts the server
- Used only for
git pull/git fetch - Never used for deployments
2οΈβ£ CI/CD Deployments (GitHub Actions β Server)
This is a different problem.
Use case
GitHub Actions needs to run:
ssh logiccraft@your-server
in order to deploy code automatically.
Here:
- GitHub runner is the SSH client
- Your server must trust GitHub
Correct approach
π Generate one SSH key locally (no passphrase)
π Add its public key to the server
π Store its private key in GitHub Secrets
Step 1: Generate CI/CD key on your laptop
On your local machine:
ssh-keygen -t ed25519 -f ~/.ssh/logiccraft_ci_nopass -N ""
This creates:
logiccraft_ci_nopass (private key)
logiccraft_ci_nopass.pub (public key)
β No passphrase β required for automation.
Step 2: Add public key to the server
Copy the public key:
cat ~/.ssh/logiccraft_ci_nopass.pub
On the server:
mkdir -p ~/.ssh
chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys
Paste the public key on a new line, then:
chmod 600 ~/.ssh/authorized_keys
β The server now trusts GitHub Actions.
Step 3: Add private key to GitHub Secrets
On GitHub:
- Repo β Settings
- Secrets and variables β Actions
- Add secret:
| Name | Value |
|---|---|
| EC2_SSH_KEY | contents of logiccraft_ci_nopass |
β οΈ Never upload the public key here
β οΈ Never commit the private key
Step 4: Use it in GitHub Actions
Inside your workflow:
cat > ~/.ssh/deploy_key << 'KEY'
${{ secrets.EC2_SSH_KEY }}
KEY
chmod 600 ~/.ssh/deploy_key
ssh -i ~/.ssh/deploy_key logiccraft@SERVER_IP
GitHub can now deploy safely.
Security Summary (Why This Is Safe)
| Key type | Lives where | Used for |
|---|---|---|
| Server Git key | Server | Pull private repo |
| CI/CD key | GitHub Secrets | Deploy to server |
| authorized_keys | Server | Trust CI runner |
Each key has one responsibility.
Common Mistakes to Avoid
β Using the same key everywhere
β Uploading private keys to servers
β Adding CI keys as GitHub deploy keys
β Using root for deployments
β Using passphrases for CI keys
Recommended Production Setup
Server:
βββ ~/.ssh/id_ed25519 β GitHub deploy key
βββ ~/.ssh/authorized_keys β CI/CD public key
GitHub:
βββ Deploy Keys β server public key
βββ Secrets (Actions) β CI private key
Clean. Secure. Scalable.
Final Thoughts
SSH is not hard β itβs context-sensitive.
Once you separate:
- who is connecting
- who must trust whom
Everything becomes obvious.
This setup is:
- production-ready
- secure
- industry standard
- easy to rotate or revoke
If you understand this distinction, youβll never mix SSH keys again.
Related Posts
SSH Config Explained: How to Simplify Server Access with a Clean Laptop Setup
Learn how to use an SSH config file on your laptop to simplify server access, avoid mistakes, and connect to servers like LogicCraft with ease.
SSH Keys Deep Dive: Public vs Private, How Authentication Really Works
A practical deep dive into SSH keysβwhat public and private keys really are, how authentication works, and how to generate, store, and use keys safely.
SSH Key Rotation and Revocation Strategies for Production Systems
Learn how to rotate and revoke SSH keys safely in production without downtime, broken deployments, or accidental lockouts.