Skip to main content

Command Palette

Search for a command to run...

Gitea for Version Control

Home Lab 2.0 - Part 06

Updated
β€’5 min read
Gitea for Version Control
J
Welcome to my blog, where I share a few more words. No AI life forms were harmed in the writing of these posts πŸ˜‰ Any mistakes are entirely the fault of this carbon-based life form.

Hey, Good to see you around here! πŸ‘‹πŸ˜Š

🧡 This is the part 06 of the Home Lab 2.0 series.

It took me almost 8 months to write this post 😬. Mostly I feel like these posts are increasingly becoming irrelevant. Hear me out.

We are at a critical point as a society in how we consume information on the web. Major search engines, especially Google are increasingly pushing AI-generated summaries as the primary response to search queries. From there, we are encouraged (🦾 strongly) to continue interacting with the AI through follow-up questions. Over time, this could reduce our incentive to visit independent blogs and websites altogether. I'm acutely aware of the change in my own behaviour in this context.

I'm not against AI (heck, you could say they're our carbon-neutral counterparts πŸ‘¨πŸ€–), but I disagree with the approach taken by big tech companies. They’re forcing these tools onto us while effectively turning all of us into their beta-testers.

That said, I decided to keep writing, at least for my own sake πŸ§˜β€β™‚οΈ.

My initial plan was to use GitLab as the version control system. However after reading about Gitea, I decided to give it a try. It is very lightweight and has many comparable features. Most importantly it is super easy to run on an LXC container.

I tried to stick to the plan of deploying the infra ( in this case the LXC ) using Terraform and then configuration using Ansible. Since I already have the foundation built for the rest of the home-lab this was relatively straight forward.

However I spent some time figuring out the curly issues related to Keycloak SSO integration. Claude was quite useful in this regard. Gemini was off the mark in many ways and ChatGPT was... well.. I don't really know.

As alwasys, the code is in the repo. Check it out.

Keycloak SSO Red herrings πŸ”΄πŸŸ

1. kcadm.sh silently drops protocol mapper config in Keycloak 26.x

When creating protocol mappers using kcadm.sh create protocol-mappers, the config block is silently ignored regardless of whether you pass it as heredoc JSON, -s flags, or -f file. The mapper is created but with an empty config: {}, meaning the groups claim is never included in tokens. Thanks Keycloak!

Fix: Use the Keycloak REST API directly via ansible.builtin.uri instead of kcadm.sh for any mapper creation tasks. See keycloak-gitea-integration.yml and keycloak-vault-integration.yml for the working pattern.

2. emailVerified defaults to false β€” blocks Gitea auto-registration

When creating users via kcadm.sh, emailVerified defaults to false unless explicitly set. Gitea checks this field and will silently refuse to auto-create the account, routing the user to the link_account page instead with a generic "Registration is disabled" error.

Fix: Always set "emailVerified": true in all user creation and update tasks.

3. ALLOW_ONLY_EXTERNAL_REGISTRATION alone is not enough for auto-registration

Setting ALLOW_ONLY_EXTERNAL_REGISTRATION = true in [service] controls who can register (external OAuth users only), but does not actually trigger auto-account creation on first OAuth login. Without the [oauth2_client] section, Gitea routes every new OAuth user to the link_account page with "Registration is disabled" β€” even though the setting is correct.

Fix: Add this section to app.ini:

[oauth2_client]
ENABLE_AUTO_REGISTRATION = true
ACCOUNT_LINKING          = auto
USERNAME                 = preferred_username
REGISTER_EMAIL_CONFIRM   = false

4. --scopes flag in gitea admin auth add-oauth only takes the first word

When passing --scopes "openid profile email" as a quoted string to the Gitea CLI, only openid is stored. The Gitea CLI splits on spaces and treats profile and email as separate unrecognised arguments without erroring.

Fix: Use a variable: gitea_oauth2_scopes: "openid profile email" and reference it in the command. Alternatively, verify the scopes in the DB after deployment:

SELECT cfg::jsonb->'Scopes' FROM login_source WHERE name = 'keycloak';

Expected: ["openid", "profile", "email"]

5. Gitea OAuth2 source update-oauth does not reset missing fields

If --scopes was missing when the source was first created, running update-oauth without --scopes will leave the old value intact. Always include all flags on both add-oauth and update-oauth commands.

6. Always verify token claims independently of the application

When OAuth/OIDC login is failing silently, decode the token directly to verify what claims are actually being sent. Enable directAccessGrantsEnabled temporarily on the Keycloak client, get a token via curl, and decode the JWT payload:

curl -s -X POST http://localhost:8080/realms/homelab/protocol/openid-connect/token \
  -d "client_id=gitea&client_secret=<secret>&username=<user>&password=<pass>&grant_type=password" \
  | python3 -c "
import sys,json,base64
d=json.load(sys.stdin)
p=d['access_token'].split('.')[1]
p+='='*(4-len(p)%4)
print(json.dumps(json.loads(base64.urlsafe_b64decode(p)),indent=2))
"

⚠️ Remember to disable direct grants again after testing.

Finally πŸŽ‰

Reflection πŸ€”

At this point, I’ve stopped busting my rear trying to figure out code by combing through forums, blog posts, and product documentation. As I mentioned at the start of this post, I’m finding AI (especially Claude) to be a useful tool. It can generate code, and while it’s far from perfect, with enough patience and some trial-and-error cycles, I can usually get it into a working state. At least, that has been my experience so far.

I’m still not really sure whether this approach is actually productive, as I end up spending a lot of time fixing and testing things anyway πŸ™„. That said, one thing is clear: something feels missing when I rely on AI to generate large amounts of code. Maybe it’s nostalgia, or maybe it’s the satisfaction that came from digging through documentation, forums, and blog posts to solve problems manually. It's like fixing something around the house by myself vs hiring a contractor. Maybe..

I honestly don’t know where this all leads 🎭.

Thank you for sticking this far, I will see you (if you are a human) soon with another episode of this Home Lab series.

Have a look at the human+AI collaborated code for this deployment in my repo. Who knows, you may find a thing or two that may be useful πŸ˜…

Reference πŸ“š

Gitea documentation. It is seriously good!

Home Lab 2.0

Part 1 of 6

In this series, I will write about the process of setting up my Home Lab, again!. As usual I will automate things as much as possible, with Terraform, Ansible, Bash and other beautiful things. Hopefully there will be something useful for you as well!

Up next

Vault for Secret Management

Home Lab 2.0 - Part 05