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:

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:

sudo useradd git -md /srv/git -s /bin/bash

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:

ssh-keygen -t ed25519 -f ~/.ssh/<git-key>

I also recommend creating an entry in ~/.ssh/config for your Git server:

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:

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:

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:

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:

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:

#!/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:

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.