Failing with git-http-backend on Rocky Linux 10
My problem is: I am unable to get git push to work with a repository using
the git-http-backend CGI handler on Rocky Linux 10.
My solution is: use something other than a RHEL 10-based distribution or
something other than "Smart HTTP" to access the repository. I could get the
git-http-backend method to work on Slackware, Rocky Linux 9, and Debian
13.6. These are my notes on what I tried, what worked, and what didn’t.
Why git-http-backend? If you want to self-host a git repository on a remote
server the usual options are:
- ssh — recommended, especially if you already do everything with SSH.
- git — no authentication so only suitable for public read-only repositories.
- http — can use standard HTTP authentication for restricted read/write access.
See the "Git on the Server" chapter of the Pro Git book for the details.
I needed to collaborate with a friend who isn’t a regular Linux or SSH user so I figured the simplest way to set up a non-public repository that we can both access is using the "Smart HTTP" protocol for git. I plan to host the repository on my personal VPS running Rocky Linux 10.
The packages I required on top of a minimal Rocky 10 install are:
-
httpdandhttpd-tools -
certbotandpython3-certbot-apache(so I can enable SSL via LetsEncrypt) -
git
At the time of testing, Rocky Linux 10.2 was providing httpd-2.4.63 and
git-2.52.0.
Preparing the git repository for HTTP access
My repositories are located in /srv/git/ on the host. This directory and
its repositories must belong to the web server’s group (in this case "apache")
and be group-writeable in order for git push to have a chance of working.
### On my local PC # Create a bare clone of the repository git clone --bare my_proj my_proj.git # Copy the bare repo to the remote host scp -r my_proj.git gsl@server.gslewis.id.au:my_proj ### On the remote host, as root # Move the repository to /srv/git mv ~gsl/my_proj /srv/git/ # Set group and write permissions on all repos to the web server group chgrp -R apache /srv/git/ chmod -R g+w /srv/git/
We also need to either disable SELinux or change the context of the
/srv/git/ directory so that it can be read from and written to by the web
server. I usually just disable SELinux (see /etc/selinux/config for
instructions) but I did test (and fail) with SELinux enabled as well.
If we were using SSH with multiple users pushing to the repository, it would be necessary to re-init the repository as shared:
cd /srv/git/my_repo git init --bare --shared
However, as I am using HTTP then all writes will be as the web server’s user so the repository doesn’t need to be shared.
To simplify matters, I also "disable" the git safe directory check by treating all repositories as safe, system-wide.
git config --system --add safe.directory "*"
This creates the /etc/gitconfig file if not already present.
Web server configuration
I am using Apache httpd as the web server with
digest authentication to restrict access. Based on the Pro Git chapter on
Smart HTTP, the
basic <VirtualHost> entry I created was:
<VirtualHost _default_:443>
ServerName https://server.gslewis.id.au
# A bunch of SSL-related directives for LetsEncrypt certs.
SetEnv GIT_PROJECT_ROOT /srv/git
SetEnv GIT_HTTP_EXPORT_ALL
SetEnvIf Git-Protocol ".*" GIT_PROTOCOL=$0
ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
<Location /git>
AuthName "Git Access"
AuthType Digest
AuthUserFile /etc/httpd/auth/git-users.htdigest
Require valid-user
</Location>
</VirtualHost>
This configures git-http-backend as the CGI script handler for the URL path
/git/ which will match repositories in the /srv/git directory.
For each user I want to grant access to the repositories, I use htdigest to
populate the git-users.htdigest file.
# Create the htdigest file and add myself (gsl) htdigest -c /etc/httpd/auth/git-users.htdigest "Git Access" gsl
Git clone and push
Given I have my repository at /srv/git/my_repo, I can now clone it as
follows which works as expected, prompting for a username and password that
was added to the htdigest file.
gsl@sula:~/workspace$ git clone https://server.gslewis.id.au/git/my_repo Cloning into 'my_repo'... Username for 'https://server.gslewis.id.au': gsl Password for 'https://gsl@server.gslewis.id.au': remote: Enumerating objects: 17, done. remote: Counting objects: 100% (17/17), done. remote: Compressing objects: 100% (11/11), done. remote: Total 17 (delta 8), reused 15 (delta 6), pack-reused 0 (from 0) Receiving objects: 100% (17/17), 6.30 KiB | 6.30 MiB/s, done. Resolving deltas: 100% (8/8), done.
All fine and dandy. Now make a change, commit it and push it up to the server.
gsl@sula:~/workspace/my_repo$ git commit -a -m "Testing push" [master 5fb4dbb] Testing push 1 file changed, 1 insertion(+) gsl@sula:~/workspace/my_repo$ git push Username for 'https://server.gslewis.id.au': gsl Password for 'https://gsl@server.gslewis.id.au': Enumerating objects: 5, done. Counting objects: 100% (5/5), done. Delta compression using up to 16 threads Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 288 bytes | 288.00 KiB/s, done. Total 3 (delta 2), reused 0 (delta 0), pack-reused 0 (from 0) remote: error: unable to create temporary file: Operation not permitted remote: fatal: failed to write object error: remote unpack failed: unpack-objects abnormal exit To https://server.gslewis.id.au/git/my_repo ! [remote rejected] master -> master (unpacker error) error: failed to push some refs to 'https://server.gslewis.id.au/git/my_repo'
I spent the best part of a day fiddling around on the usually correct assumption that I had done something wrong (which still may be the case) but my RHEL sysadmin and git knowledge was not sufficient to fix the problem.
As far as I can tell, the problem isn’t with the Apache setup: there is no
push-related message in the error_log and the entries in the access_log is
the expected sequence of GET and POST requests starting with the "401
Unauthorized" GET request that leads to the prompt for htdigest username and
password.
... - [14/Aug/2026:14:37:38 +1000] "GET /git/my_repo/info/refs?service=git-receive-pack HTTP/1.1" 401 381 "-" "git/2.55.0" ... gsl [14/Aug/2026:14:37:43 +1000] "GET /git/my_repo/info/refs?service=git-receive-pack HTTP/1.1" 200 224 "-" "git/2.55.0" ... gsl [14/Aug/2026:14:37:43 +1000] "POST /git/my_repo/git-receive-pack HTTP/1.1" 200 197 "-" "git/2.55.0"
I can see that the /srv/git/my_repo/objects directory in the bare repository
has its timestamp updated to match the time of the POST request but nothing is
changed within that directory. Where exactly git is trying to create the
temporary file, I don’t know. The httpd process has a systemd-provided
"PrivateTmp" directory under /tmp.
As mentioned above, I also tried re-enabling SELinux, on the very unlikely
possibility that actually having SELinux on would fix a problem. This
involved installing the policycoreutils-python-utils package to get the
semanage utility which I could then use to set the context of the /srv/git
directory so that it was readable and writeable by the httpd process.
semanage fcontext -a -t httpd_sys_rw_content_t "/srv/git(/.*)?" restorecon -vR /srv/git/
Not surprisingly, I get the exact same git push error as with SELinux
disabled.
What works?
My initial assumption was that something was wrong with git-http-backend,
with my Apache configuration, or with my file and directory permissions.
Before giving up, I thought I would try another distribution. Recall that
Rocky Linux 10.2 was using httpd-2.4.63 and git-2.52.0.
First up was Slackware64-current with httpd-2.4.68 and git-2.55.0. I
setup Apache on a laptop (no SSL) and repeated the git repository setup
process. Both the git clone and the git push steps worked fine.
I then tried a bunch of server distributions in QEMU VMs with user networking.
- Debian 13.6 (httpd-2.4.68, git-2.47.3) worked.
- Almalinux 10.2 (httpd-2.4.63, git-2.52.0) failed.
- Rocky Linux 9.8 (httpd-2.4.62, git-2.52.0) worked.
The conclusion then is that the problem is with RHEL 10 and its derivatives or with my lack of knowledge of how to properly adminster them. The choice I have to make is either stick to Rocky 10 and use SSH to access the repository or switch to a distribution in which the "Smart HTTP" method works.