summaryrefslogtreecommitdiff
path: root/forgejno.html
blob: e36b358e630794d5c9667011436e46fb77028db7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
<!DOCTYPE html>
<html>
	<head>		
		<title>You don't need Forgejo</title>
		<meta property="article:published_time" content="2026-05-03">
		<link rel="stylesheet" href="prose.css">
		<script src="article.js"></script>
	</head>
	<body>
		<article>
			<header></header>

			<p>
				<a href="https://mitchellh.com/writing/ghostty-leaving-github">People</a>
				<a href="https://vinyl-cache.org/organization/moving.html">ditching</a>
				GitHub after
				<a href="https://github.blog/news-insights/company-news/an-update-on-github-availability">recent issues</a>
				are looking to self-hosting Forgejo as an alternative.
			</p>
			<p>Despite some (poorly disclosed) <a href="https://dustri.org/b/carrot-disclosure-forgejo.html">security issues</a>, I think there's a more fundemental reason why you shouldn't use Forgejo.</p>
			<p>If you're just a single person trying to publish your independent coding projects, why do you need: CI, Issue Tracking, Pull Requests, Multi-user account management, etc?</p>
			<p>just use <a href="https://git.zx2c4.com/cgit">cgit</a>.</p>

			<div id="toc"></div>

			<h2 id="forge">How do git forges (like GitHub) even work?</h2>
			<p>A git forge is basically just a directory of git repositories, served over http or ssh, with a fancy web frontend.</p>
			<p>On the server, since the repos don't need to store mutable state (local commits, staged files, etc) they're stored in a special format that's basically just the <code>.git</code>
				directory of a normal git repository.</p>
			<p>This is called a bare repository.</p>

			<h2 id="cgit">What is cgit?</h2>
			<p>cgit is essentially just a web-based frontend for these bare repos.</p>
			<p>While full-on software forges (like GitHub or Forgejo) give you many extra features, cgit is just the read-only frontend, with nothing else included.</p>
			<p>Interested?</p>

			<h2 id="guide">cgit Setup Guide</h2>
			<p>The cgit setup process is fairly involved, but if you were planning on self-hosting Forgejo, then you can handle it.</p>
			<p class="comment">I'll be using Caddy as the webserver for this setup, but this could be modified for NGINX and Apache if you prefer xml configuration files and manual https certificates ;&rpar;</p>

			<p>To keep everything organized, I'll run cgit under a user called "git". Our git repos will be stored in this directory.</p>
			<pre><code>$ useradd -r -d /var/lib/cgit -s /usr/bin/git-shell git</code></pre>

			<h3 id="cfg-fcgiwrap">fcgiwrap</h3>
			<p>cgit is served as a <a href="https://en.wikipedia.org/wiki/Common_Gateway_Interface">cgi application</a>.</p>
			<p>A long time ago in a galaxy far far away, web servers had builtin support for cgi, but now its often delegated to <a href="https://github.com/gnosek/fcgiwrap">fcgiwrap</a>.</p>
			<p>So our first step will be setting up fcgiwrap.</p>

			<ol>
				<li><p>Install fcgiwrap with a package manager. This will create two systemd files: fcgiwrap.service and fcgiwrap.socket.</p></li>
				<li><p>Open fcgiwrap.service in a text editor (<code>$ systemctl edit fcgiwrap.service</code>) and set the User and Group to be our "git" user.</p></li>
				<li><p>Open fcgiwrap.socket (<code>$ systemctl edit fcgiwrap.socket</code>) and add "SocketUser=caddy" and "SocketGroup=caddy" under the <code>[Socket]</code> section. (replace with the correct Caddy user for your system)</p></li>
			</ol>

			<h3 id="cfg-cgit">cgit</h3>
			<p>Since cgit is a cgi application, it does not run as its own service. It is simply a script that gets executed by Caddy and piped into fcgiwrap. As such, the setup is pretty simple:</p>
			<ol>
				<li><p>Install cgit with a package manager. This will place the cgit files at /var/www/htdocs/cgit</p></li>
				<li>
					<p>Make sure that location is owned by "git", but is readable by all users:</p>
					<code>$ chown -R git /var/www/htdocs/cgit && chmod -R 764 /var/www/htdocs/cgit</code>
				</li>
			</ol>

			<h3 id="cfg-caddy">Caddy</h3>
			<p>To complete the setup, add this block to the Caddyfile to serve cgit.</p>
			<pre><code>git.my.tld {
    @assets path /cgit.css /cgit.js /favicon.svg /robots.txt
    handle @assets {
        root * /var/www/htdocs/cgit
        file_server
    }

    reverse_proxy unix//run/fcgiwrap-cgit.sock {
        transport fastcgi {
            env SCRIPT_FILENAME /var/www/htdocs/cgit/cgit.cgi
        }
    }
}</code></pre>

			<h3 id="cfg-shell">Lock down SSH access</h3>
			<p>Using <code>git-shell</code> blocks normal shell access and allows only git-style SSH commands.</p>
			<ol>
				<li><p>Create an SSH directory for the git user: <code>$ install -d -o git -g git -m 700 /var/lib/cgit/.ssh</code></p></li>
				<li><p>Add your public key to <code>/var/lib/cgit/.ssh/authorized_keys</code>
					with mode <code>600</code>.</p></li>
			</ol>

			<h3 id="cfg-repos">Create some repos</h3>
			<p>Create a bare repository:</p>
			<code>$ sudo -u git git init --bare /var/lib/cgit/example</code>
			<p>Push to it from your local machine:</p>
			<pre><code>$ git remote add origin git@git.my.tld:example
$ git push -u origin main</code></pre>

			<h3 id="cfg-cgitrc">cgitrc</h3>
			<p>A clean default <code class="path">/etc/cgitrc</code> file might look like this:</p>
			<pre><code>css=/cgit.css
logo=
virtual-root=/

enable-index-owner=0
enable-http-clone=1
scan-path=/var/lib/cgit

root-title=git.my.tld
root-desc=Personal git repositories
snapshots=tar.gz zip</code></pre>

			<h2 id="tradeoffs">Tradeoffs</h2>
			<p>You lose integrated issues, pull requests, built-in CI, orgs, and a social graph.</p>
			<p>In return, you get a tiny stack, fewer moving parts, lower attack surface, lower resource use, and a setup you can understand.</p>
			<p>For my personal projects, that trade is acceptable.</p>

			<footer>
				<hr>
				<p class="comment">Discuss on <a href="https://bsky.app/profile/cwilliams1221.bsky.social/post/3mkyog3enls2w">Bluesky</a></p>
			</footer>
		</article>
	</body>
</html>