PowerCloud PowerCloud Contact Us

Remove Alibaba Cloud identity link How to Set Up a Private Git Repository on Cloud Servers

Alibaba Cloud / 2026-05-21 23:00:58

Introduction: Private Git, Not Private Feelings

Private Git repositories are like putting your house keys in a lockbox instead of tossing them under the doormat. The lockbox isn’t glamorous, but it prevents chaos, late-night searching, and that special kind of panic where you discover your “private” repository is, in fact, public in a way that makes lawyers sigh.

In this article, we’ll set up a private Git repository on cloud servers. We’ll cover secure access, repository layout, server hardening basics, backup strategies, and the usual troubleshooting gremlins. You’ll end up with a setup that’s reliable enough for everyday development and paranoid enough to keep unwanted visitors out.

We’ll assume you want something like: developers push code to your server; the server stores it securely; and only authorized people can access it. We’ll focus on a typical “SSH + bare repository” approach, because it’s fast, standards-based, and doesn’t require complicated OAuth circus performances.

What “Private Git on Cloud” Actually Means

“Private” can mean different things depending on how your brain interprets reality. Let’s align expectations:

  • Not publicly reachable: Your Git server should not be open to the entire internet. Ideally, it listens on SSH only and you restrict inbound traffic.
  • Authenticated access: People must prove identity, usually with SSH keys.
  • Authorization: Not every user should have full access to every repository.
  • Integrity and backups: Your code should not be one accidental “rm -rf” away from becoming a cautionary tale.
  • Logging: You should be able to tell who did what and when.

Cloud servers are just servers. They don’t magically know you want privacy; you have to configure them like an adult who understands locks.

Choosing an Approach: Bare Repos with SSH (The Workhorse)

The most common non-enterprise setup is:

  • Install Git on the server.
  • Create a `bare` repository (a repo without a working directory).
  • Configure SSH access so only authorized users can push.
  • Optionally use read-only clones for some users.

This approach is simple and doesn’t require running a full Git hosting platform (like GitLab or Gitea). You can still add more structure later, but for many teams, this is plenty.

There are alternatives:

  • Full platforms: GitLab/Gitea provide UI, permissions, issues, CI integration, etc. More setup, more services, more moving parts.
  • HTTPS with credentials: Works, but managing tokens/credentials on servers and clients tends to be fiddly and can invite avoidable security footguns.
  • Managed hosting services: AWS CodeCommit, Azure DevOps repos, GitHub Enterprise, etc. Convenient, but sometimes you want control, and cloud cost accounting loves surprising you.

We’ll proceed with the workhorse method.

Prerequisites

Before you start, gather a few ingredients:

  • A cloud server (or VM) running a Linux distribution (Ubuntu/Debian are friendly, but the concept works elsewhere).
  • SSH access to the server as an admin user.
  • A domain name or at least the server IP (optional but helpful).
  • SSH key pairs for developers (or at least one key pair to test).
  • An understanding that backups are not optional, they’re just delayed responsibility.

Also, decide on your repository naming convention now, because you’ll be naming directories at 2 a.m. later otherwise.

Step 1: Provision the Cloud Server (and Don’t Leave the Door Wide Open)

In your cloud provider’s console, create a new virtual machine. Consider the following:

  • Region: Choose a region close to your team for lower latency.
  • Size: Small is often enough for Git storage and basic pushes. CPU matters less than you’d think; network and disk matter more.
  • Security group / firewall: Allow inbound SSH (port 22) from only your IP range or specific VPN/subnet. If you allow 22 to the entire internet, you are basically speedrunning “bot attack simulator.”
  • Disk: Ensure enough storage for repository growth and backups. Git repos grow quietly until they don’t.

After provisioning, log in using SSH. Do yourself a favor: update packages immediately.

Step 2: Update System Packages

On the server, run:

sudo apt-get update
sudo apt-get upgrade -y

If you’re on a different distro, the commands vary, but the idea stays the same: keep your system patched, because attackers love outdated software the way raccoons love trash.

Step 3: Install Git

Install Git:

sudo apt-get install -y git

Confirm it’s installed:

git --version

At this point, your server can understand Git’s language. Now we teach it what to do with it.

Step 4: Create a Dedicated Git User (Recommended)

Remove Alibaba Cloud identity link Create a user that owns the repositories. This user doesn’t need a shell for day-to-day development. Think of them as a quiet librarian who never asks you how your day is going.

Example using a user called `git`:

sudo adduser --disabled-login --gecos '' git

Some admins prefer creating a separate user per repository, but starting with one `git` user is common and easy. If you want stronger per-repo isolation, we’ll discuss it later.

Create a directory for bare repositories, for example:

sudo mkdir -p /srv/git
sudo chown git:git /srv/git
sudo chmod 755 /srv/git

Your repositories will live under `/srv/git`. This is clean, predictable, and friendly to future you.

Remove Alibaba Cloud identity link Step 5: Create a Bare Repository

A bare repository stores the history and references without a working tree. That’s perfect for servers because you don’t need a checked-out directory for pushing.

Create the bare repo. Suppose your repo is called `my-private-project`:

sudo -u git git init --bare /srv/git/my-private-project.git

Set the correct ownership:

sudo chown -R git:git /srv/git/my-private-project.git

Now you have an empty repository ready to receive pushes.

Step 6: Set Up SSH Access with Keys

The most important part of private Git access is authentication. You do not want passwords in the middle. SSH keys are the grown-up solution.

Generate SSH Keys (On the Developer Machine)

Remove Alibaba Cloud identity link On a developer machine, generate a key pair if one doesn’t exist:

ssh-keygen -t ed25519 -C "developer@yourteam"

When prompted, accept the default location unless you have a reason not to. If you set a passphrase (recommended), you’ll be prompted when using the key, which is annoying but safer. Keys without passphrases are like leaving your door locked but your windows open.

Copy the Public Key to the Server

There are multiple ways. The simplest: append the public key to the authorized keys for the git user.

First, print the public key:

cat ~/.ssh/id_ed25519.pub

Then, on the server:

sudo -u git mkdir -p /home/git/.ssh
sudo -u git chmod 700 /home/git/.ssh

Now append the key. Replace the string with the developer’s actual public key:

echo "ssh-ed25519 AAAA... developer@yourteam" | sudo tee -a /home/git/.ssh/authorized_keys > /dev/null
sudo -u git chmod 600 /home/git/.ssh/authorized_keys
sudo chown -R git:git /home/git/.ssh

Test SSH login:

ssh git@your-server-ip

You might not be able to get a shell because we disabled login or restricted the user. That’s fine. We’re aiming for Git over SSH, not interactive Linux tourism.

Step 7: Create an SSH Transport for Git

Now the repo exists, and the keys exist. But will Git know what to do when someone pushes? Usually, the `git@host:repo-path` syntax maps to the server-side repository path.

The most common server-side format is:

ssh://git@your-server-ip/srv/git/my-private-project.git

Or the scp-like syntax:

git@your-server-ip:/srv/git/my-private-project.git

On the developer machine, set the remote accordingly.

Step 8: Initialize the Repo Locally and Push

On a developer machine, start a local repo in your project folder:

git init
git add .
git commit -m "Initial commit"

Now add the remote:

git remote add origin git@your-server-ip:/srv/git/my-private-project.git

Push to the server:

git push -u origin master

If you’re using a newer Git default branch, it might be `main` instead of `master`. Use:

git branch --show-current

Then push the correct branch:

git push -u origin main

Your commit should now exist on the server.

Clone it elsewhere to verify:

git clone git@your-server-ip:/srv/git/my-private-project.git

If this works, congratulations: your code has crossed the internet moat successfully.

Step 9: Lock Down SSH and Repository Permissions

If you set up SSH keys and created a repository owned by `git`, you’ve already done a lot right. But there are still a few important security details to check.

Restrict SSH Users (Basic Hardening)

Open the SSH daemon config, usually:

sudo nano /etc/ssh/sshd_config

Look for or add these settings:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AllowUsers your-admin-username git

Restart SSH after changes:

sudo systemctl restart ssh

Be careful with `AllowUsers`—if you lock yourself out, you’ll be debugging from the cloud provider console like it’s a horror movie. Test changes with a second session if possible.

Ensure Correct Directory Permissions

Your repository directories should be owned by `git`, and the `git` user should have permissions to write into them.

Typically:

sudo chown -R git:git /srv/git
sudo chmod -R 755 /srv/git

Inside bare repositories, Git uses internal permission structures. Usually you don’t need to modify them beyond ownership. The goal is: only trusted users can push via SSH, not via random filesystem access.

Step 10: Handling Multiple Repositories and Users

Once the first repo works, you can create additional repos. For example:

sudo -u git git init --bare /srv/git/another-project.git

Permissions and access can get tricky when multiple teams or people exist. You have a few options.

Option A: One Git User, Shared Access (Simple, Less Granular)

All authorized developers get their SSH public keys into the same `authorized_keys` file for the `git` user. Then they can push to all repos under `/srv/git` (unless you implement additional restrictions).

This is easiest and works fine for small teams who share responsibility. It’s less ideal if different teams shouldn’t see each other’s repos.

Option B: Separate Git Users Per Repository (More Isolation)

Create users like `git-project1` and `git-project2`. Each user owns one repository directory. Each repo user has its own `authorized_keys` file.

Example:

sudo adduser --disabled-login --gecos '' git-project1
sudo mkdir -p /srv/git
sudo mkdir -p /srv/git/my-private-project.git
sudo chown -R git-project1:git-project1 /srv/git/my-private-project.git
sudo -u git-project1 git init --bare /srv/git/my-private-project.git

Then add keys to `/home/git-project1/.ssh/authorized_keys`.

This gives you per-repository control without running a full hosting platform. It’s like giving each cat its own bowl—less fighting, fewer surprises.

Option C: Deploy Keys / Limited Access (Careful, Useful)

Deploy keys are common in hosting platforms. In raw SSH + bare repo setups, you can approximate limited access by using restricted commands in SSH (not always trivial) or by controlling which keys have access to which user/repo.

As a rule: if you need real per-repo read/write control, consider a lightweight Git hosting tool. Otherwise, separate users per repo is the most straightforward path.

Step 11: Add a Read-Only Mode (If You Need It)

Sometimes you want some people to clone and fetch but not push. With the simple authorized-keys method, any key in the `git` user can often push. Read-only enforcement requires additional configuration.

The most common technique is to use separate users for read-only access and make them unable to write. But Git over SSH needs server-side permissions to allow `git-receive-pack` (for pushes). Disabling those for certain users can be done with SSH forced commands and wrappers. This is powerful but can be finicky.

If you do need strict read-only permissions, consider a platform like Gitea or GitLab, or implement restricted SSH command rules. In this guide, we’ll keep it practical: start with a permission model where authorized keys correspond to the ability to push, and then upgrade if your requirements grow claws.

Step 12: Backups (The Part Everyone Plans to Do Later)

Backups are not sexy. They’re also not optional. Git repos can recover from a lot, but not from missing disks, ransomware, or an admin accidentally deleting `/srv/git` because the command looked short enough.

For bare repos, a straightforward backup is copying the repo directories. Options:

  • File-level backup: rsync or snapshots from the filesystem level.
  • Remove Alibaba Cloud identity link Cloud volume snapshots: Take periodic snapshots of the disk.
  • Git-level backup: Run `git bundle` exports periodically.

File-level example with rsync to another server (with appropriate permissions):

sudo rsync -a --delete /srv/git/ backupuser@backup-server:/backup/git/

Better: use snapshots or scheduled tasks. The exact tooling depends on your cloud provider.

Also test restore. A backup you can’t restore is just a stress-inducing slideshow.

Remove Alibaba Cloud identity link Step 13: Monitoring and Auditing Access

To keep your repository truly private, you want to know who connected and when. SSH logs usually record authentication events.

On many systems, you’ll find logs in:

/var/log/auth.log

Check the logs periodically or set up alerting. Keep it simple: if you see repeated failed logins from suspicious IPs, tighten firewall rules. If you see pushes from unexpected users, investigate quickly.

Even in a private setup, people do dumb things. Logging helps you find out who did the dumb thing and whether it was you, your teammate, or a bot with free time.

Step 14: Troubleshooting Common Errors

Here are the classic problems that show up when setting up private Git over SSH, along with what they usually mean.

“Permission denied (publickey)”

Meaning: SSH didn’t accept your key.

  • Remove Alibaba Cloud identity link Check that the correct public key is in the right user’s `authorized_keys` file.
  • Confirm you’re connecting as the right SSH user (e.g., `git@host`).
  • Verify permissions on `/home/git/.ssh` and `authorized_keys` (commonly 700 for .ssh and 600 for authorized_keys).
  • Make sure SSH uses your intended key. You can try: `ssh -v git@your-server-ip` to view verbose output.

Remove Alibaba Cloud identity link “Repository not found” or “fatal: ‘...’ does not appear to be a git repository”

Meaning: the remote path is wrong, or the bare repo wasn’t created at that location.

  • Confirm the repository path exists: `/srv/git/my-private-project.git`.
  • Confirm the repo is bare (contains `HEAD`, `objects`, etc.).
  • Double-check your remote URL syntax.

“fatal: Could not read from remote repository”

Meaning: either permissions/SSH authentication failed, or the SSH connection succeeded but Git cannot access the repo.

  • Try cloning with verbose mode from the client: `GIT_SSH_COMMAND='ssh -v' git clone ...`.
  • Check server logs and repository ownership.
  • Make sure the repo directory is accessible to the SSH user.

Host Key Verification Changed

Meaning: the server’s SSH host key changed. This can happen if you reinstalled the server or swapped instances.

To fix legitimately:

  • Remove the old host key entry on your client (be careful; this is security-related).
  • Remove Alibaba Cloud identity link Reconnect to accept the new host key if you trust the server.

If you don’t trust the server, stop and investigate. If you trust it, proceed with caution.

Push works but clone doesn’t (or vice versa)

Meaning: you might have inconsistent permissions or SSH restrictions.

  • Check that the SSH user can read the repo directory.
  • Check that pushing permissions are granted (write access to objects/refs).
  • Confirm your remote URL used for clone is correct.

Step 15: Improving Developer Experience

Once the system works, you can polish it so developers don’t file “why is this annoying” tickets.

Use SSH config on developer machines

Create (or edit) `~/.ssh/config`:

Host my-git-server
  HostName your-server-ip
  User git
  IdentityFile ~/.ssh/id_ed25519

Then your remote becomes simpler:

git remote add origin my-git-server:/srv/git/my-private-project.git

This avoids repetitive host/IP typing and reduces mistakes.

Use consistent branch naming

Decide on `main` or `master`. Then set the default branch accordingly in your team workflow. This avoids the “why does my push create a weird new branch” ritual.

Scaling Up: When You’ll Outgrow DIY Git Hosting

Your DIY server setup is great until you want features like:

  • Web UI for browsing code
  • Issue tracking
  • CI integrated with the repo
  • Fine-grained per-user permissions
  • Pull request workflows
  • LDAP/SAML integration

When that happens, you can migrate to a lightweight Git hosting tool like Gitea (simple, smaller footprint) or a full solution like GitLab. Migration often involves backing up repos and restoring them into the new system.

But don’t fear a future migration. The important thing now is having a stable, secure repository foundation. Future you will still be future you, not a panicked archaeologist.

Checklist: A Quick “Did We Do the Basics?” Review

  • Server provisioned with restricted inbound SSH firewall rules
  • System updated with security patches
  • Git installed
  • Dedicated repository user created (e.g., `git`)
  • `/srv/git` created and owned by the repository user
  • Bare repository created with `git init --bare`
  • SSH keys installed in the correct user’s `authorized_keys`
  • SSH hardened: root login disabled, password auth disabled
  • Permissions verified for repository directories
  • Backups scheduled and restore tested
  • Monitoring in place for SSH access and repository activity

If you can check most of these, your repository is already in a far better state than the typical “it’s private because I deleted the old link” approach.

Conclusion: Your Code’s New Fortress

Setting up a private Git repository on cloud servers isn’t magic. It’s mostly careful configuration: create bare repos, control access with SSH keys, lock down permissions, and don’t forget backups. Once it’s working, it tends to stay working, which is the dream scenario in IT where the only surprise should be productivity gains.

Whether you start with a single `git` user or move to per-repository users, the core idea remains the same: the cloud stores your code, but you store the control. Now go forth and push commits with confidence, knowing that the wrong people won’t be “cloning” your repository like it’s a public buffet.

TelegramContact Us
CS ID
@cloudcup
TelegramSupport
CS ID
@yanhuacloud