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
- Unlimited private repositories with 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 in Morocco fully under control
- With Gitea: a single binary, back up by copying a folder and a database
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'll 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.
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 network docker 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/gitea service plus a postgres, mount the volumes, and expose HTTP port 3000 and Git SSH port 2222 locally. For GitLab, use gitlab/gitlab-ce with the GITLAB_OMNIBUS_CONFIG variable to set the external_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-Proto so 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_DOMAIN so that the git clone commands 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 doesn't bring the forge down.
| 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 |
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.