Setting up a private/public git repository


It’s common practice to have a private repository in a secure network, available only to core developers, which push all changes to a public read-only repository, available to every Internet user.

The private repository is only available via ssh and requires ssh key authentication. Developers can only use their ssh account to access the git repository, they cannot run any commands on the server.

The public repository is available via HTTP or git native protocol and doesn’t require authentication.


Here is how my setup looks like on the private server.

The private server will be only accessible via ssh and allow read/write:

  • initialize the repository:
    • cd /var/lib/git/repo.git
    • GIT_DIR=. git –bare init –shared
  • create a system account for all the developers
    • adduser dev1
    • adduser dev2
  • create a dedicated account for propagating the changes to the public git server:
    • adduser gitsync
  • create a private/public ssh key for user gitsync, with empty passphrase:
    • su – gitsync
    • ssh-keygen -t rsa
  • add all the developers and gitsync to a dedicated group: e.g. devgit . All of them must be able to write to the repository directory, e.g.:
    • usermod -a -G devgit gitsync
  • Give group write permissions to group devgit on the repository directory:
    • chgrp -R devgit /var/lib/git/repo.git
    • chmod -R g+w /var/lib/git/repo.git
  • Change the login shell of developers, so that they can only use their system account for git. For each user do:
    • chsh  -s /usr/bin/git-shell dev1
    • chsh  -s /usr/bin/git-shell dev2
  • Allow users with ‘git-shell’ to login to the system: open /etc/shells in your favourite editor and add /usr/bin/git-shell to it.
  • Periodically git needs to optimize the repository structure. Add the following task to crontab of gitsync:
    • 0 0 * * * cd /var/lib/git/repo.git; git repack && git gc
  • Every developer (that’s to say, all members of group devgit) must be able to push the updates to the public git server using the account ‘gitsync’. We need sudo for this. Add the following entry to /etc/sudoers (WARN: sudoers is really picky about \ and spaces, be careful!):
    • %devgit ALL=(gitsync) NOPASSWD: /usr/bin/git push –mirror gitsync@git.public-server.tld\:/var/lib/git/repo.git
  • Everytime the repository is updated, we must push the new data to the public server. Add the following line to /var/lib/git/repo.git/hooks/post-update:
    • cd /var/lib/git/repo.git;sudo -u gitsync /usr/bin/git push –mirror gitsync@git.public-server.tld:/var/lib/git/repo.git
  • Make post-update executable:
    • chmod a+x /var/lib/git/repo.git/hooks/post-update

Onto the public server:

  • Initialize the repository:
    • cd /var/lib/git/repo.git; GIT_DIR=. git –bare init
  • Create user gitsync:
    • adduser gitsync
  • Give gitsync ownership of the repository:
    • chown -R gitsync /var/lib/git/repo.git
  • Change the default shell of gitsync to git-shell, so that the user cannot run any commands on the system except git:
    • chsh -s /usr/bin/git-shell gitsync
  • Add /usr/bin/git-shell to /etc/shells
  • Copy the content of ~gitsync/.ssh/ on the private server to ~gitsync/.ssh/authorized_keys on the public server. Make sure that ~gitsync/.ssh is owned by gitsync and has permission 700. Make sure that authorized_keys is owned by gitsync and has permissions 600.
  • If you want the repository to be available via http, add a virtualhost to Apache, with DocumentRoot /var/lib/git . Disable any language interpreters on that vhost (e.g. PHP).
  • If you want the repository to be available via git native protocol, install git-daemon and create the file git-daemon-export-ok inside the repository:
    • touch /var/lib/git/repo.git/hooks/git-daemon-export-ok .
  • Enable update-server-info on every commit:
    • chmod a+x /var/lib/git/repo.git/hooks/post-update
  • Add the following task to gitsync’s crontab:
    • 0 0 * * * cd /var/lib/git/repo.git;(git gc;git repack;git update-server-info) >/dev/null 2>&1

Remember to edit /var/lib/git/repo.git/description on both servers, to give a meaningful name to your repository.

Now you are ready to import your project:

git push username@git.server.tld:/var/lib/git/repo.git

If you want to receive an email notification whenever somebody commits some changes to the repository, add a proper script to the post-receive hook (e.g.: git-notify).


  • Edwin: for his precious help in setting up and testing the repository

Leave a Reply