Gitea, a First Look

I've spent the last week or so trying to configure Gitea with Ansible and not entirely succeeding. This is going to be a short tour through the problems I encountered and my impressions of the product.

(I'm forced to acknowledge that this post won't be of much use to anyone: if you're not familiar with Gitea, this is too advanced and scattered, and if you are it's probably pointless. Maybe I just needed to whinge.)

What Is Gitea

Gitea calls itself "a community managed lightweight code hosting solution written in Go." It's open source, and what it doesn't say is that "it's a damn good GitHub clone." GitLab was trying for similar functionality to GitHub - they succeeded, and possibly surpassed GitHub. But they never aimed to look like GitHub, whereas that's exactly what Gitea has done. Unfortunately GitLab took a kitchen sink approach that requires a machine with at least 8G of memory, preferably 16G - and on a limited budget, that's a painful cloud instance to pay for. Gitea has your answer though: they claim Gitea will run comfortably on a Raspberry Pi. I was skeptical - but not anymore. I installed it on Digital Ocean's smallest instance (1G of memory) and with all its cloned GitHub functionality (including your choice of backend database support and 2FA to name the ones that surprised me the most) the damn thing has never cracked the 500M mark on memory usage. Even when we uploaded our favourite monster repository (3.8G without any branches, don't even ask). This thing is heavy on the functionality and LIGHT on memory.

What it was Meant For

Gitea was meant to be a clone of GitHub - stop and think about that. It's meant to have tens of thousands of public-facing repos (and thousands of users). It supports MySQL, MariaDB, and PostgreSQL on the back end. But it also supports SQLite - and let's face it, with 20 people and a repo count under 100, we don't need more than SQLite (it's lightweight and very fast). The other major difference of opinion between their intention and my use case is that the default behaviour is to make everything public. Not just "public" within the organization using it, that's not how Gitea thinks about this: public to the entire internet. Happily, there's a switch in the config that allowed me to force all repositories to be private - period, end of story. I think I'd rather have toggled it to "private by default but switchable," but that wasn't an option. Likewise, I had to find all of the three or four different ways it allowed self-account creation (OpenID and the main login screen - I think there was at least one more) and disable those.

The Ansible Caveats

Gitea wasn't designed with Ansible in mind. The assumption is that you (the administrator) will log in after start-up and tweak the configuration. Of course, many of the settings can't be changed through the UI, only in the app.ini configuration file. But on first run, Gitea creates a SECRET_KEY and an INTERNAL_TOKEN (and a couple other secrets depending on whether or not you use OAuth2 or LFS) which look like strings of random characters, but apparently conform to some cryptographic standard because my strings of random characters didn't work. You see the problem here? Templating is hard if you have to tell it what properties you want, get it to generate secrets, then do more templating. (Or embed secrets in your template, and then they also don't get refreshed the next time you create a new Gitea instance.) But it gets worse: once you start using SECRET_KEY, don't EVER change it: if you do, every previous 2FA user will get a huge 500 error when they try to log in, because 2FA is reliant on that key. I can't tell you what the others are for, but I have to assume they're just as sensitive to change after first start.

At this point our pattern is to template in a very secure configuration file without secrets. Gitea fills in the secrets at startup. At that point, the machine is so secured that no one can log in - not even an admin. If you know your DB well enough, the thing to do would be to reach into it with Ansible and add an admin user. Instead, I put Gitea in install mode by hand (INSTALL_LOCK = false) long enough to add the admin user, then lock it down again. And the plan for re-templating is to syphon off the secrets into Ansible variables so that we can put them back into the template if we find the config already exists. It's going to be a PITA, but should allow templating after the first run.