DevOps

SSH Keys Explained: Private Git Repos vs CI/CD Deployments (The Right Way)

B
Bishal Bhattarai
January 23, 2026
5 min read
2 views

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?

ScenarioWho connectsWho must trust whom
Server pulls private Git repoServer β†’ GitHubGitHub must trust the server
CI/CD deploys to serverGitHub Runner β†’ ServerServer 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:
NameValue
EC2_SSH_KEYcontents 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 typeLives whereUsed for
Server Git keyServerPull private repo
CI/CD keyGitHub SecretsDeploy to server
authorized_keysServerTrust 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


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