If you want to set up your own version control for a project, but prefer not to host it on a Git hosting service (like GitHub), you can run your own Git server to store your code and act as a central repository for all of collaborators.
Why host your own Git server?
You may run your own Git server, if you don't want to store your code on someone else's servers. You may need to have full control of your version control infrastructure.
Also, if you're using a Git hosting service, there are some restrictions that may not be ideal. For example, GitHub doesn't allow files above 100 MB, which may be a critical problem for projects with large files. Running your own Git server may allow these larger files.
Initializing Git repositories
First off, you need to install Git. To do that, you can run sudo apt install git
on Debian-based systems (Debian, Devuan, Ubuntu, Linux Mint), sudo dnf install git
or sudo yum install git
on RHEL-based systems (Fedora, CentOS, RHEL), sudo zypper install git-core
on SUSE-based systems (SUSE Linux Enterprise, openSUSE) or sudo pacman -S git
on Arch-based systems (Arch Linux, Manjaro).
Next, add repositories to a Git root directory. In our case we're using /var/lib/git
directory. For our scenario, we're just making a clone of SVR.JS Git repository:
sudo mkdir /var/lib/git
cd /var/lib/git
sudo git clone --bare https://git.svrjs.org/svrjs.git #You can also clone any other Git repository or just create a new blank repository using `git init --bare myrepo.git`
You may need to add git-daemon-export-ok
to mark Git repositories as safe for export:
sudo find /var/lib/git -mindepth 1 -maxdepth 1 -exec touch {}/git-daemon-export-ok \;
If you want to learn more about Git commands, you can check out our post about Git commands.
Then create a new Git user (we're using /var/lib/gituser
directory as a user directory):
sudo useradd -s /usr/bin/git-shell -d /var/lib/gituser -m -r git
And change the permissions for the Git root directory and Git user directory:
sudo chown -hR git:git /var/lib/git
sudo chmod -R ug+rw /var/lib/git
sudo chown -hR git:git /var/lib/gituser
sudo chmod -R g-w /var/lib/gituser
Finally, change the default file creation mask by adding or replacing the UMASK
line in the /etc/login.defs
file:
UMASK 002
The initial setup of a Git server
Now you are ready to serve Git to the public through one of three protocols:
- Git protocol
- SSH
- HTTP(S)
Hosting through Git protocol
Git protocol is a protocol used by Git daemon that comes packaged with Git; it listens on a dedicated port (9418) that provides a service similar to one using SSH protocol, but it has absolutely no authenttication or cryptography. That means no uploading files by default. But Git protocol is very efficient.
To set up Git daemon, first install the Git daemon. You can use sudo apt install git-daemon-sysvinit
(SystemV init or systemd) or sudo apt install git-daemon-run
(runit) on Debian-based systems (Debian, Devuan, Ubuntu, Linux Mint), sudo dnf install git-daemon
or sudo yum install git-daemon
on RHEL-based systems (Fedora, CentOS, RHEL), sudo zypper install git-daemon
on SUSE-based systems (SUSE Linux Enterprise, openSUSE). Users of Arch-based systems (Arch Linux, Manjaro) don't need to install an additional package.
Then change the /etc/default/git-daemon
file contents to:
GIT_DAEMON_ENABLE=true
GIT_DAEMON_USER=git
GIT_DAEMON_BASE_PATH=/var/lib/git
GIT_DAEMON_DIRECTORY=/var/lib/git
GIT_DAEMON_OPTIONS=""
You may change GIT_DAEMON_OPTIONS
to enable git push
, however anybody can do git push
and do unauthorized changes:
#WARNING!!! INSECURE!!!
GIT_DAEMON_OPTIONS="--enable=receive-pack"
Finally, restart the daemon with sudo systemctl restart git-daemon
or sudo /etc/init.d/git-daemon restart
.
If you're using a ufw
firewall, you can use:
sudo ufw allow git
The Git protocol setup of a Git server
You can test the server by running this command on a client (assuming that the server has 10.0.0.2
address):
git clone git://10.0.0.2/svrjs.git
#Replace `svrjs.git` with repository name and `10.0.0.2` with your server address.
Test of the Git protocol setup
Now you have set up a Git server serving through Git protocol!
Hosting through SSH
SSH protocol is a more common protocol for Git servers. This is because SSH access is already set up in many server configurations, and if it isn't, it's easy to set it up. SSH is also authenticated, and it's generally easy to set up and use due to it being ubiquitous.
If you don't have SSH server, you can install it (in this case we're using OpenSSH). To do that, you can run sudo apt install openssh-server
on Debian-based systems (Debian, Devuan, Ubuntu, Linux Mint), sudo dnf install openssh-server
or sudo yum install openssh-server
on RHEL-based systems (Fedora, CentOS, RHEL), sudo zypper install openssh
on SUSE-based systems (SUSE Linux Enterprise, openSUSE) or sudo pacman -S openssh
on Arch-based systems (Arch Linux, Manjaro).
If you're using a ufw
firewall, you can use:
sudo ufw allow ssh
You can either use password or public key for the authentication.
If you want to use password, you can set it using this command (run it on server):
sudo passwd git
If you want to use public keys, first set up a password for the user. Then you can temporary switch the shell of git
user to sh
(run it on server):
sudo usermod -s /bin/sh git
Then you can generate the SSH keys and send it to the server (assuming that 10.0.0.2
is a server address; run it on client):
ssh-keygen -o
ssh-copy-id git@10.0.0.2
#Replace `10.0.0.2` with your server address.
Preparing the SSH keys...
Finally you can revert the shell change (run it on server):
sudo usermod -s /usr/bin/git-shell git
If you want to disable password authentication, you can add this to your /etc/ssh/sshd_config
file:
Match User git
PasswordAuthentication No
And restart the server using either sudo systemctl restart ssh
or sudo /etc/init.d/ssh restart
.
You may also create a chroot
environment for a Git server.
The SSH setup of a Git server
You can test the server by running this command on a client (assuming that the server has 10.0.0.2
address):
git clone ssh://git@10.0.0.2/var/lib/git/svrjs.git
#Replace `svrjs.git` with repository name and `10.0.0.2` with your server address.
#When using chroot, remove the `/var/lib/git` part.
Test of the SSH setup
Now you have set up a Git server serving through SSH protocol!
Hosting through HTTP (along with GitWeb)
HTTP protocol is also a very common protocol for Git servers. You can use HTTP authentication to protect Git server from unauthorized git push
operations (if there is no HTTP authentication, git push
operations are disabled by default). You can also set up to anonymously serve Git repositories for cloning.
For Git hosting services (like GitHub), the cloning URL is the same as the URL you use to view the repository through the web browser. You will also set up this configuration.
For this purpose, we're using GitWeb as a Git repository viewer and git-http-backend
for the Git cloning.
That means that you can use any web server software, that supports CGI (Common Gateway Interface), since both GitWeb and git-http-backend
use CGI.
To set up GitWeb, first install the GitWeb. You can use sudo apt install gitweb
on Debian-based systems (Debian, Devuan, Ubuntu, Linux Mint), sudo dnf install gitweb
or sudo yum install gitweb
on RHEL-based systems (Fedora, CentOS, RHEL), sudo zypper install git-web
on SUSE-based systems (SUSE Linux Enterprise, openSUSE). Users of Arch-based systems (Arch Linux, Manjaro) may need to install Perl CGI module (a dependency of GitWeb) to use GitWeb using sudo pacman -S perl-cgi
.
For the web server, we're using SVR.JS web server with RedBrick mod. First download SVR.JS web server and SVR.JS installer for GNU/Linux. You can use these commands to install SVR.JS:
cd ~
wget https://downloads.svrjs.org/svr.js.3.14.5.zip #Replace "3.14.5" with the latest SVR.JS version you can get from the SVR.JS website
wget https://downloads.svrjs.org/installer/https://downloads.svrjs.org/installer/svr.js.installer.linux.20240219.zip #You can replace "svr.js.installer.linux.20240219.zip" with latest installer archive you can find on the "installer" directory
mkdir installer
cd installer
unzip ../svr.js.installer.linux.20240219.zip
cp ../svr.js.3.14.5.zip svrjs.zip
sudo bash installer.sh
After installing SVR.JS, start the server using sudo systemctl start svrjs
or sudo /etc/init.d/svrjs start
.
SVR.JS default page
Later, run these commands to avoid permission conflict with SSH- and Git protocol-based Git services (for web servers other than SVR.JS installed with SVR.JS installer, replace svrjs
with www-data
):
sudo usermod -aG svrjs git
sudo usermod -aG git svrjs
If you're using a ufw
firewall, you can use:
sudo ufw allow http
For other web servers, you can read the git-http-backend documentation and the GitWeb documentation.
Now after installing SVR.JS you can delete everything under the /var/www/svrjs
directory (sudo rm -rf /var/www/svrjs/*
) and restart the server using sudo systemctl restart svrjs
or sudo /etc/init.d/svrjs restart
.
After the deletion, the only thing that's left is the empty directory listing, when you visit http://10.0.0.2/
(assuming that the server address is 10.0.0.2
):
Empty directory listing on the HTTP server
Since SVR.JS itself doesn't have CGI support, you need to install the RedBrick mod. RedBrick basically adds CGI support for the SVR.JS web server. To do that, you can go to the SVR.JS mods page and run the commands:
cd /usr/lib/svrjs/mods
sudo wget https://downloads.svrjs.org/mods/redbrick.cgi.2.6.0.tar.gz #Replace "2.6.0" with the latest version of the RedBrick found on the SVR.JS mods page.
After installing the mod, restart the server using sudo systemctl restart svrjs
or sudo /etc/init.d/svrjs restart
.
Then add files under the cgi-bin
directory on the web root:
cd /var/www/svrjs
sudo ln -s /usr/lib/cgi-bin cgi-bin
sudo ln -s /usr/share/gitweb/static .
cd cgi-bin
sudo ln -s /usr/share/gitweb/gitweb.cgi .
sudo ln -s /usr/share/gitweb/static .
sudo ln -s /usr/lib/git-core/git-http-backend git.cgi
After adding the files, stop the SVR.JS service using sudo systemctl stop svrjs
or sudo /etc/init.d/svrjs stop
and change the SVR.JS configuration (in /etc/svrjs-config.json
or /usr/lib/svrjs/config.json
) like this:
{
"nonStandardCodes": [
{
"scode": 301,
"regex": "/^\\/git\\/(.*)/",
"location": "/$1"
},
{
"scode": 301,
"regex": "/^\\/git($|[?#].*)/",
"location": "/$1"
},
{
"scode": 401,
"realm": "Git Access",
"regex": "/^\\/cgi-bin\\/git\\.cgi(?:(\\/.*)?\\/git-receive-pack(?:$|[?#])|(?:\\/[^?]*)?\\?(?:[^#]*[;&?]|)service=git-receive-pack(?:$|[;&?#]))/"
}
],
"allowStatus": true,
"enableCompression": true,
"customHeaders": {},
"enableLogging": true,
"enableDirectoryListing": false,
"enableDirectoryListingWithDefaultHead": false,
"stackHidden": true,
"enableRemoteLogBrowsing": false,
"exposeServerVersion": false,
"disableServerSideScriptExpose": true,
"rewriteMap": [
{
"definingRegex": "/^\\/(?!git(?:$|[?\\/#])|svrjsstatus\\.svr(?:$|[?#]))(?![^\\/]+\\/(?:branches|hooks|info|logs|objects|refs|config|description|HEAD|git-upload-pack|git-receive-pack)(?:$|[?\\/#]))/",
"isNotFile": true,
"replacements": [
{
"regex": "/^\\//",
"replacement": "/cgi-bin/gitweb.cgi/"
}
]
},
{
"definingRegex": "/^\\/(?!git(?:$|[?\\/#])|svrjsstatus\\.svr(?:$|[?#]))/",
"isNotFile": true,
"replacements": [
{
"regex": "/^\\//",
"replacement": "/cgi-bin/git.cgi/"
}
]
}
],
"dontCompress": [
"/.*\\.ipxe$/",
"/.*\\.img$/",
"/.*\\.iso$/",
"/.*\\.png$/",
"/.*\\.woff$/"
],
"enableIPSpoofing": false,
"exposeModsInErrorPages": false,
"enableETag": true,
"disableUnusedWorkerTermination": false,
"rewriteDirtyURLs": false,
"wwwroot": "/var/www/svrjs",
"disableTrailingSlashRedirects": false,
"environmentVariables": {
"GIT_PROJECT_ROOT": "/var/lib/git"
},
"customHeaders": {
"Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
"X-Frame-Options": "sameorigin",
"X-Content-Type-Options": "nosniff",
"Content-Security-Policy": "default-src 'self'; object-src 'none'; script-src 'self' 'sha256-dacEZQWGxky95ybZadcNI26RDghVLeVdbdRC/Q3spJQ='; img-src 'self' www.gravatar.com data:"
},
"allowDoubleSlashes": false
}
Change the enableIPSpoofing
property to true
, if your server is behind a reverse proxy. For more configuration options, see the SVR.JS documentation.
After the configuration changes, you can add the git
user to the HTTP server:
sudo svrpasswd -a git
Later, modify the /etc/gitweb.conf
file (GitWeb configuarion) like this:
$projectroot = "/var/lib/git";
$git_temp = "/tmp";
@stylesheets = ("/static/gitweb.css");
$javascript = "/static/gitweb.js";
$logo = "/static/git-logo.js";
$favicon = "/static/git-favicon.js";
$feature{'pathinfo'}{'default'} = [1];
@diff_opts = ();
For more configuration options, see the gitweb.conf
documentation.
After those modifications, start the server using sudo systemctl start svrjs
or sudo /etc/init.d/svrjs start
.
You have now both Git server and GitWeb running!
The HTTP setup of a Git server
You can test the server by running this command on a client (assuming that the server has 10.0.0.2
address):
git clone http://10.0.0.2/svrjs.git
#Replace `svrjs.git` with repository name and `10.0.0.2` with your server address.
Test of the HTTP setup
You can also test out GitWeb by visiting http://10.0.0.2/
(assuming that the server address is 10.0.0.2
):
The GitWeb view of the svrjs.git repository
You may notice this description: Unnamed repository; edit this file 'description' to name the repository. In this case you can change it by editing the description
file in the Git repository directory.
Adding HTTP encryption
You have probably set up service per instructions above, but there may be one problem: there is no encryption! You may need encryption to ensure that no one is eavesdropping for your Git data and credentials you use to push the repositories.
To do that in SVR.JS, first obtain a TLS certificate from a certificate authority (you can obtain Let's Encrypt certificates for free using certbot
).
After obtaining a TLS certificate, copy the certificate and the private key, so that it is accessible in those paths:
/usr/lib/svrjs/cert/cert.crt
- TLS certificate/usr/lib/svrjs/cert/key.key
- private key
After copying the files, stop the SVR.JS service using sudo systemctl stop svrjs
or sudo /etc/init.d/svrjs stop
and append to the SVR.JS configuration (in /etc/svrjs-config.json
or /usr/lib/svrjs/config.json
) like this:
...
"allowDoubleSlashes": false,
"secure": true,
"cert": "cert/cert.crt",
"key": "cert/key.key"
}
After those modifications, start the server using sudo systemctl start svrjs
or sudo /etc/init.d/svrjs start
.
If you're using a ufw
firewall, you can use:
sudo ufw allow https
You have now added encryption to the HTTP server!