Setting Up a Self-Hosted Git Server on a Raspberry Pi with Apache, HTTPS, and Correct Permissions

Published May 12, 2026, 7:21 p.m. by james

A Raspberry Pi can be used as a small private Git server. The clean approach is to create a bare Git repository on the Raspberry Pi, store it in a shared location such as /var/www/git, and set permissions so Git can write to the repository when you push changes from another computer.

The most important part of this setup is ownership and permissions. A bare Git repository is not just a folder of source files. When a push happens, Git writes objects, refs, lock files, and pack files inside the repository. If the user handling the push does not have write permission, the push will fail.

1. Install Git and Apache

sudo apt update
sudo apt install git apache2

If the repository will be served through Apache over HTTP or HTTPS, enable the required Apache modules:

sudo a2enmod cgi alias env
sudo systemctl restart apache2

2. Create a Shared Git Group

Create a shared group for Git repository access:

sudo groupadd -f gitgroup

Add your normal Raspberry Pi user to the group:

sudo usermod -aG gitgroup "$USER"

If Apache needs to serve or receive Git data over HTTP or HTTPS, add Apache's user to the same group:

sudo usermod -aG gitgroup www-data

Log out and log back in so the new group membership becomes active. Then check your groups:

groups
id
id -nG

The repository should not normally be owned by www-data. A better setup is to make the repository owned by your normal server user and assign it to gitgroup. Apache can then access the repository through group permission instead of owning the whole repository.

3. Create the Main Git Directory

Create a directory to store all bare Git repositories:

sudo mkdir -p /var/www/git

Give ownership to your normal user and assign the directory to the shared Git group:

sudo chown -R "$USER":gitgroup /var/www/git

Set directory permissions:

sudo find /var/www/git -type d -exec chmod 2775 {} \;

Set file permissions:

sudo find /var/www/git -type f -exec chmod 664 {} \;

The 2 in 2775 enables the setgid bit. This makes newly created files and directories inherit the gitgroup group. That keeps repository permissions consistent after future Git pushes.

4. Create a Bare Git Repository

A server-side repository should usually be bare. A bare repository stores Git history and references, but it does not contain a checked-out working tree.

Create a new bare repository called project.git:

cd /var/www/git
mkdir project.git
cd project.git
git init --bare --shared=group

Fix ownership and permissions:

sudo chown -R "$USER":gitgroup /var/www/git/project.git
sudo find /var/www/git/project.git -type d -exec chmod 2775 {} \;
sudo find /var/www/git/project.git -type f -exec chmod 664 {} \;

Tell Git that the repository is shared by a group:

git --git-dir=/var/www/git/project.git config core.sharedRepository group

5. Configure Apache Git HTTP Backend

To serve repositories through Apache, create a Git HTTP backend configuration file:

sudo vim /etc/apache2/conf-available/git-http.conf

Add this configuration:

SetEnv GIT_PROJECT_ROOT /var/www/git
SetEnv GIT_HTTP_EXPORT_ALL
SetEnv GIT_HTTP_RECEIVE_PACK 1

ScriptAlias /git/ /usr/lib/git-core/git-http-backend/

<Directory "/usr/lib/git-core">
    Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
    AllowOverride None
    Require all granted
</Directory>

Enable the configuration and restart Apache:

sudo a2enconf git-http
sudo systemctl restart apache2

The repository URL will have this form:

https://yourdomain.com/git/project.git

6. Allow Git Push over HTTP or HTTPS

Git clone access and Git push access are different. Clone access uses git-upload-pack. Push access uses git-receive-pack. If you want to push through Apache over HTTP or HTTPS, enable receive-pack for the repository:

git --git-dir=/var/www/git/project.git config http.receivepack true

Restart Apache:

sudo systemctl restart apache2

Do not expose anonymous HTTP push to the public internet. If push over HTTPS is enabled, protect it with authentication, network restrictions, or another access-control method. For private use, SSH is often simpler and safer.

7. Mark the Repository as Safe

If Git is executed by a different user than the repository owner, Git may require the repository to be marked as safe. For an Apache-served repository, add it as a safe directory at the system level:

sudo git config --system --add safe.directory /var/www/git/project.git

To allow all repositories under /var/www/git:

sudo git config --system --add safe.directory '/var/www/git/*'

Restart Apache:

sudo systemctl restart apache2

8. Test Repository Access

Test clone access:

curl -I "https://yourdomain.com/git/project.git/info/refs?service=git-upload-pack"

Test push access:

curl -i "https://yourdomain.com/git/project.git/info/refs?service=git-receive-pack"

If push access is enabled correctly, the response should mention:

application/x-git-receive-pack-advertisement

9. Push a Local Project to the Raspberry Pi

On your local computer, go into your project folder:

cd /path/to/project

If the folder is not already a Git repository:

git init
git add .
git commit -m "Initial commit"

Add the Raspberry Pi repository as the remote:

git remote add origin https://yourdomain.com/git/project.git

Push the current branch. If your branch is called master:

git push -u origin master

If you want to use main instead:

git branch -M main
git push -u origin main

10. Clone the Repository Later

From another machine, clone the repository with:

git clone https://yourdomain.com/git/project.git

Or, if using SSH:

git clone user@yourdomain.com:/var/www/git/project.git

11. Add a Generic .gitignore

Create a .gitignore file:

vim .gitignore

A useful generic version:

# OS files
.DS_Store
Thumbs.db
Desktop.ini

# Editor files
.vscode/
.idea/
*.swp
*.swo
*~

# Logs
*.log
logs/

# Environment and secrets
.env
.env.*
!.env.example
*.pem
*.key
*.crt
secrets/

# Build output
build/
dist/
out/
target/
bin/
obj/
Debug/
Release/

# Python
__pycache__/
*.py[cod]
.venv/
venv/
env/
.pytest_cache/
.mypy_cache/
.ruff_cache/

# C / C++ / CMake
*.o
*.obj
*.a
*.so
*.dylib
*.dll
*.exe
*.out
*.dSYM/
CMakeFiles/
CMakeCache.txt
cmake_install.cmake
compile_commands.json

# Node
node_modules/
.npm/
.yarn/
coverage/

# Databases
*.sqlite
*.sqlite3
*.db

# Temporary files
tmp/
temp/
.cache/
*.tmp
*.bak
*.orig

Add and commit it:

git add .gitignore
git commit -m "Add gitignore"

12. Check Final Permissions

Check the main Git directory:

ls -ld /var/www/git

Check the repository:

ls -ld /var/www/git/project.git

The output should look similar to this:

drwxrwsr-x user gitgroup /var/www/git
drwxrwsr-x user gitgroup /var/www/git/project.git

The important parts are:

owner: your normal server user
group: gitgroup
directory permission: 2775
file permission: 664
Apache access: through gitgroup membership, not ownership

13. Complete Permission Repair Command

If a repository already exists and you want to repair its permissions, use:

sudo groupadd -f gitgroup
sudo usermod -aG gitgroup "$USER"
sudo usermod -aG gitgroup www-data

sudo chown -R "$USER":gitgroup /var/www/git/project.git
sudo find /var/www/git/project.git -type d -exec chmod 2775 {} \;
sudo find /var/www/git/project.git -type f -exec chmod 664 {} \;

git --git-dir=/var/www/git/project.git config core.sharedRepository group
git --git-dir=/var/www/git/project.git config http.receivepack true
sudo git config --system --add safe.directory /var/www/git/project.git

sudo systemctl restart apache2

14. SSH Alternative

Git over HTTPS through Apache works, but SSH is often simpler for private repositories. With SSH, the SSH user writes directly to the bare repository.

Set the remote to SSH:

git remote set-url origin user@yourdomain.com:/var/www/git/project.git

Push the current branch:

git push -u origin master

Or, if your branch is called main:

git push -u origin main

15. Summary

The key idea is to avoid giving the whole repository to Apache. The cleaner permission model is to make the repository owned by your normal server user, assign it to a shared Git group, and add Apache's www-data user to that group only if Apache needs Git access. Directories should use 2775 so new Git files inherit the shared group, and files should generally use 664 so group members can write to them.

Share this post

Similar posts

Git Cheatsheet

comment

There are no comments yet.

Add a new comment