Recently I switched my home server to GNU Guix, this is a quick guide on how I setup cgit. Hope this helps some of you.

Why I use GNU Guix for my server

GNU Guix is my preferred system for every machine I use, recently I even switched my home server (back) to Guix. I use my home server to host this blog as well as my cgit instance, so far I recommend using Guix, as a hobbyist, for the following reasons:

  • Roll backs
    • I had tried both Gentoo and Debian before for my server, more than once I had one or more of my self hosted services down because an update broke my setup. My time is limited and those breakages would often occur at busy weeks where I didn’t have the time to fix them. With Guix I can just roll back to a previous working generation if an update or a misconfiguration of mine breaks something.
  • Reproducibility
    • Define your setup once, run anywhere. I do not want to manually setup anything.
  • Simplicity
    • The entirety of Guix code lives in one repo and is almost entirely written in one language, guile scheme. If you find any issues with a service or a package you can redefine it as you like in your configuration.

Why self-host a git server?

Why would you not self-host your own projects? I find it antithetical to the FOSS ethos, and perhaps even ironic, to rely on proprietary platforms for distributing free software.

Hosting a Git server is straightforward and requires minimal resources.

git-http-backend “/” uri issue and fix

Guix has a separate configuration data type for serving Git repositories over HTTP. Currently, the regex it uses does not allow the root "/" to be used a URI for serving (ref archive).

The issue is in the (gnu services version-control) module, specifically in this part:

(define* (git-http-nginx-location-configuration #:optional
                                                (config
                                                 (git-http-configuration)))
  (match config
    (($ <git-http-configuration> package git-root export-all?
                                 uri-path fcgiwrap-socket)
     (nginx-location-configuration
      ;; ISSUE here: regex won't allow for "/"
      (uri (string-append "~ /" (string-trim-both uri-path #\/) "(/.*)"))
      (body
       (list
        (list "fastcgi_pass " fcgiwrap-socket ";")
        (list "fastcgi_param SCRIPT_FILENAME "
              package "/libexec/git-core/git-http-backend"
              ";")
        "fastcgi_param QUERY_STRING $query_string;"
        "fastcgi_param REQUEST_METHOD $request_method;"
        "fastcgi_param CONTENT_TYPE $content_type;"
        "fastcgi_param CONTENT_LENGTH $content_length;"
        (if export-all?
            "fastcgi_param GIT_HTTP_EXPORT_ALL \"\";"
            "")
        (list "fastcgi_param GIT_PROJECT_ROOT " git-root ";")
        (list "fastcgi_param GIT_CONFIG_GLOBAL "
              (plain-file "gitconfig"
                          (string-append "[safe]\n\tdirectory = " git-root "/*\n"))
              ";")
        "fastcgi_param PATH_INFO $1;"))))))

Thankfully, we can easily resolve this by redefining (gnu services version-control).

  • My changes (diff) to fix this issue, as well as specify git actions:
diff --git a/gnu/services/version-control.scm b/gnu/services/version-control.scm
index a7f40812a6c..e33e5de21f0 100644
--- a/gnu/services/version-control.scm
+++ b/gnu/services/version-control.scm
@@ -235,7 +235,11 @@ (define* (git-http-nginx-location-configuration #:optional
     (($ <git-http-configuration> package git-root export-all?
                                  uri-path fcgiwrap-socket)
      (nginx-location-configuration
-      (uri (string-append "~ /" (string-trim-both uri-path #\/) "(/.*)"))
+      (uri (string-append "~ "
+			  (if (string=? uri-path "/")
+                              ""
+                              (string-append "/" (string-trim-both uri-path #\/)))
+			  "/(info/refs|git-upload-pack|git-receive-pack)"))
       (body
        (list
         (list "fastcgi_pass " fcgiwrap-socket ";")
@@ -254,7 +258,7 @@ (define* (git-http-nginx-location-configuration #:optional
               (plain-file "gitconfig"
                           (string-append "[safe]\n\tdirectory = " git-root "/*\n"))
               ";")
-        "fastcgi_param PATH_INFO $1;"))))))
+        "fastcgi_param PATH_INFO $uri;"))))))


 ;;;

You can find the full module that I use here in my dotfiles repo.

I’d recommend creating your own module and load it by running the following command:

sudo guix system reocnfigure ~/dotfiles/guix/server-config.scm -L ~/dotfiles/guix

Configuring cgit-service-type

Guix provides a cgit service module (gnu services cgit).

A couple things to note:

  • You can provide css and favicon using an external url. You can find the css styling that I use here.

Example cgit-service-type

(define my/git-repos
  (list
   (repository-cgit-configuration
     (url "repo1")
     (name "repo1")
     (desc "Example Repo 1")
     (path "/srv/git/repo1")
     (section "Section 1"))
   (repository-cgit-configuration
     (url "repo2")
     (name "repo2")
     (desc "Example Repo 2")
     (path "/srv/git/repo2")
     (homepage "https://myhomepage")
     (section "Section 2"))))

(service cgit-service-type
         (cgit-configuration
           (package cgit)
           (root-desc "Root Desc")
           (repository-directory "")
           (root-title "Root Title")
           (repositories my/git-repos)
           (enable-index-owner? #f)
           (enable-http-clone? #f)
           (clone-prefix '("https://git.thanosapollo.org")) ;; change this to your own url
           ;; Cache
           (cache-root "/var/cache/cgit")
           ;; 20000 = 5 hours.  Feel free to adjust this.
           (cache-size 20000)
           (cache-static-ttl 20000)
           (cache-dynamic-ttl 20000)
           (cache-repo-ttl 20000)
           (cache-root-ttl 20000)
           (cache-scanrc-ttl 20000)
           (cache-about-ttl 20000)
           (cache-snapshot-ttl 20000)
           (case-sensitive-sort? #f)
           (css "https://thanosapollo.org/cgit.css") ;; My personal css.
           (nginx
            (list
             (nginx-server-configuration
               (server-name '("git.thanosapollo.org"))
               (root cgit)
               (try-files (list "$uri" "@cgit"))
               ;; I use cloudflare tunnel, which handles certs for me.
               (listen '("80"))
               (ssl-certificate #f)
               (ssl-certificate-key #f)
               ;; ;; If you use certbot try the following:
               ;; (listen '("443 ssl"))
               ;; (ssl-certificate
               ;;  "/etc/certs/git.my-host.org/fullchain.pem")
               ;; (ssl-certificate-key
               ;;  "/etc/certs/git.my-host.org/privkey.pem")
               (locations
                (list
                 (nginx-location-configuration
                   (uri "@cgit")
                   (body '("fastcgi_param SCRIPT_FILENAME $document_root/lib/cgit/cgit.cgi;"
                           "fastcgi_param PATH_INFO $uri;"
                           "fastcgi_param QUERY_STRING $args;"
                           "fastcgi_param HTTP_HOST $server_name;"
                           "fastcgi_pass 127.0.0.1:9000;")))
                 (git-http-nginx-location-configuration
                  (git-http-configuration
                    (uri-path "/")
                    (export-all? #t))))))))))

You can find my home server configuration here.