summaryrefslogtreecommitdiff
path: root/forgejno.html
diff options
context:
space:
mode:
authorCollin Williams <96917990+bluedragon1221@users.noreply.github.com>2026-05-04 09:40:42 -0500
committerCollin Williams <96917990+bluedragon1221@users.noreply.github.com>2026-05-04 09:40:42 -0500
commit85a2a330f729d70dfce1b81dc69ec15436a3d44c (patch)
treec96214d08cf397efbb98c5e67292604395933093 /forgejno.html
Initial clean commit
Diffstat (limited to 'forgejno.html')
-rw-r--r--forgejno.html148
1 files changed, 148 insertions, 0 deletions
diff --git a/forgejno.html b/forgejno.html
new file mode 100644
index 0000000..71246e7
--- /dev/null
+++ b/forgejno.html
@@ -0,0 +1,148 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>You don't need Forgejo</title>
+
+ <link rel="stylesheet" href="style.css">
+ <link rel="stylesheet" href="prose.css">
+ <style>
+ summary {
+ font-family: 'Gelasio';
+ color: #888;
+ }
+ summary::marker {
+ color: #888;
+ }
+
+ code {
+ font-size: 1rem;
+ color: #00ff78;
+ }
+ </style>
+ <script>
+ function htmlTableOfContents() {
+ var toc = document.getElementById('toc-list');
+ var headings = document.body.querySelectorAll('h1, h2, h3, h4, h5, h6');
+ headings.forEach(function (heading, index) {
+ var link = document.createElement('a');
+ link.setAttribute('href', '#'+heading.id.toLowerCase());
+ link.textContent = heading.textContent;
+
+ var li = document.createElement('li');
+ li.appendChild(link);
+
+ toc.appendChild(li);
+ });
+ }
+ </script>
+ </head>
+ <body onload="htmlTableOfContents();">
+ <div class="toc">
+ <ul id="toc-list"></ul>
+ </div>
+
+ <div class="content">
+ <h1 id="title">You Don't Need Forgejo</h1>
+ <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>
+
+ <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 I'll link tutorials for NGINX and Apache if 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>
+ <p><code>$ useradd -r -d /var/lib/cgit -s /usr/bin/git-shell git</code></p>
+
+ <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 [Socket] 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>
+ <p><code>$ chown -R git /var/www/htdocs/cgit && chmod -R 764 /var/www/htdocs/cgit</code></p>
+ </li>
+ </ol>
+
+ <h3 id="cfg-caddy">Caddy</h3>
+ <p>To complete the set up, 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 <code>git</code>
+ 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>
+ <p><code>$ sudo -u git git init --bare /var/lib/cgit/example</code></p>
+ <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 /etc/cgitrc 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>
+ </div>
+ </body>
+</html>