Run git on your own server

For things that are not ready to be shared with the world… #git #server #vps #privacy #ssh

The goal of this text is to teach you how to use your server to create your own private git repository—and be able to share it with a selected few. It is based on a Digital Ocean tutorial, with some improvements.1

Scenario. More specifically, say you have a folder on your local machine, which may or may not be under git’s version control, and you wish to set it up under git (if that is not already the case), and create a version of it in a server you control, so that you (and others selected by you) can push and pull to/from it.

Prerequisites. A server (or VPS) that you own, and to which you have SSH access configured (in particular, access with key files is required). In what follows, I assume the server is accessible at example.com. Also, git must be installed. The following instructions were tested in Ubuntu Server, but they should work, mutatis mutandis, with other Linux distributions.

First, we deal with the server. We have to create an user to own the location where the git projects will be stored. It (the user) can be named anything, but to keep it simple, let’s name it git.2 Create like so:3

# useradd git --home-dir /srv/git --user-group --shell /bin/git-shell

Here, I use git-shell, provided by git itself, as the login shell. That basically ensures that it is not possible to login with this user (after all, its only purpose is to handle git stuff). This is not strictly true, but it is with the default configuration—it will not be possible to login with the git user (you can try it just to make sure). The --user-group ensures that a group called git will also be created (to which the user git will belong).

Now by default, the new user’s home directory will not be created if it does not exist. So let’s create it (and couple of more things), and set up permissions (ssh can make a fuss if some files have the wrong permissions).

# mkdir -p /srv/git/.ssh
# touch /srv/git/.ssh/authorized_keys
# chown -R git:git /srv/git
# chmod 700 /srv/git/.ssh/
# chmod 600 /srv/git/.ssh/authorized_keys

In that file, append the public part of your SSH key (i.e., the part with a name that ends in .pub). Be careful to not add any white space (or white lines) while in the copy/paste process.

So, the git user’s home folder is /srv/git— and I use it also as the location for the repositories. It is not strictly necessary, but it quite simplifies matters. In that location, create a bare git repository like so (git might issue a warning about the default name of the master branch, but we can just ignore it):

# git init --bare my-project.git

Don’t worry about doing this as root, it is an empty repository, which can be created without setting up any identity information (recall that if, for example, you try to commit with having set your name and email, git complains somewhat loudly). And besides, the next step is to change the ownership of this folder to the git user and group:

# chown -R git:git my-project.git

Now if you already have a local repository under git control, that you want to push to this server, do:

$ git remote set-url origin git@example.com:my-project.git

On the other hand, if you are going to create a new repository in the folder where your project resides, then after doing git init, do:

$ git remote add origin git@example.com:my-project.git

Now you can make changes and push them to the server, like so:4

$ GIT_SSH_COMMAND='ssh -i ~/.ssh/mykey -o IdentitiesOnly=yes' git push

mykey is the private key corresponding to the public key that was added to /srv/git/.ssh/authorized_keys above (the public key corresponding to mykey should be named mykey.pub).

The environment variable GIT_SSH_COMMAND must be specified for all commands that need to contact the server: besides push, there will be pull, fetch, and of course, clone (inter alia). So a more convenient approach might be to use an SSH config file (usually ~/.ssh/config), and place the following inside:

Host mygit
Hostname example.com
Port 22
User git
IdentityFile ~/.ssh/mykey
IdentitiesOnly=yes

And thus, the push command is reduced to: $ git push.5 The string in Host is arbitrary: just pick something convenient and short.6

Finally, to share access with someone else, let us call him John, ask him for an SSH key (the .pub part), and append that to the server’s /home/git/.ssh/authorized_keys file, just as done above. After that, he can clone the repository, like so:

$ GIT_SSH_COMMAND='ssh -i ~/.ssh/john_priv_key -o IdentitiesOnly=yes'
git clone git@example.com:my-project.git

(Remove the linebreak; I added it above only for improved readability.)

He will also be able to push any changes made back to the server, albeit always with the need to use that ghastly environment variable GIT_SSH_COMMAND. So it might pay off for him to also setup an SSH config file, just as shown above (but with the path of his own SSH key, of course).

However, he will not be able to get a shell (because the login shell of the git user, git-shell, will not allow it).

February 23, 2022. Twitter.


  1. See https://www.digitalocean.com/community/tutorials/how-to-set-up-a-private-git-server-on-a-vps.↩︎

  2. In Archlinux, the git user actually already exists… and its home folder is set to /. We can use that preexisting user, but we need to modify its home folder (git user’s shell should already be set to git-shell by default, but I add it to the usermod command, just to be sure):

    # usermod git --home /srv/git --shell /usr/bin/git-shell
    ↩︎
  3. The --user-group option means that, in addition to the git user, also the git group will be created.↩︎

  4. See https://stackoverflow.com/questions/4565700/how-to-specify-the-private-ssh-key-to-use-when-executing-shell-command-on-git for more on SSH authentication when using git.↩︎

  5. This config file also changes some previous commands. For example, if you already a config file set up previously, then the line to add the origin remote after doing git init would change from this:

    $ git remote add origin git@example.com:my-project.git

    to just:

    $ git remote add origin mygit:my-project.git
    ↩︎
  6. If you are wondering about the IdentitiesOnly=yes line, see https://superuser.com/questions/268776/how-do-i-configure-ssh-so-it-doesnt-try-all-the-identity-files-automatically.↩︎