Head to head
Gitea vs GitLab CE
Most people comparing these two need a git remote and an issue tracker. This page is about establishing whether you are one of them before you commit 16 GB of RAM.
Should you self-host Gitea or GitLab CE?
Run Gitea unless you can name a GitLab feature you will use every week. GitLab's own requirements page documents 8 GB of memory as the minimum for constrained environments and 16 GB with 8 vCPU as the baseline, against roughly 150 MB idle and 450 MB in ordinary use for Gitea. That is a real hardware bill, paid monthly, for a platform whose governance features (enforced approvals, CODEOWNERS gating, merge trains, security dashboards) are not in Community Edition anyway.
The comparison people expect is a feature grid where GitLab wins on rows and Gitea wins on weight. That grid is misleading, because it invites you to price GitLab's extra capability without pricing its floor.
Here is the floor. GitLab's own requirements page documents 8 GB of memory as the minimum for memory-constrained environments, 16 GB as the baseline, and 8 vCPU alongside it. It also tells you to disable swap because swapping degrades performance badly under load. Gitea idles at roughly 150 MB and settles near 450 MB in ordinary single-team use. That is not a 3x difference. It is closer to 20x before you have done anything.
In the table below, read ram_idle_mb and ops_load together. Gitea is 150 MB and ops 2. GitLab CE is 4096 MB observed and ops 4, which in the rubric means version-pinned upgrade paths, stateful coupling between services, and restores worth rehearsing. That ops score is the number people ignore and then feel.
| Specification | Gitea | GitLab CE |
|---|---|---|
| Licence | MIT (Permissive) | MIT (Permissive) |
| Written in | Go / TypeScript | Ruby on Rails / Go / Vue |
| First release | 2016 | 2011 |
| Maturity | Mature | Mature |
| Datastore | SQLite (default), PostgreSQL or MySQL optional | PostgreSQL plus Redis (both bundled in Omnibus) |
| Services to run | 1 | 1 |
| Idle memory | 150 MB | 4096 MB |
| Memory in use | 450 MB | 8192 MB |
| Operational load | 2 / 5, Light | 4 / 5, Heavy |
| Identity | Native OIDC | Native OIDC |
| arm64 builds | Yes | Yes |
| Default ports | 3000, 22 | 80, 443, 22 |
| Backup shape | SQLite backup | Postgres dump |
What the RAM floor costs in money#
The documented 16 GB baseline is not a suggestion you can wave away by "it runs fine on my machine". It runs on less, in the sense that it boots. The behavior under load is the problem.
Concretely, on a first start GitLab runs database migrations and seeds, and the docs warn it may take a long time. On modest hardware expect 5 to 15 minutes before the instance answers anything, with the health check returning 502 throughout. Every configuration change afterwards is a gitlab-ctl reconfigure, which is a Chef run that restarts services. Omnibus is supervising PostgreSQL, Redis, Puma, Sidekiq, Gitaly, Workhorse, NGINX and Prometheus under runit, and that entire cast is resident whether you have four users or four hundred.
The rented-hardware version of that: a VPS with 16 GB and 8 vCPU costs roughly four to six times what a 2 GB instance costs at any mainstream provider (an estimate, since pricing moves). Over three years that is a genuinely large number to pay for a git remote. If the machine is yours, the cost is opportunity: 16 GB spent on GitLab is 16 GB not spent on Immich, Jellyfin and everything else you wanted the server for. Stack planner makes that tradeoff explicit, and Choosing home server hardware covers what a box with that headroom actually costs to buy.
There is one legitimate mitigation: put CI runners on a different machine. GitLab's own guidance is that runners compete with web workers, and moving them elsewhere is the single highest-value change on a small instance. It does not lower the floor, but it stops the floor from moving.
What Community Edition is missing, exactly#
This is where the argument for GitLab usually collapses for small teams, because the reason people want GitLab is process, and process is the paid part.
CE and EE are different builds, not a licence flag. The gitlab-ce package contains only FOSS code. The gitlab-ee package contains the proprietary code and runs at the Free tier until a licence is applied. CE to EE is a package swap; EE back to CE is a documented downgrade that involves removing EE-only data.
Fully present in CE:
- Repositories, merge requests, issues, milestones, boards, wikis, snippets
- CI/CD with runners, pipelines, artifacts and scheduled jobs
- Container registry and package registry
- OmniAuth with OIDC, SAML, LDAP and social providers, configured in
gitlab.rb
Not in CE, at any amount of configuration:
- Required merge request approvers and approval rules
- CODEOWNERS enforcement (the file is parsed, the gating is not)
- Merge trains
- Epics and roadmaps
- Pull mirroring (push mirroring is free, pull is not)
- Advanced code search
- Audit events
- Security dashboards and the SAST/DAST result surfaces
- LDAP group sync and multiple LDAP servers
Read that list against why you were considering GitLab. If the answer was "so reviews are enforced before merge", CE does not do that. Gitea does not either, so this is not a point for Gitea; it is a reason the whole comparison may be the wrong one, and you should be pricing GitLab Premium rather than CE.
The real point: a git remote is not a DevOps platform#
Ask what you actually need, in the order you will use it.
A git remote that is not GitHub. An issue tracker. Pull requests with review comments. Probably a package or container registry. Possibly CI, for a test suite and a container build.
Gitea does all of that. One Go binary or one container, SQLite by default, port 3000, roughly 450 MB in use. Gitea Actions runs GitHub Actions style workflows through an act_runner, and the package registry includes an OCI container registry alongside npm, Maven, PyPI, Cargo and the rest. The 2020-era argument that "GitLab is the only one with CI and a registry" stopped being true some years ago.
What you give up is genuine but narrower than the marketing suggests: GitLab's environments and deployments model, review apps per merge request, the enormous CI template ecosystem, and the operational maturity of pipelines at scale. If you deploy to Kubernetes from your forge and want per-environment tracking, that gap is real. If you build a container and push it to a registry, it is not.
The uncomfortable version, which vendor-friendly posts skip: most self-hosted GitLab instances are running a platform designed for hundreds of engineers to serve four people who use repositories, issues and one pipeline. The instance then becomes the most fragile thing on the server, because its upgrade path has required intermediate stops and its backup does not contain its own secrets.
The operational difference, in the two things that hurt#
Upgrades. GitLab publishes required stop versions for each major and minor path, and each stop must be allowed to finish its background database migrations before you continue, which can take hours on a large instance. You cannot jump from an old version to current in one step. Check the upgrade path tool for your exact source version before touching anything. Gitea applies schema migrations on startup and the guidance is simply not to jump several majors in one hop. An update strategy that does not lose data applies to both, but only one of them can consume a weekend.
Backups. gitlab-backup create writes a tar holding the database, repositories, LFS, uploads, CI artifacts, job logs, the package and container registries, wikis, Pages, Terraform state and snippets. It deliberately excludes /etc/gitlab/gitlab-secrets.json and gitlab.rb, because the database holds encrypted columns and keeping the key with the ciphertext defeats the purpose. Restore without that file and GitLab starts, but every CI/CD variable, integration token, two-factor seed and runner registration token decrypts to garbage.
The docs also warn that around 100 GB of repositories the backup takes over two hours, and near 400 GB the built-in command stops being viable at all.
Gitea's equivalent is gitea dump, which archives app.ini, the custom tree, the data directory, the repository directory and an SQL dump into one file. The docs are explicit that the instance should be stopped during the dump so the database and repository files do not diverge mid-copy. Exclude gitea/indexers and gitea/queues; they rebuild in minutes.
# Gitea, weekly, off-box
docker compose stop gitea
docker compose run --rm -u git gitea gitea dump -c /data/gitea/conf/app.ini
docker compose start giteaBoth restore into a different directory layout badly unless you regenerate repository hooks afterwards, because hooks bake in absolute paths and pushes then fail with an opaque error while the web UI looks healthy. Backups that actually restore and Backing up a running database cover the rehearsal that catches this.
When GitLab CE is genuinely the right answer#
It exists, so state it fairly:
- An engineering group of 10 to 200 that wants source control, CI, a container registry and packages from one vendor and one upgrade unit.
- A migration off GitHub Enterprise where the team expects feature parity rather than a lighter tool, and retraining cost dominates hardware cost.
- A regulated environment where build artifacts and container images have to live inside one audited boundary with one set of access controls.
- A server that already has 16 GB spare and an operator who would rather run one large thing than five small ones. That is a legitimate preference.
If you are in one of those, run GitLab CE, put runners on separate machines, set artifact expiry in .gitlab-ci.yml on day one, and schedule container registry garbage collection before the volume fills and PostgreSQL stops accepting writes.
Verdict by situation#
| Your situation | Pick | Why |
|---|---|---|
| One to ten developers, repos and issues | Gitea, or Forgejo | 450 MB against a documented 8 GB floor, for the same daily workflow |
| Homelab forge sharing a box with other services | Gitea | GitLab will crowd out everything else you wanted the server for |
| Under 8 GB of RAM total | Gitea | Below GitLab's documented minimum, and swap is explicitly discouraged |
| You need enforced approvals and CODEOWNERS gating | Neither, price GitLab Premium | Those are paid tiers; CE does not have them and nor does Gitea |
| Kubernetes deploys with per-environment tracking | GitLab CE | Environments, deployments and review apps have no Gitea equivalent |
| Regulated, everything in one audited boundary | GitLab CE | One upgrade unit covering repos, CI, registry and packages |
| You want CI but not the platform | Gitea plus Woodpecker CI | Separate concerns, roughly 240 MB combined |
| You need a serious registry with scanning and retention | Gitea plus Harbor | Trivy scanning, RBAC, replication and retention policies |
| Choosing a forge for the first time in 2026 | Forgejo | See Forgejo vs Gitea: same software, nonprofit governance |
What to do next#
Write down the GitLab features you would use in a typical week. If the list is repositories, issues, merge requests and one pipeline, install Gitea (or Forgejo, for the reasons in Forgejo vs Gitea) on a 2 GB VM this afternoon and spend the saved hardware on something else.
If the list includes environments, review apps or a genuine need for one audited platform, provision 16 GB, set external_url before first boot because changing it later means fixing every stored URL, and put your runners somewhere else. Then rehearse the restore with /etc/gitlab included, because that is the failure that turns a bad week into a bad quarter.
Either way, if the actual goal is leaving GitHub, Replace GitHub covers the parts that are not the git remote: Actions, Pages, releases and the issue import. Add Uptime Kuma pointed at your forge's health endpoint, because a git remote nobody notices is down is a git remote nobody can push to.
Questions#
How much RAM does GitLab actually need?
GitLab's documentation states 8 GB of memory as the minimum for memory-constrained environments and 16 GB as the baseline, alongside 8 vCPU. Separately it advises disabling swap where possible because swapping causes significant degradation under load. Those two statements together mean a 4 GB instance has no safety valve: Puma and Sidekiq workers get OOM-killed under CI load and jobs stall silently.
Can you run GitLab on 4 GB of RAM?
It will boot. It will also spend the first 5 to 15 minutes running migrations while returning 502, and then swap under any real load. You can reduce Puma workers and Sidekiq concurrency in gitlab.rb and get something usable for two or three people who never run CI on the box. You will spend your evenings tuning worker counts instead of shipping code. Do not plan around it.
What is the difference between GitLab CE and EE?
They are different builds, not a licence toggle. The gitlab-ce package contains only FOSS code under MIT. The gitlab-ee package contains the proprietary code and runs at the Free tier until you apply a licence. Moving CE to EE is a package swap that works. Going back from EE to CE is a documented downgrade that involves removing EE-only data. Decide before you have five years of history in it.
What does GitLab CE not include?
The governance layer, which is exactly what teams adopting GitLab for process usually want: required merge request approvers, CODEOWNERS enforcement, merge trains, epics and roadmaps, pull mirroring, advanced code search, audit events and the security dashboards. All sit in Premium or Ultimate and cannot be unlocked in CE. Source control, CI, the container registry and issues are all fully present.
Does Gitea have CI and a container registry?
Yes to both, which narrows the gap more than most comparisons admit. Gitea Actions runs GitHub Actions style workflows through an act_runner you register against the instance, and Gitea's package registry includes an OCI container registry alongside npm, Maven, PyPI and the rest. What Gitea does not have is GitLab's environments and deployments model, review apps, or the depth of its CI ecosystem.
Can you migrate from GitLab to Gitea?
Repositories and issues, yes. Gitea's migration tool imports repositories with issues, pull requests, labels, milestones and releases from a GitLab instance over the API. What does not come across is CI configuration: .gitlab-ci.yml has no equivalent in Gitea Actions and every pipeline has to be rewritten as a workflow. Budget for that separately, because it is usually the larger job.
Is GitLab CE free forever?
The CE build is MIT-licensed source you can run without payment indefinitely. The caution is directional rather than legal: features have historically landed in EE tiers rather than CE, so the gap widens over time without anything changing about your existing install. You keep what you have; you do not necessarily get what comes next.
Sources#
- GitLab docs, installation requirements
- GitLab docs, install with Docker
- GitLab docs, install with the Linux package
- GitLab docs, back up GitLab
- GitLab docs, upgrade overview and required stops
- Gitea releases and current version
- Gitea docs, install from binary and supported platforms
- Gitea docs, backup and restore
Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.