Why Self-Host Your Git Forge on a VPS
A self-hosted Git forge keeps your intellectual property off public platforms and removes any arbitrary limit on the number of private repositories, collaborators, or CI minutes. On a VPS, you control data location, backups, and integration with your internal pipeline. Gitea (and its fork Forgejo) is written in Go: a single binary, instant startup, minimal memory footprint, ideal for a modest VPS. GitLab CE is an integrated suite (issues, CI/CD, Docker registry, package registry, wiki, environments) but consumes far more RAM. The choice depends on your need: a clean, fast Git, or a complete DevOps platform in a single product.
The Benefits of a Self-Hosted Forge
- Private repositories with no cap and no per-seat subscription
- CI/CD under your control, with your own runners on the VPS
- Secrets and tokens stored on your own server, off a third-party platform
- Built-in Docker image and package registry, close to your deployments
- Compliance and data residency fully under control
- With Gitea: a single binary, back up by copying a folder and a database
Gitea vs GitLab: License Differences in 2026
Gitea is released under the MIT license: you can read the code, modify it, redistribute it, and host it without commercial restrictions. Forgejo, its community fork born in 2022, applies the same license. GitLab CE is also MIT — but only the Community Edition layer. GitLab EE (Enterprise Edition) is proprietary: premium features (advanced SAML SSO, multiple MR approvals, integrated SAST/DAST, epics, roadmaps) are locked behind a paid subscription starting at several hundred euros per user per year. For a team self-hosting GitLab CE, the main risk is not the license itself but the upsell effect: missing CE features progressively push toward paid EE. With Gitea or Forgejo, all available features are included in the free version with no paid tier. If license compliance or audits matter in your context — public administration, government contracting, or enterprise accounts — this difference is structural.
Server Performance and Resources
Gitea uses roughly 100 to 150 MB of RAM at idle in a standard Docker deployment with PostgreSQL. With about ten active runners and a few simultaneous pipelines, you stay under 400 MB for the forge itself — easily fits on a 2 GB RAM VPS while leaving room for the OS and database. GitLab CE in omnibus is in a different league: PostgreSQL, Redis, Sidekiq, Gitaly, Puma, and NGINX all run in parallel. At idle, the stack commonly exceeds 3 to 4 GB of RAM on a fresh deployment and can climb to 6 to 8 GB under CI load. GitLab officially recommends 8 GB of RAM for production use with CI enabled. On the CPU side, Gitea is nearly invisible at idle; GitLab triggers background Sidekiq processes (indexing, webhooks, cleanups) that consume CPU intermittently even with no visible activity.
Gitea vs GitLab CE: Full Comparison Table
Scroll the table
| Criterion | Gitea / Forgejo | GitLab CE |
|---|---|---|
| Idle RAM | ~128 MB (forge only) | 3 to 4 GB (full omnibus) |
| Recommended production RAM | 1 to 2 GB | 8 GB |
| Architecture | Single Go binary | Multi-service suite (omnibus) |
| Startup time | Near-instant | Several minutes |
| License | MIT (Gitea) / MIT (Forgejo) | MIT (CE) / proprietary (EE) |
| Built-in CI/CD | Gitea Actions (GitHub-style) | GitLab CI/CD, very complete |
| Distributed runners | Basic (act runner) | Advanced (distributed runners, k8s executor) |
| Integrated SAST / DAST | No (via CI plugins) | EE only |
| Multiple MR approvals | Simple | Advanced (EE) |
| Docker / package registry | Included and lightweight | Included, rich |
| Project management (issues, boards) | Essential | Advanced (epics by edition) |
| Ease of backup | Folder + SQL dump | Dedicated omnibus procedure |
| Ideal for | Modest VPS, teams of 1 to 30 devs | Enterprise DevOps platform |
CI/CD Features Compared
Gitea Actions appeared in Gitea 1.19 (2023) and reuses exactly the GitHub Actions workflow syntax: existing .github/workflows/*.yml files are reusable without modification in most cases. The underlying executor is act (the open-source engine that simulates GitHub Actions locally), wrapped in a Docker runner. This is sufficient for the majority of pipelines: build, test, lint, image push, SSH deployment. Limitations appear on advanced cases: no shared caches across runners at scale, no native review environments, more limited artifact management. GitLab CI is more mature on these points: distributed runners across multiple machines, Kubernetes executors, shared caches, dynamic environments, review apps, inline test report pages, powerful rules: and include: directives. If your pipeline goes beyond a dozen parallel jobs, manages per-MR review environments, or requires integrated SAST/DAST in the merge report, GitLab CI is irreplaceable in its CE version (for CI/CD itself — SAST remains EE). The practical rule: Gitea Actions is sufficient for teams of 1 to 20 developers with standard pipelines; GitLab CI becomes necessary when pipeline maturity is itself a selection criterion.
GitLab to Gitea Migration: What CVE-2026-85706 Covers
CVE-2026-85706 is a Server-Side Request Forgery (SSRF) vulnerability discovered in GitLab CE and EE affecting versions prior to the March 2026 patch. It allows an authenticated attacker to force GitLab to make HTTP requests to internal network resources via the remote repository import feature. On a VPS where GitLab shares the internal network with other services (databases, registries, APIs), the exposure is real. Gitea is not affected by this specific CVE — its remote import code is separate. The GitLab to Gitea migration is documented and covered by gitea-migration-tool: it imports repositories, issues, labels, milestones, pull requests, and comments from the GitLab API. CI/CD pipelines do not migrate automatically (GitLab CI syntax differs from Gitea Actions), but for teams using GitHub-Actions-close syntax in their GitLab CI, the conversion is lightweight. If you are running an unpatched version of GitLab CE on an exposed VPS, migrating to Gitea or urgently upgrading are the only reasonable options.
When to Choose GitLab CE Despite the Resource Cost
- Teams of more than 50 active developers: GitLab's fine-grained permissions and nested groups justify the complexity
- Enterprise compliance or ISO 27001 certification: audit logs, quorum-based MR approvals, and push protection policies are more mature
- SAST / DAST integrated into the pipeline (requires GitLab EE, but some teams prefer consolidating on a single paid tool)
- Review apps and dynamic per-MR environments: GitLab CI handles this natively; Gitea Actions requires external tooling
- Kubernetes runners with autoscaling: GitLab has a dedicated K8s operator and a native executor
- Wiki linked to the repository with Git history and Mermaid / PlantUML renders built into the interface
Prerequisites by Chosen Forge
Gitea is frugal: 1 vCPU and 1 GB of RAM are enough for a small team, 2 GB with the database and a few runners. GitLab CE is far more demanding: aim for at least 4 GB of RAM (8 GB recommended in real-world use with CI), 4 vCPUs, and a fast disk, since the suite bundles PostgreSQL, Redis, Sidekiq, and Gitaly. In both cases you need Docker and Compose, a subdomain (git.yourdomain.com), a reverse proxy for TLS, and ideally a second subdomain if you enable the Docker registry. Plan for a dedicated volume for the repositories and a backup strategy.
Install Gitea on a ServOrbit VPS in 4 Steps
Prepare the Volume and Docker Network
On your ServOrbit VPS, create the data directories and an isolated Docker network:
mkdir -p /srv/gitea/{data,db} && docker network create forge. This network lets Gitea and PostgreSQL communicate without exposing the database to the public interface.Write the docker-compose.yml
Declare two services:
gitea/gitea:latestmounted on/srv/gitea/dataandpostgres:16-alpinemounted on/srv/gitea/db. Expose port3000internally (your reverse proxy will reach it) and Git SSH port2222externally. PassGITEA__database__DB_TYPE=postgres,GITEA__server__DOMAIN, andGITEA__server__ROOT_URLas environment variables.Configure the Domain and SSL
In your ServOrbit client area, create a DNS record for
git.yourdomain.compointing to your VPS IP. Proxy this subdomain with Caddy or Nginx to Gitea's port3000. Caddy handles Let's Encrypt automatically; with Nginx, add a certbot block. Verify thatX-Forwarded-Proto: httpsis forwarded so that clone URLs are correct.Create the First Repository and Wire Up a CI Runner
Run
docker compose up -d, then go tohttps://git.yourdomain.com. The installation wizard opens on first connection: set the database type (PostgreSQL), confirm the domain, and create the admin account. For CI, installact_runneron the same VPS or a second lightweight VPS, register it under Gitea's site administration (Administration > Runners), and place your first.gitea/workflows/ci.ymlfile in a repository.
Deploy Gitea (or GitLab) on a VPS with Docker
Create the Volumes and the Network
Prepare
mkdir -p /srv/gitea/{data,db}and a dedicated Docker networkdocker network create forge. Separating data/db makes it easier to back up the code and the database independently.Define the docker-compose.yml
For Gitea, declare the
gitea/giteaservice plus apostgres, mount the volumes, and expose HTTP port3000and Git SSH port2222locally. For GitLab, usegitlab/gitlab-cewith theGITLAB_OMNIBUS_CONFIGvariable to set theexternal_url.Launch and Finish the Installation
Run
docker compose up -d. Gitea shows an installation wizard on first connection (fill in the database type and the domain); GitLab takes several minutes to initialize — retrieve the generated root password from the logs.Configure the Reverse Proxy and SSL
Proxy git.yourdomain.com to the forge's HTTP port with Caddy or Nginx to obtain a Let's Encrypt certificate. Make sure to pass the correct
X-Forwarded-Protoso that the HTTPS clone URLs are correct.Enable Git SSH
Map the container's SSH port to a VPS port (e.g. 2222) and set up your keys. Set
SSH_DOMAINso that thegit clonecommands suggested by the interface are accurate.Wire Up CI/CD
Register a runner: Gitea Actions (compatible with GitHub Actions syntax) or a Docker GitLab Runner. Limit its resources and isolate it in its own container so that a heavy job does not bring the forge down.
Gitea vs GitLab CE: Comparison Table
Scroll the table
| Criterion | Gitea | GitLab CE |
|---|---|---|
| Recommended minimum RAM | 1 to 2 GB | 4 to 8 GB |
| Architecture | Single Go binary | Multi-service suite (omnibus) |
| Startup time | Near-instant | Several minutes |
| Built-in CI/CD | Gitea Actions (GitHub-style) | GitLab CI/CD, very complete |
| Docker / package registry | Included and lightweight | Included, rich |
| Project management (issues, boards) | Essential | Advanced (epics depending on edition) |
| Ease of backup | Folder + SQL dump | Dedicated omnibus procedure |
| Ideal for | Modest VPS, small/medium team | Complete DevOps platform |
Gitea + Woodpecker CI: the Lightweight Stack for Teams of 1 to 15
If Gitea Actions feels too close to the GitHub ecosystem and you want a strictly self-hosted CI with no dependency on act, look at Woodpecker CI (a fork of Drone CI, Apache 2.0 license). It integrates natively with Gitea via OAuth, runs on Docker, and its YAML syntax is more explicit than GitHub Actions. The Gitea + Woodpecker CI stack uses under 300 MB of RAM at idle on a 2 GB VPS — versus a minimum of 4 GB for GitLab CE with CI enabled. For teams of 1 to 15 developers with standard pipelines (build, test, Docker push, SSH deployment), this is the most frugal self-hosted combination available.
On GitLab, avoid the OOM-kill on a small VPS by reducing concurrency: set puma['worker_processes'] to 2 and sidekiq['max_concurrency'] to 10 in the omnibus config. If RAM remains a problem, look at Forgejo (a community fork of Gitea), which keeps the lightness while covering 90% of a team's needs, registry and Actions included.