Full guide: lightweight Git server on OpenBSD (git-shell + cgit) This guide describes how to set up a light, secure, fully working Git server on OpenBSD. When you finish you will have: SSH access to your repositories for push/pull (dedicated user, key‑only authentication). A cgit web interface for browsing repositories at https://git.yourdomain.com. An automatically renewed Let’s Encrypt SSL certificate. All steps are performed on the OpenBSD server via SSH (preferably over a VPN, if direct SSH is blocked by your firewall rules). 0. Prerequisites You have an OpenBSD server with a working httpd, pf, and a VPN tunnel (for example, the VPN network is 10.0.0.1 – adjust as needed). Other websites may already be running on your server; their configuration in /etc/httpd.conf must be preserved. A DNS A record for git.yourdomain.com points to your server’s public IP address (e.g., 203.0.113.10) and proxy/CDN (like Cloudflare’s orange cloud) is turned off (grey cloud / DNS‑only). You have root access or can execute doas. 1. Install Git and create the dedicated user bash doas pkg_add git Create a dedicated user that will own the repositories. In this guide we use the username gituser – you may choose any name. bash doas adduser Answer the prompts as follows: username : gituser full name : Git Server shell : ksh (we will change it to git-shell later) home : /home/gituser password : leave empty (only key‑based login will be used) Set up the home directory: bash doas mkdir -p /home/gituser/.ssh doas chmod 700 /home/gituser/.ssh doas touch /home/gituser/.ssh/authorized_keys doas chmod 600 /home/gituser/.ssh/authorized_keys doas chown -R gituser:gituser /home/gituser 2. Configure SSH keys for the administrator You need an SSH key pair that will be used to push to and manage the repositories. If you already have a key, copy its public part (e.g. ~/.ssh/id_ed25519.pub) to the server. If not, generate one on your local machine: bash ssh-keygen -t ed25519 -C "git admin key" Assume the public key file is now available on the server at, for example, /tmp/your_key.pub. Add it to the gituser account with the necessary restrictions: bash echo 'no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ' | doas tee /home/gituser/.ssh/authorized_keys doas cat /tmp/your_key.pub | doas tee -a /home/gituser/.ssh/authorized_keys doas chown gituser:gituser /home/gituser/.ssh/authorized_keys doas chmod 600 /home/gituser/.ssh/authorized_keys Change the user’s shell to git-shell – this forbids interactive logins and only allows Git operations: bash doas chpass -s git-shell gituser Test the connection from your local machine (adjust the IP or VPN address accordingly): bash ssh gituser@10.0.0.1 You should see a message like “PTY allocation request failed” (login is blocked) with no password prompt. 3. Create the repositories directory bash doas mkdir -p /var/www/git-repos doas chown -R gituser:www /var/www/git-repos doas chmod 750 /var/www/git-repos Create a symbolic link so developers can use the short path /git: bash doas ln -sf /var/www/git-repos /git Initialise a bare test repository: bash doas -u gituser git init --bare /var/www/git-repos/my_project.git Now from your local machine you can push to it (replace the host address): bash git remote add origin gituser@10.0.0.1:/git/my_project.git git push -u origin master 4. Install cgit and prepare the chroot Install cgit: bash doas pkg_add cgit Create the cgit configuration file: bash doas mkdir -p /var/www/conf doas tee /var/www/conf/cgitrc << 'EOF' scan-path=/git-repos css=/cgit.css logo=/cgit.png enable-index-links=1 enable-http-clone=1 root-title=My Git Repositories root-desc=Public projects snapshots=tar.gz readme=:README.md EOF Copy static assets (logo, CSS) – these are shipped with the cgit package: bash doas cp /var/www/cgit/cgit.png /var/www/cgit/ 2>/dev/null || true doas cp /var/www/cgit/cgit.css /var/www/cgit/ 2>/dev/null || true doas chown -R www:www /var/www/cgit Add the web server user (www) to the gituser group so cgit can read the repositories: bash doas usermod -G www,gituser www doas chmod 750 /var/www/git-repos doas find /var/www/git-repos -type d -exec chmod 750 {} \; doas find /var/www/git-repos -type f -exec chmod 640 {} \; Critical step – because httpd on OpenBSD runs cgit inside a chroot at /var/www, you must copy the git binary and all its dependencies into that chroot: bash doas mkdir -p /var/www/usr/local/bin /var/www/usr/local/libexec /var/www/usr/lib /var/www/usr/local/lib /var/www/dev # Copy git doas cp /usr/local/bin/git /var/www/usr/local/bin/ # Copy the dynamic linker and required libraries doas cp /usr/libexec/ld.so /var/www/usr/libexec/ for lib in $(ldd /usr/local/bin/git | awk '/\/usr\// {print $NF}' | sort -u); do doas mkdir -p "/var/www/$(dirname "$lib")" doas cp "$lib" "/var/www/$lib" done # Create /dev/null (needed even for reading operations) doas mknod /var/www/dev/null c 2 2 doas chmod 666 /var/www/dev/null Verify that git works inside the chroot: bash doas chroot /var/www /usr/local/bin/git --version You should see the git version number. If the command fails, double‑check the copied libraries. 5. Configure httpd for the cgit subdomain Edit /etc/httpd.conf and add the following blocks at the end (be careful to use only multi‑line location stanzas, no single‑line shortcuts): text # --- git.yourdomain.com (cgit) --- server "git.yourdomain.com" { listen on egress port 80 location * { block return 301 "https://$HTTP_HOST$REQUEST_URI" } } server "git.yourdomain.com" { listen on egress tls port 443 log style combined log access "git-access.log" tls { certificate "/etc/ssl/git.yourdomain.com.fullchain.pem" key "/etc/ssl/private/git.yourdomain.com.key" } location "/cgit.css" { root "/cgit" no fastcgi } location "/cgit.png" { root "/cgit" no fastcgi } location "/*" { root "/cgi-bin/cgit.cgi" fastcgi socket "/run/slowcgi.sock" } location "/.git*" { block } } Check the syntax and restart httpd: bash doas httpd -n doas rcctl restart httpd 6. SSL certificate for the subdomain Add a section to /etc/acme-client.conf (if it does not already exist): text domain git.yourdomain.com { domain key "/etc/ssl/private/git.yourdomain.com.key" domain full chain certificate "/etc/ssl/git.yourdomain.com.fullchain.pem" sign with letsencrypt } Issue the certificate: bash doas acme-client -v git.yourdomain.com doas rcctl restart httpd To renew the certificate automatically every week, append these lines to /etc/weekly.local: bash echo 'acme-client -v git.yourdomain.com' | doas tee -a /etc/weekly.local echo 'rcctl restart httpd' | doas tee -a /etc/weekly.local 7. Start slowcgi and final verification bash doas rcctl enable slowcgi doas rcctl start slowcgi doas rcctl restart httpd Open your browser and go to https://git.yourdomain.com. You should see the cgit page listing your repositories (initially only the test repository my_project.git). If you see “No repositories found”, clear the cgit cache: bash doas rm -rf /var/cache/cgit/* doas rcctl restart slowcgi httpd Everything is ready! Your personal Git server is now fully operational. Replace every placeholder (gituser, git.yourdomain.com, your_server_ip, etc.) with your actual values throughout the configuration.