Forgejo is the default: it idles around 250MB of RAM, ships GitHub- Actions-compatible CI, imports your GitHub repos with issues and PRs intact, and is governed by a nonprofit rather than a company. GitLab CE is a genuinely more powerful platform that wants 4GB+ of RAM before you've pushed a single commit — self-host it only if you specifically need its integrated CI/CD depth or work-tracking. Gitea is nearly identical software to Forgejo with company-driven governance; if you're choosing fresh, the fork's clarity wins.
The self-hosted git forge comparison
| Forgejo | Gitea | GitLab CE | |
|---|---|---|---|
| Idle RAM | ~250MB | ~250MB | 3–4GB realistic |
| Runs on a Pi | Comfortably | Comfortably | No |
| CI | Forgejo Actions (GH-compatible syntax) | Gitea Actions (same lineage) | GitLab CI — best in class |
| GitHub migration | Built-in importer + mirrors | Built-in importer + mirrors | Importer |
| Licence / steward | GPLv3+, Codeberg e.V. nonprofit | MIT, Gitea Ltd | MIT core, GitLab Inc |
| Upgrade discipline | Low ceremony | Low ceremony | Strict version-path upgrades |
The resource gap is the story
Forgejo and Gitea are single Go binaries; with SQLite they run a personal forge — repos, issues, wiki, packages, container registry — in a footprint smaller than most Electron apps. GitLab is Rails plus Sidekiq plus Gitaly plus Redis plus Postgres; the official minimum is 4GB RAM and it's honest, with 8GB the comfortable floor once CI runs. That's the difference between "runs unnoticed on the mini PC with everything else" and "owns its own VM". The maintenance asymmetry matches: GitLab ships mandatory monthly security releases and upgrades must walk specific version paths (skipping stops breaks things), where Forgejo upgrades are typically pull-and-restart. Underestimating this gap is the top regret in self-hosted-GitLab threads.
CI: the gap has closed
The historical reason to endure GitLab's weight was CI, and it's still the best: mature runners, caching, environments, a decade of documentation. But Forgejo Actions changed the calculus — it consumes GitHub Actions workflow syntax, which means the .github/workflows/ files you already have mostly run unmodified, and thousands of published actions work (compatibility is high but not total; complex actions occasionally need substitutes). The runner is one binary:
# compose for a Forgejo runner
services:
runner:
image: code.forgejo.org/forgejo/runner:6
volumes:
- ./data:/data
- /var/run/docker.sock:/var/run/docker.sock
command: forgejo-runner daemon
Register it against your instance with a token, tag it, and your existing workflows execute on your own hardware — with no per-minute billing and build caches that live on your LAN. For a solo developer or small team, this covers 95% of what GitLab CI would have been bought for. If you need review apps, multi-stage environments with approvals, or compliance pipelines, that's the remaining 5% and GitLab genuinely earns its RAM. More CI options, including the deliberately minimal Woodpecker, live in the CI/CD category.
One runner caveat from experience: mounting the Docker socket, as above, means CI jobs can control the host's Docker — acceptable for a personal forge running your own code, the wrong default the moment anyone else can open a pull request against a repo with workflows enabled. For that case, run jobs in the runner's Docker-in-Docker mode or on a throwaway VM, and treat CI runners with the same suspicion you'd give any code-execution service.
Gitea vs Forgejo: the fork, briefly
Forgejo forked from Gitea in late 2022 after Gitea's trademark and control moved to a for-profit company, and became a hard fork in 2024 — the codebases now evolve independently. Feature-wise they remain close; philosophically they've diverged: Forgejo is developed under Codeberg e.V. (the nonprofit that runs it in production for hundreds of thousands of repos on codeberg.org) with GPL licensing and federation work ongoing, while Gitea's roadmap tracks its company's priorities. Practical consequence: migration from Gitea to Forgejo is supported from Gitea 1.22 and earlier, but the window doesn't extend forever — if you're on Gitea and considering the move, do it before, not after, your next major upgrade. Existing happy Gitea installs don't need to panic; new installs have no reason not to start on Forgejo.
Migrating from GitHub without losing history
All three forges import more than git objects. Forgejo/Gitea's "New Migration" flow takes a GitHub token and pulls issues, pull requests, labels, milestones, releases, and wikis — a 50-repo account migrates in an afternoon. Two patterns worth distinguishing: migration (one-time copy, you switch remotes) versus mirror (the forge polls GitHub on an interval and stays current). Mirroring is the right first move — run your forge as a warm standby for six months while GitHub remains canonical, and lose nothing while you decide. Push mirrors work in reverse too: develop on your forge, auto-push to GitHub for visibility. That reverse pattern is how most public open-source work coexists with a sovereign forge, and it composes well with keeping your forge entirely off the public internet behind Tailscale or WireGuard.
What I'd do
Solo dev or homelab: Forgejo with SQLite in one container, a Forgejo runner beside it, GitHub mirrors for anything public — total footprint under 500MB of RAM including CI at idle. Small team: same, but Postgres instead of SQLite. Choose GitLab only when a concrete requirement — its CI environment model, compliance features, or your team's existing GitLab muscle memory — names itself, and give it a dedicated 8GB VM when you do. The quiet payoff of the lightweight path: your entire development infrastructure, CI included, now runs on hardware that costs less per year than one month of a team SaaS plan — with the smaller players waiting in the git hosting category if none of these three fits.