Keeping in line with my policy of low memory overhead, I do not want to waste several hundred megabytes on a git repository manager, such as Gitlab or Bitbucket. If you get back to Git basics, you can set up a Git repository with no additional overhead.


Let’s get back to basics, what is Git? Git is a distributed version control system, which means it’s just a bunch of files on disk. Git is fairly protocol-agnostic, so it can transfer data over HTTP, FTP or the Git protocol. Often, the Git protocol is used in conjunction with ssh, which handles the transport layer encryption and authentication. Whenever you see a command line git clone git@git:repo, you are most likely using Git+SSH.

This guide assumes you know how SSH public-private key authentication works. If not, follow this tutorial on Digital Ocean.



To set up a Git+SSH server, so you can clone/commit etc. via SSH is fairly simple. Create a single Unix user, through which all users will authenticate based on their password. To make it a bit more user-friendly and secure, do the following steps:

  1. Create a Git user.

    groupadd git
    useradd -g git -m -d /srv/git -s /usr/bin/git-shell -c "Git User" git

    There are several aspects which are important in this command:

    • -m -d /srv/git This creates a home directory for the git user which will store all the repositories. Since you can use relative paths with SSH, this allows you to put a repository in /srv/git/repo1 and clone it with git clone [email protected]:repo1. No need to use full paths.

    • -s /usr/bin/git-shell is a purpose built shell for Git use only. Remember, SSH normally grants you a login shell, which allows you to do everything on a system what a non-root user can, which is a lot! The git-shell only allows clients to execute Git related commands.

    Tip: You might need to add /usr/bin/git-shell to the list of allowed shells (often found at /etc/shells) to allow logins using git-shell. This depends on your distribution.

  2. Make sure the git user home directory is owned by the git user:

    chown git: /srv/git
  3. Add public keys for the users to the git user’s authorized_keys file:

    mkdir /srv/git/.ssh
    chmod 700 /srv/git/.ssh
    echo 'ssh-rsa AAAAB3NzaC1yc2EAA...ZGXMchiG0K4aNp5= [email protected]' >> /srv/git/.ssh/authorized_keys
    chmod 600 /srv/git/.ssh/authorized_keys
    chown -R git: /srv/git/.ssh

    Make sure you end up with the following permissions:

    drwx------ 2 git git 4096 nov 23  2015 .ssh
    -rw------- 1 git git 218  nov 23  2015 .ssh/authorized_keys

    Tip: if you have problems logging on using Git, test if you can log on with a normal SSH client. Make sure the permissions for the .ssh/ directory and authorized_keys file exactly match the ones stated above.

  4. Create a Git repository on the server. You need to create a ‘bare’ Git repository to allow operations over SSH.

    cd /srv/git
    git init --bare repoName
    chown -R git: repoName
  5. Clone and work with the repo!

You can now work with the repository by using the git remote [email protected]:repoName, for example with:

git clone [email protected]:repoName
git remote add origin [email protected]:repoName

HTTP Browsing

So now we have a basic Git server running, we can enhance it a bit by allowing read-only HTTP browsing of the repositories for easy access. Since I already have an Apache HTTP server with PHP running, I decided to use GitList. The stable GitList version at time of writing is 0.5.0.

  1. Download GitList

  2. Untar it in your webroot or other directory which is accessible by your webserver

    cd /var/www/
    tar -zxvf /home/user/Downloads/gitlist-0.5.0.tar.gz
  3. Copy the config.ini-example file to config.ini and change the following parameters:

    repositories[] = '/srv/git';
    timezone = 'Europe/Amsterdam'
  4. Make sure the Git repository directory is readable by the webserver user. The easiest way to do this is by adding the webserver user to the git Unix group:

    usermod -G git www-data
  5. Browse to the GitList page (probably at

  6. Optionally, add authentication and SSL. Use this Apache config as a guide:

      DocumentRoot /var/www/gitlist/
      SSLEngine on
      SSLProtocol all -SSLv2
      SSLCertificateFile /etc/apache2/ssl/
      SSLCertificateKeyFile /etc/apache2/ssl/
      SSLCACertificateFile /etc/apache2/ssl/git.example.com_ca.crt
      ErrorLog ${APACHE_LOG_DIR}/git.example.com_error.log
      CustomLog ${APACHE_LOG_DIR}/git.example.com_access.log combined
      LogLevel warn
      <Directory /var/www/gitlist>
        Options MultiViews FollowSymLinks
        AllowOverride all
        Order allow,deny
        allow from all
        AuthName "GitList"
        AuthType Basic
        AuthBasicProvider ldap
        AuthLDAPURL ldap://,dc=com?uid
        AuthLDAPGroupAttribute memberUid
        AuthLDAPGroupAttributeIsDN off
        Require ldap-group cn=git,ou=Group,dc=example,dc=com
    # Upstep to SSL
    <Virtualhost *:80>
      Redirect 301 /

Read-only HTTP clones

As a mechanism to deploy software, it might come in handy to have read-only access to the repository for certain servers. I use this mechanism to deploy Puppet code accross the network.

  1. Set up Apache HTTPd with the appropriate config:

      Alias "/puppet-readonly.git" "/srv/git/puppet.git/"
      <Directory /srv/git/puppet.git/>
        AuthName "Puppet git repository"
        AuthType Basic
        AuthUserFile /etc/apache2/puppet.git.htpasswd
        Require valid-user
  2. Create a .htpasswd file for authentication

    htpasswd -c /etc/apache2/puppet.git.htpasswd user1
  3. When Git clones or pulls a repository over SSH, it discovers references and packs dynamically. This cannot be done over HTTP, so you need to make sure a list up these is updated every time you push changes to the repository. This can be automated by using Git hooks. Uncomment the exec git update-server-info line in the repository’s hooks/post-update.sample file and rename it to hooks/post-update

    less /srv/git/puppet/hooks/post-update
    # An example hook script to prepare a packed repository for use over
    # dumb transports.
    # To enable this hook, rename this file to "post-update".
    exec git update-server-info
  4. Push a change to the repository to check if the Git hook works. update-server-info should change the following files in the repository:

  5. Access the Git repository through the following url:

    git clone https://user1:[email protected]/puppet-readonly.git