---
title: 'Hosting your own Git "server"'
date: "2022-09-30T14:27:31+02:00"
author: "$HUMANOID"
tags: ["git", "ssh"]
description: "The basis of a Git server is a machine accessible in a way that Git understands with a bunch of bare repositories"
toc: true
---

# Introduction

Pretty much the moment I started to work with Git a few years ago, I've wanted to host my own server.
I didn't really bother looking into how that could work for a very long time.
I assumed it would be rather complex considering Git itself is an extremely complex (set of) tool(s) to work with.
I was hilariously wrong.
It's _so_ simple to me that I'm under the impression that the lack of posts regarding the topic on blogs like this, is because it's so simple.
Still, I'm going to spend the rest of this post explaining how to set up a server and why even calling a basic configuration a server, is a bit of an exaggeration.

# Setting up the server

The first step is to set up a Unix box of any kind.
Linux, any of the BSDs and probably even Darwin will work (hell, even Haiku might suffice).
The rest of this post assumes a Debian system (or rather, that you have the core GNUtils available).
Basics requirements are quite few:

- It needs to be accessible using SSH.
- It needs Git installed.

## Adding an account

Common practice dictates that you have a dedicated account for managing your Git repositories.
Common practice again dictates that this account be called `git`.
Its home directory doesn't really matter, but I like tossing it into `/srv/git`.
Login shell also doesn't matter _yet_, I tend to change it to `bash` for when I need to do things as the `git` user.

You can create an account with the following:
```sh
sudo useradd git -md /srv/git -s /bin/bash
```
- `-d` specifies where the home directory should be
- `-m` creates the home directory
- `-s` specifies what binary to use as login shell

## Setting up SSH

### Client side

On the machine from where you want to push your git repositories, generate a new SSH key to access your Git server:
```sh
ssh-keygen -t ed25519 -f ~/.ssh/<git-key>
```
I also recommend creating an entry in `~/.ssh/config` for your Git server:
```ssh_config
Host <host>-git
	Hostname <hostname>
	User git
	Identityfile ~/.ssh/<git-key>
```

### Server side
Next step is to make the `git` account accessible over SSH.
`ssh-copy-id` won't work because the `git` user doesn't have a password.
So instead, log in as the `git` user and create a `.ssh` directory with an `authorized_keys` file with the public half of the `<git-key>` keypair:
```sh
sudo su - git
umask 077 # strict read write permissions
mkdir ~/.ssh
cat <git-key>.pub >> ~/.ssh/authorized_keys
```

## Adding a repository

You should now be able to log into your host as the `git` user with using `<git-key>`.
Setting up the route from client to server is half the work.

Next step.
Make this actually serve Git repositories.
All this requires is a bare Git repository somewhere within `/srv/git`.
To do this, log in as the `git` user and run:
```sh
git init --bare <repo-name>.git
```

That's it.

You now have a Git "server" with a repo named `<repo-name>`.

To add something to this repo, on your client machine create a new repository and add `<host>-git:/srv/git/<repo-name>.git` as remote and push your project to it.
Common practice dictates that this remote be called `origin`:
```sh
mkdir my-fancy-project
cd my-fancy-project
git init
git remote add origin <host>-git:/srv/git/<repo-name>.git
echo "# Self-hosted repo!" > README.md
git add -A
git commit -m "Initial commit"
git push -u origin main # assuming you use main as your default branch name
```

To clone this repository on another machine, add the `<git-key>` SSH keypair and related configuration section in `~/.ssh/config` to said machine and run:
```sh
git clone <host>-git:/srv/git/<repo-name>.git
```

# Automation

Creating bare repositories by hand gets annoying quite quickly.
I have an SSH forced command for another key that runs a script with the following:
```sh
#!/bin/sh

BASELOC="/srv/git"
echo "Enter new repo name:"
read REPONAME
[ -z $REPONAME ] && echo "No repo initialised" && exit 1
git init --bare $BASELOC/$REPONAME.git
```
`authorized_keys` entry for `git` user:

```authorized_keys
command="/srv/git/.bin/new-repo" ssh-ed25519 <new-repo-git-key>.pub
```

Another thing you could do is change the login shell binary to a script that does something similar.
I haven't bothered to do so for reasons that presently escape me.
