Along with GitHub's "one 9 of reliability" and the general enshittification of Microsoft products1 due to pushing AI down the throats of all customers as well as their own developers, a debloat trend has started where a non-negligible number of more tech-savvy users started to move away from mainstream services and either moved to more privacy-respecting alternatives or went all in with self-hosting their own instances.
I have been self-hosting different services for many years now but replacing GitHub has not crossed my mind once, at least not in a serious manner. I've enjoyed the product for many years now and never felt the need to replace it. The free plan is quite generous with the GitHub Actions usage and package registry storage, and there's not many other features that I would expect from a Git hosting. It just worked. But by focusing on pushing Copilot on every repository there is, GitHub has stopped being the reliable Git hosting they were. And since using their product is no longer pain-free, there were now multiple reasons to switch. Fortunately, I've read plenty of good things about Forgejo so that would be the next safe haven for my projects.
Self-hosting a Forgejo instance
Oh, how I love simple software. Coming up with a Docker Compose file to host the Forgejo instance took no longer than a couple of minutes, whereas self-hosting Nextcloud took me a couple days to get right.
The code below is the compose.yaml that I'm using. I could not imagine working with a Git server just using HTTP, I've opted for the SSH passthrough which works well. Naturally, the Docker volumes are being backed up using my simple homelab backup strategy. I use Caddy as my reverse proxy so by adding Forgejo to the reverse_proxy network and adding a short entry in my Caddyfile, I am able to provision certificates and route the traffic accordingly.
services:
forgejo:
container_name: forgejo
image: codeberg.org/forgejo/forgejo:14-rootless
restart: unless-stopped
environment:
USER_UID: 1000
USER_GID: 1000
networks:
- reverse_proxy
ports:
- 222:2222
volumes:
- /etc/localtime:/etc/localtime:ro
- forgejo_data:/var/lib/gitea
- forgejo_config:/etc/gitea
- ./custom:/var/lib/gitea/custom
volumes:
forgejo_data:
name: forgejo_data
forgejo_config:
name: forgejo_config
networks:
reverse_proxy:
external: true
I've used the extra time I had left after setting up the instance to slightly customize the homepage and the layout colors, just to add some of that personal pizzazz. This is what the ./custom:/var/lib/gitea/custom bind mount does.
Migrating to Forgejo
I'll be honest - this isn't a very "correct" way to perform this migration. I've basically done it the simplest way possible because:
- I had "only" about 50 repositories in total that I wanted to migrate (both on my personal and organization accounts).
- I didn't care about migrating packages. I believe most of my open-source software is anyways used by myself only so I just wanted to migrate the code first and I would then re-create the packages on the Forgejo registry using Forgejo Actions.
So, what I did was that I generated a GitHub personal access token and went manually (!) through each of the repositories I wanted to migrate from GitHub, copied the link over to Forgejo's "Migrate from GitHub" form along with the access token I generated previously and selected "This repository will be a mirror" if I knew I'll have to make some commits to GitHub before I finish the full migration (at this point I already knew my Forgejo Actions would not work out of the box, more on that later) and repeat that process until I was finished2.
Forgejo Actions and a self-hosted runner
This was definitely the tricky part. Forgejo did a good job with the compatibility layer so most of the steps in my workflows worked just fine. Since I wanted to change the package registry, I had to update all workflows that deploy Docker images to now point at my Forgejo instance. For reasons unknown to me, I also could no longer rely on ${{ GITHUB_TOKEN }} and had to generate a new registry token that I used in my actions. And there was of course the setup of the runner itself.
Forgejo Runner is unfortunately not straightforward to configure. The installation itself, depending on whether you want to run the binary or a container, is ok-ish. Where I ran into most trouble was when setting up Docker-in-Docker because I need to use Docker in Actions (and so do you, probably). I don't blame Forgejo for this inconvenience at all, since the "why doesn't it just work" section was most likely created exactly because they also understand it's a cumbersome process.
But the abovementioned problems were minor compared to the big one. I wanted to have a dedicated machine acting as the runner so I dusted off my Raspberry Pi and set up the Forgejo runner on it. But the problem was that the Pi has an ARM-based CPU and I'm hosting my services on an AMD64-based CPU powered server. So I had to build my packages for another CPU architecture. Fortunately, I was at least a bit experienced on how to do it with GitHub Actions because I was running QEMU and Docker Buildx Actions to build ARM packages from AMD servers. So now I had to go the other way around. This took quite a bit of time depending on the language/build tool I was using. I had major trouble getting Bun to build in QEMU, most likely due to this long-standing issue with emulators. Eventually I was able to get every single one of my packages up in the new registry (though I did resort to atomic solutions such as rewriting the whole project in another language).
Summary
I am super happy with having Forgejo in my homelab. Not only is Forgejo more responsive than GitHub, but I can also back up my Git repositories the proper way now as previously I was running a scheduled weekly backup with a custom Bash script using the GitHub API. I also appreciated the opportunity to learn about how action runners work. I advise you to try and migrate from GitHub regardless of whether you want to have better control of your data or simply are tired of GitHub's lack of focus on what really matters.



Top comments (0)