Announcement 13 min read
19

Migrating 200 projects from GitHub to self‑hosted Forgejo

Viktor Shchelochkov
Viktor Shchelochkov Fullstack developer

Following self-hosting trend and hype and my own self-hosting & degoogle/demicrosoft/debloat journey that has been going for three years now, I'm adding another entry to this list:

  • Google Password Manager → Bitwarden → Vaultwarden
  • Gmail → selfhosted postfix & dovecot, macOS Mail client & K9 Mail
  • Google Chrome → Firefox
  • Google Analytics → self-hosted Sentry
  • Google Authenticator → Aegis
  • Google Drive → physical ssd
  • Google Maps → OpenStreetMap → Organic Maps
  • Google Search → DuckDuckGo
  • Google Passkeys → hardware passkeys & pgp
  • Google Fonts → Fontsource
  • Google Account / sign in with Google → Email registration
  • Google Translate → Deepl
  • Google reCAPTCHA → hCaptcha
  • Google Wallet → Apple Pay & credit card nfc
  • YouTube → still YouTube but with uBlock origin + sponsorblock
  • Google Play → APKs & f-droid, soon going to switch to AppStore + AltStore
  • Cloudflare → no Cloudflare (except R2 and static pages.dev)
  • Telegram → Matrix
  • iCloud → E2ee iCloud mode
  • GitHub → Forgejo

For the last 6 days I have been moving all of my repositories from GitHub to my own self-hosted git server powered by Forgejo. I wanted to do that for a long time for several reasons:

  1. I don't trust Microsoft or GitHub. I believe my private projects should be securely stored on my own infra, including secret keys allowing access to third party platforms. I'm also familiar with GitHub erasing repositories without possibility of recovery, so unless I store copies of every git repository locally, it's just a matter of time when I cease to have access to them.
  2. I need more freedom on remote git. I want to upload LFS for free and keep 100MB+ files tracked in git. I want direct database management. I want to customize interface to my liking. I don't want to be limited in CI/CD workflows or in what I can use in private organizations.
  3. Political reasons. I don't like Microsoft politics and I certainly don't like GitHub taking funds from fascists (ICE). See dear-github-2.0.

Also some smaller benefits like better performance technical-wise because the latency for Action workflows, pulls, deploys is practically zero and custom patches to git server and git frontend.

Migrating #

Private repositories — my very old projects that aren't good enough to show them to the world — were the easiest to migrate since they are mostly just small side projects with no actions workflows and releases.

Forgejo migration page

I made sure to delete every single private repository from GitHub after migration and rotate any keys that were linked to them.

At this point I had about 50 repositories migrated by hand.

The next step was to migrate my public projects except for the most popular and complex ones (lufin, sipacker).

GitHub mirrors or lack thereof #

Initially I was going to remove them from GitHub too but I've decided to keep them mirrored because I'd have to update hundreds of links.

I wanted to setup an "official" mirror like those used by Mozilla, Linux Kernel development team, Apache, to get a neat label like this:

Screenshot of how pull mirrors were indicated in early years of GitHub

To my surprise, not only GitHub have not officially implemented any automatic way of creating a pull mirror, and only had a manual way of creating it via support (read this thread on StackOverflow), which after 2014 was only available to big corporations...

Screenshot of aosp-mirror/platform_frameworks_base repository on GitHub with text "mirrored from <...>"

...today, even repositories that once had this "mirrored from" label have removed it.

apache/libcloud repository on GitHub on July 11th, 2016 with "mirrored from" label under the name

Take for instance apache/libcloud which clearly was a pull mirror back in 2016. Today it looks just like every other repository.

The same apache/libcloud repository but without "mirrored from" label

There are some public mirrors left on GitHub such as v8/v8 but it's clear GitHub wants them either gone or moved to their website.

v8/v8 repository with "mirrored from" label

Fine. I can just put "Read-only mirror of <repository>" to description. It's even better because it's also displayed in OGP tags and in profile. I settled on it and accepted the missing real description trade-off.

been-foss repository screenshot
It even highlights the link!

But is it really read-only? Sure, you can disable issues, discussions, wiki, you can even disable Actions tab, but you're still left with "Pull Requests", "Security" and "Insights".

Pull Requests, Security and Insights tabs
You can run from GitHub but you can't hide those three buttons.

There is no way to disable any of these. You can disable everything in Security but the tab won't go away. You can automatically close pull requests (how I do it) but it won't hide button to open them. Oh, GitHub, why are you so desperately trying to hold onto me but every time I stay you just shit on me more, just like my ex...

"Add feature to disable pull requests on a repository". 755&nbsp;👍 reactions

Fine. Let's just keep those useless 3 tabs and inappropriately placed "read-only mirror" label.

Screenshot of GitHub pull request with me asking to open it on git.hloth.dev instead
I spent way too much time figuring out how to make my bot reply on my username behalf. It took one oauth backend and several octokit libraries...

Since GitHub really does not want to pull mirror my repository I'll just set up push mirror. Luckily, Forgejo has amazing support for push mirrors. 

Forgejo "Mirror settings" options

You can even enable SSH authentication AND Forgejo generates a separate SSH-key just for this repository automatically for you!

Forgejo "Mirror settings" options with "Use SSH authentication" and "Sync when commits are pushed" checkboxes checked

Isn't that awesome?

Forgejo "Mirror settings" options with "Copy public key" button highlighted

Issues, pull requests, releases, projects, wikis #

Forgejo also can easily migrate all issues from GitHub with attachments, labels, assignments, projects, milestones, etc

Screenshot of issue in hloth/lufin repository with commens from several users
Note the "(Migrated from github.com)" caption 

And pull requests!

Screenshot of pull requests page in hloth/sipacker repository

And releases with tags!

Screenshot of releases page in hloth/lufin repository

And wikis!

Screenshot of wiki page in hloth/mafiaonline.js repository

And it all works just by checking checkboxes in migrator menu! Forgejo also migrates GitHub repository's description and topics by default.

Forgejo Actions #

This is my personal hell. By "this" I mean Docker of course. Oh, what a naive sweet summer child I was when thinking you can just run CI/CD on your own server securely. If only we lived in a world where nested containerization is possible...

A long vertical screenshot of "Utilizing Docker within Actions" documentation by Forgejo
pov it's 2 AM and your only thought is "what the fuck is wrong with docker"

If your idea of how Docker works is "a fast vm that you can automatically start and do whatever you want" YOU'RE WRONG. I'm going to save you about 6 hours of research and summarize why you can run everything in Docker except Docker itself.

  • Docker is container engine, not VM
  • Containers are not VMs they're more like a separate process on your computer
  • Docker itself uses your system containerization so in essence using docker = using your system's containers
  • Meaning if you want to use Docker inside of Docker containers you'd have to get access to that containerization system of your system
  • See a security vulnerability here?

As of today (2025/09/17) built-in Forgejo Actions Runner only supports Docker virtualization, LXC and no virtualization at all, which as you might've guessed a major security hole because it's literally allows running anyone their code on your machine with root access.

Remote code execution (RCE) is typically considered one of the most critical security vulnerabilities that can be discovered and exploited in a system. For Forgejo Actions, their entire purpose is to perform remote code execution. Therefore, there are many security considerations in deploying a self-hosted Forgejo Runner.

— "Securing Forgejo Actions Deployments" docs

TL;DR of the Utilizing Docker within Actions is you have the following options:

  1. Give jobs containers access to your system's Docker → jobs can manage each other or even other services on the same server → very insecure
  2. Run jobs inside of LXC (somewhat between Docker and real VM) → better isolation but of course there is a catch: it's not a real VM so if someone finds a kernel exploit they're getting rooted access to your whole system → still very insecure, not recommended
  3. Run a real VM. It's not supported by current version of Forgejo Actions Runner but it is what GitHub Actions, GitLab, Vercel, Cloudflare, Netlify use. Remember that managing VMs is a really hard task where you can miserably fail by omitting one line in iptables or configs and render all your isolation useless

asked how to run Docker in Forgejo Actions in the Forgejo subreddit and even got a reply from one of Forgejo developers, the one who wrote all these docs. Unfortunately, they didn't give me any new ideas on how to implement runner isolation properly and only confirmed the current constraints.

Comment by mfenniak about Forgejo Actions security
https://www.reddit.com/r/forgejo/comments/1niogmh/comment/nekh6yb/

So after about 10 more hours I finally migrated my testing workflows from GitHub Actions to Forgejo Actions.

hloth/lufin repository, Actions tab with 7 failed jobs and one final successfull

Federation #

Forgejo was forked from Gitea in early 2024 and became a standalone project in early 2024. One of key features that convinced me to use it instead of Gitea was promised federation. And Gitea selling to for-profit company.

Diagram showing Mastodon logos connected with arrows
How Mastodon's fediverse works. Source: https://fedi.tips/what-is-mastodon-what-is-the-fediverse/

Federation allows you to interact with a website without registering an account there. This is a next step after centralized websites that want you to keep one account with them and sign in with it everywhere (hello, Google). Only fediverse isn't controlled by anyone and you manage your account yourself.

Screenshot of issue "federated-star" in forgejo/forgejo repository
Basic support for federation shipped last year and more features are added with every update

But until federation is fully supported, I've decided to simplify registration by adding some OAuth providers. The well-known, good old "Sign in with <insert megacorp name>" buttons. I got a little carried away and accidentally added 50 of them.

A lot of OAuth buttons on registration page

Well, there were supposed to be about 50 but half are broken and some are straight up unfair requiring you to pay or register an official business.

The easiest providers to integrate were IT platforms (GitHub, GitLab, Jira, Gitea, Codeberg, StackOverflow, Hugging Face, Dribbble, Figma) and indie/small websites (osu!, MyAnimeList, GoodGame, DeviantArt).

The hardest providers to integrate were gaming platforms (Roblox, Riot Games, Battle.net, Bungie.net) and, of course, Zoom deserves a special place in hell for making me go through their application form for two hours, filling every detail about me, my app, my business, my goals, my education, my job, my dog's name, my life routines, and then rejecting because (insert 24 reasons why and resubmit with your app's source code).

So with 30 "sign in" buttons that's like 30x more reasons to sign up! Or make 30 accounts! Go ahead, create an account, star some of my projects :)


I love Forgejo and advice everyone to migrate away from GitHub to take control over your data and privacy. I'm following Forgejo development very closely and going to always upgrade as soon as stable versions are released. If you haven't checked it out yet, I recommend starting with comparison to Gitea. And if you liked this post, please give it a 👍 reaction below!

Published at:
0
0
0
0
0
0
0
0
0
0
0
0