cgit — Deployment Guide

Overview

cgit runs as a CGI application under a web server. This guide covers compilation, installation, web server configuration, and production tuning.

Prerequisites

Build dependencies:

  • GCC or Clang (C99 compiler)
  • GNU Make
  • OpenSSL or compatible TLS library (for libgit HTTPS)
  • zlib (for git object decompression)
  • Optional: Lua or LuaJIT (for Lua filters)
  • Optional: pkg-config (for Lua detection)

Runtime dependencies:

  • A CGI-capable web server (Apache, Nginx+fcgiwrap, lighttpd)
  • Git repositories on the filesystem

Building

# Clone/download the source
cd cgit/

# Build with defaults
make

# Or with custom settings
make prefix=/usr CGIT_SCRIPT_PATH=/var/www/cgi-bin \
     CGIT_CONFIG=/etc/cgitrc CACHE_ROOT=/var/cache/cgit

# Install
make install

Build Variables

Variable Default Description
prefix /usr/local Installation prefix
CGIT_SCRIPT_PATH $(prefix)/lib/cgit CGI binary directory
CGIT_DATA_PATH $(prefix)/share/cgit Static files (CSS, images)
CGIT_CONFIG /etc/cgitrc Default config file path
CACHE_ROOT /var/cache/cgit Default cache directory
CGIT_SCRIPT_NAME "/" Default CGI script name
NO_LUA (unset) Set to 1 to disable Lua

Installed Files

$(CGIT_SCRIPT_PATH)/cgit.cgi    # CGI binary
$(CGIT_DATA_PATH)/cgit.css      # Stylesheet
$(CGIT_DATA_PATH)/cgit.js       # JavaScript
$(CGIT_DATA_PATH)/cgit.png      # Logo image
$(CGIT_DATA_PATH)/robots.txt    # Robots exclusion file

Apache Configuration

CGI Module

# Enable CGI
LoadModule cgi_module modules/mod_cgi.so

# Basic CGI setup
ScriptAlias /cgit/ /usr/lib/cgit/cgit.cgi/
Alias /cgit-data/ /usr/share/cgit/

<Directory "/usr/lib/cgit/">
    AllowOverride None
    Options +ExecCGI
    Require all granted
</Directory>

<Directory "/usr/share/cgit/">
    AllowOverride None
    Require all granted
</Directory>

URL Rewriting (Clean URLs)

# Enable clean URLs via mod_rewrite
RewriteEngine On
RewriteRule ^/cgit/(.*)$ /usr/lib/cgit/cgit.cgi/$1 [PT]

With corresponding cgitrc:

virtual-root=/cgit/
css=/cgit-data/cgit.css
logo=/cgit-data/cgit.png

Nginx Configuration

Nginx does not support CGI natively. Use fcgiwrap or spawn-fcgi:

With fcgiwrap

# Install fcgiwrap
# Start it (systemd, OpenRC, or manual)
fcgiwrap -s unix:/run/fcgiwrap.sock &
server {
    listen 80;
    server_name git.example.com;

    root /usr/share/cgit;

    # Serve static files directly
    location /cgit-data/ {
        alias /usr/share/cgit/;
    }

    # Pass CGI requests to fcgiwrap
    location /cgit {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi;
        fastcgi_param PATH_INFO $uri;
        fastcgi_param QUERY_STRING $args;
        fastcgi_param HTTP_HOST $server_name;
        fastcgi_pass unix:/run/fcgiwrap.sock;
    }
}

With spawn-fcgi

spawn-fcgi -s /run/cgit.sock -n -- /usr/bin/fcgiwrap

lighttpd Configuration

server.modules += ("mod_cgi", "mod_alias", "mod_rewrite")

alias.url = (
    "/cgit-data/" => "/usr/share/cgit/",
    "/cgit/"      => "/usr/lib/cgit/cgit.cgi"
)

cgi.assign = (
    "cgit.cgi" => ""
)

url.rewrite-once = (
    "^/cgit/(.*)$" => "/cgit/cgit.cgi/$1"
)

Configuration File

Create /etc/cgitrc:

# Site identity
root-title=My Git Server
root-desc=Git repository browser
css=/cgit-data/cgit.css
logo=/cgit-data/cgit.png
favicon=/cgit-data/favicon.ico

# URL routing
virtual-root=/cgit/

# Features
enable-commit-graph=1
enable-blame=1
enable-http-clone=1
enable-index-links=1
snapshots=tar.gz tar.xz zip
max-stats=quarter

# Caching (recommended for production)
cache-size=1000
cache-root=/var/cache/cgit
cache-root-ttl=5
cache-repo-ttl=5
cache-static-ttl=-1

# Repository discovery
scan-path=/srv/git/
section-from-path=1
enable-git-config=1

# Filters
source-filter=exec:/usr/lib/cgit/filters/syntax-highlighting.py
about-filter=exec:/usr/lib/cgit/filters/about-formatting.sh

Cache Directory Setup

# Create cache directory
mkdir -p /var/cache/cgit

# Set ownership to web server user
chown www-data:www-data /var/cache/cgit
chmod 700 /var/cache/cgit

# Optional: periodic cleanup cron job
echo "*/30 * * * * find /var/cache/cgit -type f -mmin +60 -delete" | \
    crontab -u www-data -

Repository Permissions

The web server user needs read access to all git repositories:

# Option 1: Add web server user to git group
usermod -aG git www-data

# Option 2: Set directory permissions
chmod -R g+rX /srv/git/

# Option 3: Use ACLs
setfacl -R -m u:www-data:rX /srv/git/
setfacl -R -d -m u:www-data:rX /srv/git/

HTTPS Setup

For production, serve cgit over HTTPS:

server {
    listen 443 ssl;
    server_name git.example.com;

    ssl_certificate /etc/ssl/certs/git.example.com.pem;
    ssl_certificate_key /etc/ssl/private/git.example.com.key;

    # ... cgit configuration ...
}

server {
    listen 80;
    server_name git.example.com;
    return 301 https://$server_name$request_uri;
}

Performance Tuning

Enable Caching

The response cache is essential for performance:

cache-size=1000          # number of cache entries
cache-root-ttl=5         # repo list: 5 minutes
cache-repo-ttl=5         # repo pages: 5 minutes
cache-static-ttl=-1      # static content: forever
cache-about-ttl=15       # about pages: 15 minutes

Limit Resource Usage

max-repo-count=100       # repos per page
max-commit-count=50      # commits per page
max-blob-size=512        # max blob display (KB)
max-message-length=120   # truncate long subjects
max-repodesc-length=80   # truncate descriptions

Use Lua Filters

Lua filters avoid fork/exec overhead:

source-filter=lua:/usr/share/cgit/filters/syntax-highlight.lua
email-filter=lua:/usr/share/cgit/filters/email-libravatar.lua

Optimize Git Access

# Run periodic git gc on repositories
for repo in /srv/git/*.git; do
    git -C "$repo" gc --auto
done

# Ensure pack files are optimized
for repo in /srv/git/*.git; do
    git -C "$repo" repack -a -d
done

Monitoring

Check Cache Status

# Count cache entries
ls /var/cache/cgit/ | wc -l

# Check cache hit rate (if access logs are enabled)
grep "cgit.cgi" /var/log/nginx/access.log | tail -100

Health Check

# Verify cgit is responding
curl -s -o /dev/null -w "%{http_code}" http://localhost/cgit/

Docker Deployment

FROM alpine:latest

RUN apk add --no-cache \
    git make gcc musl-dev openssl-dev zlib-dev lua5.3-dev \
    fcgiwrap nginx

COPY cgit/ /build/cgit/
WORKDIR /build/cgit
RUN make && make install

COPY cgitrc /etc/cgitrc
COPY nginx.conf /etc/nginx/conf.d/cgit.conf

EXPOSE 80
CMD ["sh", "-c", "fcgiwrap -s unix:/run/fcgiwrap.sock & nginx -g 'daemon off;'"]

systemd Service

# /etc/systemd/system/fcgiwrap-cgit.service
[Unit]
Description=fcgiwrap for cgit
After=network.target

[Service]
ExecStart=/usr/bin/fcgiwrap -s unix:/run/fcgiwrap.sock
User=www-data
Group=www-data

[Install]
WantedBy=multi-user.target

Troubleshooting

Symptom Cause Solution
500 Internal Server Error CGI binary not executable chmod +x cgit.cgi
Blank page Missing CSS path Check css= directive
No repositories shown Wrong scan-path Verify path and permissions
Cache errors Permission denied Fix cache dir ownership
Lua filter fails Lua not compiled in Rebuild without NO_LUA
Clone fails enable-http-clone=0 Set to 1
Missing styles Static file alias wrong Check web server alias config
Timeout on large repos No caching Enable cache-size

Was this handbook page helpful?

This page is part of the Project Tick Handbook, which is licensed under the Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license. View full license details.
Last updated: April 18, 2026