Kubernetes
Git
SSH Keys
Secrets Management
DevOps

How to clone a private git repository into a kubernetes pod using ssh keys in secrets?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Cloning a private Git repository inside Kubernetes is usually done with an SSH deploy key stored as a Secret and mounted into a pod or init container. The secure part is not just storing the key, but also handling known_hosts, file permissions, and repository checkout in a way that does not expose the key to the main application container longer than necessary.

Prefer an init container

The cleanest pattern is:

  1. mount the SSH key and known_hosts from a Secret
  2. run git clone in an init container
  3. write the checked-out repository into a shared volume
  4. let the main container read the files without needing Git credentials

This is safer than putting the SSH key in the main application container.

Create the Secret

You normally store the private key and known_hosts together. A deploy key with read-only repository access is better than reusing a personal SSH key.

bash
kubectl create secret generic git-ssh \
  --from-file=ssh-privatekey=$HOME/.ssh/id_ed25519 \
  --from-file=known_hosts=$HOME/.ssh/known_hosts

If the secret key names do not match what the pod expects, mount them with explicit item mappings.

Pod pattern with init container

The following manifest clones the repo into an emptyDir volume:

yaml
1apiVersion: v1
2kind: Pod
3metadata:
4  name: repo-consumer
5spec:
6  volumes:
7    - name: repo-data
8      emptyDir: {}
9    - name: git-ssh
10      secret:
11        secretName: git-ssh
12        defaultMode: 0400
13        items:
14          - key: ssh-privatekey
15            path: id_ed25519
16          - key: known_hosts
17            path: known_hosts
18  initContainers:
19    - name: git-clone
20      image: alpine/git:2.45.2
21      env:
22        - name: GIT_SSH_COMMAND
23          value: ssh -i /root/.ssh/id_ed25519 -o UserKnownHostsFile=/root/.ssh/known_hosts
24      command:
25        - /bin/sh
26        - -c
27        - |
28          mkdir -p /root/.ssh
29          cp /secrets/id_ed25519 /root/.ssh/id_ed25519
30          cp /secrets/known_hosts /root/.ssh/known_hosts
31          chmod 600 /root/.ssh/id_ed25519
32          git clone [email protected]:example/private-repo.git /workspace/repo
33      volumeMounts:
34        - name: repo-data
35          mountPath: /workspace
36        - name: git-ssh
37          mountPath: /secrets
38          readOnly: true
39  containers:
40    - name: app
41      image: busybox:1.36
42      command: ["sh", "-c", "ls -la /workspace/repo && sleep 3600"]
43      volumeMounts:
44        - name: repo-data
45          mountPath: /workspace

This keeps the SSH material out of the main container's long-running filesystem layout.

Why known_hosts matters

Do not disable host key checking casually with options such as StrictHostKeyChecking=no unless this is a throwaway development environment. A proper known_hosts file protects against man-in-the-middle mistakes and makes the SSH trust boundary explicit.

You can generate the host entry ahead of time:

bash
ssh-keyscan github.com >> ~/.ssh/known_hosts

Then put that file into the Secret.

Repository updates versus one-time cloning

If the repository content changes over time, decide whether the pod should:

  • clone once at startup
  • pull periodically
  • rebuild the image instead of cloning at runtime

In many production systems, building the code into the container image is better than cloning at runtime. Runtime cloning is more common for jobs, tooling pods, or internal automation that truly needs dynamic repository access.

Security and RBAC considerations

A Secret is only part of the solution. Also verify:

  • the namespace access policy limits who can read the Secret
  • the key has least-privilege repository access
  • the pod service account does not have unnecessary permissions
  • logs do not print the SSH command or private key path contents

If multiple repositories are needed, use separate deploy keys rather than one broad key whenever possible.

Common Pitfalls

The most common mistake is mounting only the private key and forgetting known_hosts, which causes SSH verification failures or encourages insecure workarounds. Another is cloning in the main application container, leaving the SSH key present longer than needed. File permissions are another frequent problem because SSH refuses to use overly permissive private key files. Teams also often reuse personal developer keys instead of dedicated deploy keys, which is risky and harder to rotate. Finally, many people use runtime cloning when the better design is to build source artifacts into the image during CI.

Summary

  • Use a Kubernetes Secret for the SSH private key and known_hosts.
  • Prefer an init container that clones into a shared volume.
  • Keep file permissions strict so SSH accepts the mounted key.
  • Use deploy keys with least privilege instead of personal SSH credentials.
  • Treat runtime cloning as a deliberate operational choice, not the default.
  • Consider baking source or built artifacts into the image when that fits the deployment model better.

Course illustration
Course illustration

All Rights Reserved.