> ## Documentation Index
> Fetch the complete content index at: https://smallsharpsoftwaretools.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Run Your Own Git Remote with SSH


You don't need a hosting service to share code with your team or back up your work. A plain Linux server and SSH can do the job. Your server, your repositories, no third party in the middle. And when the big hosting providers have a rough day, which they sometimes do, your remote keeps working.

Before you start, set your expectations. This gives you a place to push and pull code. That's it. You won't get a website, pull requests, issues, or automated builds. If you need those, a hosted service is still the right tool. But if you just want a shared remote you control, you can set one up in a matter of minutes.

## What You need

To complete this tutorial, you need:

* A Linux server you can already reach over SSH, with access to configure users and services.
* Your public SSH key on the machine you'll push from. If you don't have a key yet, GitHub's [guide to generating one](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent) works fine no matter where you plan to use it.

## Create a `git` user and repositories on the remote machine

You're going to create a dedicated `git` user and an empty repository on the remote machine. A remote repository should be "bare," which means it has no working copy of the files. It only stores the history that people push and pull. Trying to push to a normal repository with a working copy causes problems, so bare is what you want on a server.

Log in to your Linux server over SSH with your regular user.

Make a dedicated account that owns the repositories. A separate user keeps your repositories tidy and limits the damage if a key ever leaks. Later, you'll ensure that nobody can log in as the `git` user to poke around the system. You'll make sure they can only push and pull.

Create the account with no password, which blocks password logins right away:

```command
sudo adduser --disabled-password --gecos "" git
```

This creates a normal user named `git` with a home directory at `/home/git`. You'll use that home directory in the next steps.

Create a folder to hold your repositories, owned by the `git` user:

```command
sudo -u git mkdir -p /home/git/repos
```

Now create your first repository.

```command
sudo -u git git init --bare /home/git/repos/project.git
```

The `.git` suffix on the folder name is a common convention for bare repositories. It's not required, but it makes them easy to spot.

## Add SSH keys so people can authenticate.

People will connect using their SSH keys, so the server needs to know which keys to trust. Those go in a file called `authorized_keys` inside the `git` user's `.ssh` folder.

Set up the folder and file with the right permissions. First, create the folder itsself:

```command
sudo -u git mkdir -p /home/git/.ssh
```

Then apply permissions, giving the `git` user full access:

```command
sudo -u git chmod 700 /home/git/.ssh
```

Then create the `authorized_keys` folder:

```command
sudo -u git touch /home/git/.ssh/authorized_keys
```

Finally, set permissions so the `git` user can read and write to the file:

```command
sudo -u git chmod 600 /home/git/.ssh/authorized_keys
```

SSH is picky about these permissions. If they're too open, SSH won't use the file, so don't skip the `chmod` steps.

Now add a public key to the file. Grab the contents of your `.pub` key file and append it to `authorized_keys`. For example, if your public key is `id_rsa.pub`, run the following command:

```command
cat ~/.ssh/id_rsa.pub | sudo -u git tee -a /home/git/.ssh/authorized_keys
```

Each line in the `authorized_keys` file is one public key. If you want more people to connect, you'll have to get their keys and add them.

Typing keys by hand gets old once you have a few people to add. GitHub can help here. Every GitHub user's public keys are available at a URL based on their username. Visit `https://github.com/{username}.keys` in your browser, swap in a real username, and you'll see that user's public keys.

You can use that to script the whole thing. Start with a text file of GitHub usernames, one per line. Call it `github-users.txt`:

```plain
alice
bob
carol
```

Then create a script that reads the file, fetches each person's keys, and writes them all to `authorized_keys`:

```bash
#!/usr/bin/env bash
set -euo pipefail

USER_FILE="${1:-github-users.txt}"
GIT_HOME="/home/git"
AUTH_KEYS="$GIT_HOME/.ssh/authorized_keys"
TMP="$(mktemp)"

while read -r username || [ -n "$username" ]; do
  [ -z "$username" ] && continue
  case "$username" in \#*) continue ;; esac
  curl -fsSL "https://github.com/${username}.keys" \
    | sed "s/$/ github:${username}/" >> "$TMP"
done < "$USER_FILE"

sudo install -d -m 700 -o git -g git "$GIT_HOME/.ssh"
sudo install -m 600 -o git -g git "$TMP" "$AUTH_KEYS"
rm -f "$TMP"
```

The script tags each key with the GitHub username it came from, so you can tell whose key is whose later. It also sets the correct permissions and ownership for you.

This script replaces `authorized_keys` every time it runs. The username file is now the single source of truth. Don't hand-edit `authorized_keys` after this, or your changes disappear the next time you run the script. To add or remove someone, edit `github-users.txt` and run it again.

## Lock the `git` Account Down with `git-shell`

Right now the `git` user can log in and get a full shell. That means anyone whose key is in `authorized_keys` can run any command on your server, not just Git commands. That's more access than you want to hand out.

Git ships with a tool called [git-shell](https://git-scm.com/docs/git-shell) that fixes this. It lets people run Git commands over SSH but blocks everything else. No real shell, no poking around.

Find where `git-shell` lives, add it to the list of valid login shells, and set it as the `git` user's shell:

```command
which git-shell | sudo tee -a /etc/shells
```

```command
sudo chsh git -s "$(which git-shell)"
```

Now the `git` account can only move git data in and out.

From the machine you'll push from, try connecting over SSH with the `git` user:

```command
ssh git@server
```

Instead of a prompt, you'll receive a message like the following:

```output
fatal: Interactive git shell is not enabled.
Connection to server closed.
```

That message verifies you can't log in with this user.

## Push Your First Repository

To push code to your remote server, add it as a remote on your project.

Navigate to your project and set up `git` if you haven't already:

```command
cd /path/to/project
```

Initialize the repository:

```command
git init
```

Add files to commit:

```command
git add .
```

Then commit the files:

```command
git commit -m "initial commit"
```

Now point your project at the server by adding a remote. The path is relative to the `git` user's home directory, so `repos/project.git` finds the bare repository you created:

```command
git remote add origin git@server:repos/project.git
```

Push your changes:

```command
git push -u origin main
```

Your history is now on your own server. The `-u` flag sets it as the default remote, so from now on you can just run `git push` and `git pull`.

## Clone Your Repository on Another Machine

The whole point of a remote is that more than one person, or more than one machine, can use it. Anyone with a trusted key clones the repository the same way:

```command
git clone git@server:repos/project.git
```

That pulls down a working copy they can commit to and push back.

When you have another project you want to push to your remote server, create another bare repository on the remote and push your code to it. Log in to the server with your regular account that has `sudo` access, and run the `git init` command again with a new name:

```command
sudo -u git git init --bare /home/git/repos/another-project.git
```

Then push to it the same way you pushed the first one, using `another-project.git` as the path.

Consider backing up the repositories on your remote server. Clones on people's machines hold the history too, but don't count on that as a real backup plan. Set up something that copies `/home/git/repos` somewhere safe on a schedule.

# Conclusion

Using an SSH-based Git remote has some tradeoffs. There's no web interface, no pull requests, no issue tracking, no automated builds, and no granular user access. For a personal project or a small team that just needs a shared place to push code, this is a quick low-tech solution that works well, especially if a hosted provider is offline.

