Skip to content
Lazy Engineer's Tech Blog
Go back

Choosing a self-hosted Git server and CI/CD: GitLab, Gitea, OneDev and GitHub compared

한국어로 읽기 →

If company code can’t go to an external cloud like GitHub, what should you pick to install a Git repository and CI/CD (a setup that automatically builds, tests, and deploys every time code is pushed) in-house? With that question, I checked the candidates one by one against their official documentation. The short answer: there was no single “best” option; the answer split depending on what you need to get for free. All versions and prices below are as of October 2026.

The conditions

There were six conditions.

The candidates were GitHub, GitLab, Gitea, Forgejo (forked from Gitea), Gogs (the project Gitea itself forked from), and OneDev. I also looked at CI-only tools like Jenkins and TeamCity, and the cloud service Azure DevOps.

Dropped first: GitHub Enterprise Server, Gogs, Jenkins

The only way to install GitHub in-house is GitHub Enterprise Server. Per the official docs, installing it requires a paid Enterprise license file, and there’s no free edition. Even the smallest configuration (up to 10 light users) calls for 4 CPU cores, 32 GB of memory, and 400 GB and 500 GB disks, and turning on GitHub Actions requires at least 8 CPU cores and 64 GB of memory. Actions needs separate external storage for run results (Azure Blob, Amazon S3, Google Cloud Storage, or S3-compatible MinIO), and only self-hosted runners can be used. To use actions published on github.com, you have to connect out through GitHub Connect or sync them manually. Unless the company already standardizes on GitHub Enterprise, it’s the heaviest choice for starting fresh.

Keeping code in the cloud, as with GitHub.com or Azure DevOps, fails condition 1. For reference, for organizations GitHub.com needs a paid plan of GitHub Team or higher to put rules like required approvals (rulesets) on private repositories, and Azure DevOps is free for the first 5 users.

Gogs is the lightest. According to its README, 2 CPU cores and 512 MB of memory are enough for team work, and a Raspberry Pi will do. But its feature list has no CI/CD, package registry, or project boards. It doesn’t fit if CI is the goal.

Since Jenkins has an official MATLAB plugin, I first assumed it would obviously be needed. But both GitLab and Gitea do CI themselves, and what the official plugin did can be called directly from the command line. One more system adds servers, accounts, permissions, and upgrades along with it, so I dropped it unless the company already standardizes on Jenkins.

Specs of the remaining three: GitLab is heavy, Gitea and OneDev are light

When I actually checked, even an old desktop at home (Ryzen with 6 cores and 12 threads, 27 GiB of usable memory) exceeded GitLab’s recommended specs. The guess “GitLab is too much for an old PC” was wrong, and specs turned out less decisive than I thought.

All three do CI/CD; what differs is the format and official support (MATLAB CI)

All three candidates install the CI executor (runner) separately. The difference is the CI configuration format.

I checked official support for the domain tool in the MATLAB documentation. The officially supported CIs are Jenkins, GitLab CI/CD, GitHub Actions, Azure DevOps, CircleCI, Bamboo, and TeamCity, and for platforms not on the list it says to run MATLAB directly in command-line batch mode. Simulink Check’s CI/CD support package, which automates Simulink model verification, also generates pipeline configuration automatically for GitLab, GitHub, Jenkins, and Azure DevOps. Gitea and OneDev aren’t on these lists. But the same documentation has a separate section on running outside the supported CIs, with these requirements: an executor machine with MATLAB and the support package installed, a process model file at the project root, a connection to the remote git repository, and, for incremental builds, preserving task outputs and the digital thread database files between runs. The run command is a single line that opens the project and runs all defined tasks. So it does run on Gitea or OneDev. But you write the configuration files by hand, and when something goes wrong you can’t lean on “an officially supported platform.”

Seeing test results on the PR page: the strength of GitLab’s free tier (JUnit, coverage)

From the free tier on, GitLab shows JUnit test results and coverage percentage on the merge request (MR) page, and displays coverage line by line on the changed code (official docs, Tier: Free).

Gitea gained Job Summary in 1.27 (July 2026), a feature that shows a summary the workflow writes in Markdown on the run results page (PR #37500). If you turn test results into a table and write it to the summary, you can see it right on that page, but I didn’t find a feature that reads JUnit directly to list failures or shows line-by-line coverage the way GitLab does. You can fill this gap yourself: have CI post a result summary as a PR comment, attach a detailed report link to the commit status, or use diff-cover to pull out Markdown coverage for just the lines changed in this change. On the other hand, SonarQube Community Edition’s PR decoration plugin supports only GitHub, GitLab, Bitbucket, and Azure, so it couldn’t be used with Gitea.

OneDev publishes JUnit test results and Cobertura and JaCoCo coverage, and shows coverage and issues line by line on the PR change view. On this item alone it’s similar to GitLab’s free tier.

That said, if the verification tools have their own web results page (a dedicated server that collects static analysis or test results, for example), the PR page only needs a pass/fail summary and a link. In that case this item carries less weight.

Required review approval: not in free GitLab, but in free Gitea

This was the most surprising part of the safety process.

On required approval alone, free Gitea and OneDev beat free GitLab.

Account integration: look at login and group sync separately (LDAP, SAML, OIDC)

For account integration, the differences show only when you split it into two steps: “log in with a company account” and “automatically assign permissions based on company groups.”

Company account loginCompany groups → permission sync
GitLab freeLDAP, SAML, OIDC all freeLDAP, SAML, OIDC group sync all from Premium
GiteaLDAP, OIDC free; SAML paid (Enterprise)LDAP group check, OIDC groups → admin and team membership free
OneDevLDAP/AD, OIDC, Entra ID integration freeLDAP groups, OIDC group claims → groups free

GitLab’s free tier allows company account login even via SAML, but deciding which group and permission each logged-in person gets has to be managed by hand inside GitLab. That burden grows with the organization. Gitea is the opposite: group sync is free, but SAML login is paid. Most company identity systems today also support OIDC, so unless the company allows only SAML, Gitea’s weakness isn’t big.

The cost of scaling to company size (Enterprise pricing compared)

None of the candidates need a paid plan because of user count. GitLab’s self-managed free tier has no user limit (the 5-user limit applies only to private groups on the gitlab.com cloud). What makes paid plans necessary is management and control features.

Price per user per monthMain features in the paid plan
GitLab Premium$29 (billed annually)Multi-approver rules, protected environments, push rules, LDAP/SAML group sync, audit logs, Geo replication
Gitea Enterprise$9.50–$19 (1-year commitment)SAML 2.0, org-wide protected branch inheritance, dependency vulnerability scanning
OneDev Enterprise$6 (minimum 12 user-months per order)Audit logs, cluster redundancy, security scanning, statistics and trend analysis
GitHub Enterprise ServerFrom $21 (GitHub Enterprise plan, Cloud and Server alike)(the license itself is paid)

Audit logging came to free Gitea in 28.0 (September 2026), and instance-wide enforced two-factor authentication in 1.24.

The moment GitLab is used company-wide, required approvals and group sync mean you effectively have to plan for Premium.

Can it last? OneDev’s one-person development structure

On features alone, OneDev is the most balanced. Its free edition has required approvals, PR-page result display, a project tree (children inherit parent settings), and account group integration, and its paid edition is the cheapest. What gave me pause is how it’s developed. On its GitHub repository, the top commit contributor has 4,426 commits and the second 188, so it depends heavily on one core developer. With 15k GitHub stars, its user base is also smaller than GitLab’s or Gitea’s. That’s a risk for a company-standard tool meant to last.

Gitea is actively developed (after 1.27 it switched to version 28 numbering; v28.2.0 on October 18, 2026). Forgejo is run by its contributor community, with its domains held by the nonprofit Codeberg e.V. It started as a branch tracking Gitea, but from early 2024 its code diverged and it became a separate project. GitLab has the largest user base and documentation.

Side by side

CriterionGitLab freeGiteaOneDev
On-premisesYesYesYes
SpecsHeavyLightLight
CI formatOwn formatGitHub Actions formatOwn format
Official domain-tool CI support (e.g., MATLAB)YesNo (run via command)No (run via command)
Test results and coverage on PR pageFreeSummary only (Job Summary)Free
Required review approvalPremiumFreeFree
Company account loginFree (incl. SAML)Free (excl. SAML)Free
Group → permission syncPremiumFreeFree
Org structureSubgroupsNoneProject tree
Paid price at company scale$29$9.50–$19$6
Long-term stabilityHighestHighLow (one-person concentration)

So which one?

The decision narrowed down to two questions. Do you need required review approvals and group sync for free from the start? And does the company identity system allow OIDC, or only SAML?

Final thoughts

At first I had half-reached a conclusion on two guesses: “GitLab is heavy, so it won’t run on a home PC” and “Gitea’s results view is weak.” When I checked, both were wrong or only half right. The specs were ample, and Gitea had gained a summary view in a recent version. Instead, “free GitLab has no required approvals” and “group sync is paid,” which I hadn’t even looked at at first, were what actually decided it. A tool comparison table only became useful once I redrew it not as a feature list, but as “what’s free under my conditions.”


Share this post:

Previous Post
I tested my Obsidian plugin by remote-controlling Obsidian on another PC
Next Post
My AI agent fact-checked a blog draft against a memory summary and nearly rewrote a true story