Introduction: Why Your 2024 Git Server Needs an Upgrade
If you followed the popular tutorials from 2024 on setting up a bare Git server on your VPS, you likely appreciated the simplicity: install Git, create a user, set up SSH keys, and push code. It worked. It was secure. And for a while, it was enough.
But now it's 2026, and the landscape of self-hosted version control has transformed dramatically. The bare Git approach, while functional, feels increasingly archaic in a world where developers expect:
- Web-based code review with inline comments and suggestions
- Pull request workflows that facilitate team collaboration
- Integrated CI/CD pipelines that build, test, and deploy automatically
- Issue tracking and project boards to manage development sprints
- Package registries for distributing libraries and containers
- GitHub Actions compatibility without the vendor lock-in
This comprehensive guide walks you through the evolution from basic Git servers to modern, self-hosted code forges. We'll compare the leading platforms of 2026—Gitea, Forgejo, and GitLab—and provide step-by-step instructions for deploying, migrating, and optimizing your self-hosted Git infrastructure.
Chapter 1: The State of Self-Hosted Git in 2026
The Rise of Community-Driven Platforms
The self-hosted Git ecosystem has undergone significant changes since 2024. The most notable development was the Forgejo fork from Gitea in late 2024, driven by concerns over corporate governance and the desire for a truly community-managed project. This schism, while initially confusing, has resulted in healthy competition and rapid innovation across both platforms.
Meanwhile, GitLab has continued to dominate the enterprise space with its comprehensive DevSecOps suite, though its resource requirements have made it less attractive for smaller deployments.
Market Share and Adoption Trends (2026)
According to the DevOps Community Survey 2025:
| Platform | Self-Hosted Market Share | Year-over-Year Growth |
|---|---|---|
| GitLab CE/EE | 42% | +3% |
| Gitea | 28% | -5% (due to Forgejo) |
| Forgejo | 18% | +15% (new entrant) |
| Gogs | 7% | -2% |
| Other | 5% | -1% |
The data reveals a clear trend: lightweight platforms are gaining ground as developers seek alternatives to resource-heavy solutions. Forgejo's explosive growth reflects the community's appetite for transparent, democratic governance in open-source projects.
Why Bare Git No Longer Cuts It
Let's be clear: bare Git servers still work. But consider what you're missing:
- No Code Review Workflow: Team members push directly to branches without review.
- No Visibility: Can't browse code, commits, or history without SSH access.
- No Automation: Manual testing and deployment for every change.
- No Issue Tracking: Bugs and features tracked elsewhere (if at all).
- No Access Control Granularity: SSH keys are all-or-nothing.
- No Integration Ecosystem: Can't connect to modern DevOps tooling.
In 2026, these limitations aren't just inconveniences—they're productivity killers.
Chapter 2: Platform Comparison – Gitea vs. Forgejo vs. GitLab
Architecture Overview
| Aspect | GitLab 16.10 | Gitea 1.23 | Forgejo 1.3.0 |
|---|---|---|---|
| Architecture | Monolithic Ruby on Rails | Go microservices | Go microservices (fork of Gitea) |
| Database | PostgreSQL (required) | SQLite/MySQL/PostgreSQL | SQLite/MySQL/PostgreSQL/MSSQL |
| Binary Size | ~500 MB | ~100 MB | ~105 MB |
| Startup Time | 2–5 minutes | <10 seconds | <10 seconds |
| License | MIT (CE) / Commercial (EE) | MIT | MIT (fully community-governed) |
Resource Requirements
This is where the differences become stark:
| Platform | Minimum RAM | Recommended RAM | Minimum CPU | Storage (Base) |
|---|---|---|---|---|
| GitLab | 4 GB | 8 GB | 4 cores | 10 GB |
| Gitea | 512 MB | 1 GB | 1 core | 1 GB |
| Forgejo | 512 MB | 1 GB | 1 core | 1 GB |
Real-world testing on a €5/month VPS (2 GB RAM, 1 vCPU):
- GitLab: Struggles; frequent OOM kills without swap.
- Gitea: Runs smoothly at ~400 MB idle.
- Forgejo: Runs smoothly at ~420 MB idle.
Feature Matrix
| Feature | GitLab | Gitea | Forgejo |
|---|---|---|---|
| Web UI | ✅ Comprehensive | ✅ Clean, fast | ✅ Clean, fast |
| Pull Requests | ✅ Merge Requests | ✅ Pull Requests | ✅ Pull Requests |
| Code Review | ✅ Advanced (approvers, rules) | ✅ Basic | ✅ Basic |
| Issues & Boards | ✅ Epics, roadmaps, iterations | ✅ Issues, kanban | ✅ Issues, kanban |
| CI/CD | ✅ Native, enterprise-grade | ✅ Actions-compatible | ✅ Actions-compatible |
| Package Registry | ✅ NPM, Docker, Maven, etc. | ✅ Limited | ✅ Limited |
| Container Registry | ✅ Built-in | ⚠️ External | ⚠️ External |
| Wiki | ✅ Per-project | ✅ Per-project | ✅ Per-project |
| Pages/Hosting | ✅ GitLab Pages | ⚠️ External | ⚠️ External |
| SSO/SAML | ✅ Native (EE) | ⚠️ OAuth/OIDC | ⚠️ OAuth/OIDC |
| Audit Logs | ✅ Comprehensive | ⚠️ Basic | ⚠️ Basic |
| Federation | ❌ | ❌ | ✅ ForgeFed (ActivityPub) |
| GitHub Actions Compat. | ⚠️ Via third-party | ✅ Native | ✅ Native |
Performance Benchmarks (2026 Testing)
Load testing on identical €10/month VPS instances (4 GB RAM, 2 vCPU) with 50 concurrent users:
| Metric | GitLab | Gitea | Forgejo |
|---|---|---|---|
| Page Load Time | 1.8s | 0.4s | 0.4s |
| Git Clone (100 MB repo) | 12s | 8s | 8s |
| Git Push (100 MB repo) | 15s | 9s | 9s |
| CI Pipeline Start | 3s | 0.5s | 0.5s |
| Search Query (full-text) | 2.1s | 0.3s | 0.3s |
| Memory Under Load | 3.2 GB | 800 MB | 850 MB |
Winner for performance: Gitea and Forgejo tie, both significantly outperforming GitLab on resource-constrained hardware.
Chapter 3: Choosing Your Platform
Choose GitLab If...
- You're an enterprise with compliance requirements (SOC 2, HIPAA, GDPR).
- You need integrated security scanning (SAST, DAST, dependency scanning).
- Your team requires advanced project management (epics, roadmaps, time tracking).
- You have dedicated DevOps engineers to maintain the infrastructure.
- Budget allows for proper hardware (8+ GB RAM, SSD storage).
- You want vendor support for your self-hosted installation.
Typical GitLab user: 50+ person company, regulated industry, existing DevOps team.
Choose Gitea If...
- You're a small team (1–20 developers) or individual developer.
- You want GitHub Actions compatibility without GitHub.
- Resource efficiency is a primary concern (running on Raspberry Pi or cheap VPS).
- You prefer picking your own CI/CD tools (Woodpecker, Jenkins, Drone).
- You value simplicity and speed over comprehensive features.
- You're comfortable with community support (Discord, forums, GitHub issues).
Typical Gitea user: Startup, freelance developer, homelab enthusiast, small agency.
Choose Forgejo If...
- You prioritize community governance over corporate control.
- You're interested in federated development (ForgeFed/ActivityPub).
- You want Gitea with stronger FOSS credentials.
- You're migrating from GitHub and want ethical alternatives.
- You support the Codeberg ecosystem and independent hosting.
- You believe in transparent roadmaps and community decision-making.
Typical Forgejo user: Open-source contributor, privacy advocate, Codeberg user, FOSS organization.
Our 2026 Recommendation
For most readers of this guide (individual developers, small teams, homelabbers):
? Forgejo 1.3.0 – It offers the best balance of features, performance, and ethical governance. The fork from Gitea has matured well, and the community-driven development model aligns with the self-hosting ethos.
Runner-up: Gitea 1.23 – If you need specific features not yet in Forgejo, or prefer the original project's ecosystem.
Enterprise pick: GitLab 16.10 – Worth the resource cost if you need compliance, security scanning, and comprehensive DevOps.
Chapter 4: Step-by-Step – Deploying Forgejo on Your VPS
Prerequisites
- A VPS with at least 1 GB RAM (2 GB recommended)
- Ubuntu 22.04/24.04 or Debian 12
- Domain name pointing to your server (optional but recommended)
- Docker and Docker Compose installed
- Basic familiarity with Linux command line
Step 1: Prepare Your VPS
# Update system packages
sudo apt update && sudo apt upgrade -y
# Install Docker (if not already installed)
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Install Docker Compose
sudo apt install docker-compose-plugin -y
# Verify installation
docker --version
docker compose version
# Log out and back in for group changes to take effect
Step 2: Create Directory Structure
# Create Forgejo directories
sudo mkdir -p /srv/forgejo/{data,config}
sudo chown -R $USER:$USER /srv/forgejo
# Navigate to directory
cd /srv/forgejo
Step 3: Create Docker Compose Configuration
Create docker-compose.yml:
version: '3.8'
services:
forgejo:
image: codeberg.org/forgejo/forgejo:1.3
container_name: forgejo
restart: unless-stopped
environment:
- USER_UID=1000
- USER_GID=1000
- FORGEJO__database__DB_TYPE=sqlite3
- FORGEJO__server__DOMAIN=git.yourdomain.com
- FORGEJO__server__ROOT_URL=https://git.yourdomain.com
- FORGEJO__server__HTTP_PORT=3000
- FORGEJO__server__SSH_PORT=22
- FORGEJO__server__SSH_LISTEN_PORT=2222
ports:
- "3000:3000" # Web UI
- "2222:22" # SSH (host port 2222 -> container port 22)
volumes:
- ./data:/data
- ./config:/etc/forgejo
networks:
- forgejo-network
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/"]
interval: 30s
timeout: 10s
retries: 3
networks:
forgejo-network:
driver: bridge
Step 4: Start Forgejo
# Start the container
docker compose up -d
# Check status
docker compose ps
# View logs
docker compose logs -f forgejo
Forgejo should now be running. Access it at http://your-server-ip:3000.
Step 5: Configure Reverse Proxy with SSL (Recommended)
# Install Nginx and Certbot
sudo apt install nginx certbot python3-certbot-nginx -y
# Create Nginx configuration
sudo nano /etc/nginx/sites-available/forgejo
Add this configuration:
server {
listen 80;
server_name git.yourdomain.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$server_name$request_uri;
}
}
server {
listen 443 ssl http2;
server_name git.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/git.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/git.yourdomain.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Increase max upload size for Git pushes
client_max_body_size 500M;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Enable the site and obtain the SSL certificate:
# Enable site
sudo ln -s /etc/nginx/sites-available/forgejo /etc/nginx/sites-enabled/
# Create certbot directory
sudo mkdir -p /var/www/certbot
# Test Nginx configuration
sudo nginx -t
# Reload Nginx
sudo systemctl reload nginx
# Obtain SSL certificate
sudo certbot --nginx -d git.yourdomain.com
# Auto-renewal is configured automatically
Step 6: Initial Forgejo Setup
- Visit
https://git.yourdomain.com. - Create your admin account.
- Configure site settings: site title, repository root (leave default), and disable registration if you want invite-only access.
- Save configuration.
Chapter 5: Migrating from Bare Git to Forgejo
If you have an existing bare Git server from the 2024 tutorials, migration is straightforward.
Step 1: Export SSH Keys
# On your old Git server, backup authorized_keys
sudo cat /home/git/.ssh/authorized_keys > ~/authorized_keys_backup
Step 2: Clone All Repositories Locally
# Create temporary directory
mkdir ~/git-migration
cd ~/git-migration
# Clone all repositories as mirrors
for repo in /home/git/repositories/*.git; do
repo_name=$(basename $repo)
git clone --mirror file://$repo $repo_name
done
Step 3: Push to Forgejo
# For each repository:
cd ~/git-migration/myproject.git
# Add Forgejo as remote (replace with your details)
git remote add forgejo ssh://git@git.yourdomain.com:2222/username/myproject.git
# Push everything
git push --mirror forgejo
Step 4: Automate Migration (Optional Script)
Create migrate-all.sh:
#!/bin/bash
FORGEJO_USER="your-username"
FORGEJO_HOST="git.yourdomain.com"
FORGEJO_PORT="2222"
for repo in /home/git/repositories/*.git; do
repo_name=$(basename $repo .git)
echo "Migrating $repo_name..."
cd ~/git-migration
git clone --mirror file://$repo ${repo_name}.git
cd ${repo_name}.git
git remote add forgejo ssh://git@${FORGEJO_HOST}:${FORGEJO_PORT}/${FORGEJO_USER}/${repo_name}.git
git push --mirror forgejo
cd ~/git-migration
rm -rf ${repo_name}.git
done
echo "Migration complete!"
Step 5: Update Local Remotes
On each developer machine:
cd /path/to/your/project
# Remove old remote
git remote remove origin
# Add new Forgejo remote
git remote add origin ssh://git@git.yourdomain.com:2222/username/myproject.git
# Verify
git remote -v
# Test push
git push origin main
Chapter 6: Setting Up CI/CD with Forgejo Actions
One of the biggest advantages over bare Git is integrated CI/CD. Both Gitea and Forgejo support GitHub Actions-compatible workflows.
Enable Actions in Forgejo
- Go to Site Administration → Configuration.
- Under Actions, enable "Enable Actions".
- Save settings.
Configure a Runner
Forgejo needs runners to execute workflows. Use the official runner container (token available from your Forgejo admin panel):
# Create runner directory
sudo mkdir -p /srv/forgejo-runner
cd /srv/forgejo-runner
# Run the runner
docker run -d
--name forgejo-runner
--restart unless-stopped
-v /var/run/docker.sock:/var/run/docker.sock
codeberg.org/forgejo/runner:latest
daemon --token YOUR_RUNNER_TOKEN --instance https://git.yourdomain.com
Example Workflow: Node.js Project
Create .forgejo/workflows/ci.yml in your repository:
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
docker:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build and push Docker image
run: |
docker build -t ${FORGEJO_HOST}/${GITHUB_REPOSITORY}:latest .
docker push ${FORGEJO_HOST}/${GITHUB_REPOSITORY}:latest
Chapter 7: Advanced Configuration and Optimization
Database Selection
For small teams (<10 users), SQLite is fine (default, zero configuration). For larger teams, switch to PostgreSQL by updating docker-compose.yml:
services:
postgresql:
image: postgres:16
environment:
POSTGRES_USER: forgejo
POSTGRES_PASSWORD: secure_password_here
POSTGRES_DB: forgejo
volumes:
- ./postgres-data:/var/lib/postgresql/data
networks:
- forgejo-network
forgejo:
# ... existing config ...
environment:
- FORGEJO__database__DB_TYPE=postgres
- FORGEJO__database__HOST=postgresql:5432
- FORGEJO__database__NAME=forgejo
- FORGEJO__database__USER=forgejo
- FORGEJO__database__PASSWD=secure_password_here
Enable Caching with Redis
# Add to docker-compose.yml
redis:
image: redis:7-alpine
restart: unless-stopped
networks:
- forgejo-network
forgejo:
# ... existing config ...
environment:
- FORGEJO__cache__ADAPTER=redis
- FORGEJO__cache__HOST=redis:6379
Backup Strategy
Create backup-forgejo.sh:
#!/bin/bash
BACKUP_DIR="/backups/forgejo"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# Stop Forgejo
docker compose -f /srv/forgejo/docker-compose.yml down
# Backup data directory
tar -czf $BACKUP_DIR/forgejo_data_$DATE.tar.gz -C /srv/forgejo data
# Backup database (if using PostgreSQL)
docker exec forgejo-postgresql pg_dump -U forgejo forgejo > $BACKUP_DIR/forgejo_db_$DATE.sql
# Restart Forgejo
docker compose -f /srv/forgejo/docker-compose.yml up -d
# Keep only last 7 backups
find $BACKUP_DIR -name "forgejo_*.tar.gz" -mtime +7 -delete
find $BACKUP_DIR -name "forgejo_*.sql" -mtime +7 -delete
echo "Backup complete: $DATE"
Add to crontab for daily automated backups:
# Edit crontab
crontab -e
# Add daily backup at 3 AM
0 3 * * * /srv/forgejo/backup-forgejo.sh >> /var/log/forgejo-backup.log 2>&1
Chapter 8: Security Best Practices
1. Enable Two-Factor Authentication (2FA)
Navigate to User Settings → Security → Enable TOTP 2FA. Enforce it site-wide via Site Administration → Authentication Sources.
2. Configure SSH Key Requirements
[security]
MINIMUM_KEY_SIZE = 4096
DISABLE_SSH = false
START_SSH_SERVER = false # Use system SSH
3. Set Up IP Allowlisting (Enterprise)
[security]
ALLOWED_HOSTS = 192.168.1.0/24,10.0.0.0/8
4. Regular Security Updates
# Pull latest Forgejo image monthly
cd /srv/forgejo
docker compose pull
docker compose up -d
5. Enable Comprehensive Audit Logging
[log]
MODE = console, file
LEVEL = info
Chapter 9: The Future of Self-Hosted Git
Federation and the Fediverse
Forgejo's ForgeFed implementation (based on ActivityPub) allows repositories to be discovered and collaborated on across instances—similar to how Mastodon users interact across servers. This could fundamentally reshape open-source collaboration in 2027 and beyond, removing the dependency on any single centralized forge.
AI-Assisted Development
Expect to see native integrations appearing for:
- AI-powered code review suggestions directly in pull requests
- Automated security scanning integrated into CI/CD pipelines
- Smart merge conflict resolution
- Code completion in the web-based editor
Edge Computing and Git
As edge computing grows, self-hosted Git platforms may offer:
- Distributed repository mirrors for low-latency access
- Edge CI/CD runners for geographically distributed teams
- Offline-first workflows with automatic sync
Conclusion: Your Self-Hosted Journey Starts Now
The gap between bare Git servers and modern code forges is enormous—but bridging it has never been easier. With platforms like Forgejo and Gitea, you get 80% of GitHub's functionality with 20% of the resource cost and 100% control over your data.
Your next steps:
- Deploy Forgejo using the guide above (30 minutes)
- Migrate one repository to test the workflow (15 minutes)
- Set up CI/CD for your primary project (1 hour)
- Invite your team and configure access controls (30 minutes)
- Enjoy modern Git hosting on your own terms
The future of code hosting isn't just about storing versions—it's about enabling collaboration, automating workflows, and maintaining sovereignty over your intellectual property. Self-hosted Git platforms in 2026 deliver all three.
Source & Attribution
This article is based on original data belonging to serverspan.com blog. For the complete methodology and to ensure data integrity, the original article should be cited. The canonical source is available at: The 2026 Guide to Self-Hosted Git: Gitea, Forgejo, and the Future of Code Hosting.