How to Self-Host Gitea: GitHub Alternative on Your Own Server 2026
TL;DR
Gitea is a lightweight, open source GitHub alternative written in Go. It runs on a $6/month VPS, uses ~150MB RAM for small teams, and provides code hosting, pull requests, issues, wikis, and GitHub Actions-compatible CI/CD (via Gitea Actions). Setup takes about 20 minutes. If you need a full self-hosted GitHub replacement for a private team or open source project with sovereignty requirements, Gitea is the leading choice.
Key Takeaways
- Gitea: MIT license, ~46K GitHub stars, Go-based, runs on 512MB RAM
- Gitea Actions: GitHub Actions-compatible CI/CD — your existing workflow files work
- Resource usage: ~150MB RAM for a team of 10; ~40MB for a personal instance
- Setup time: ~20 minutes (Docker Compose + web installer)
- Gitea vs Forgejo: Forgejo is a community fork with more open governance — both are good options
- Gitea vs GitLab: Gitea is far lighter (~150MB vs GitLab's 4GB+ minimum)
Why Self-Host Your Git Server?
- Privacy: Code never leaves your infrastructure
- Cost: No per-user pricing; ~$6/month VPS for unlimited repos and users
- Control: Custom integrations, your own CI runners, private mirror networks
- Compliance: Air-gapped deployments for regulated industries
- Performance: Local network access — pushing large repos is faster
Gitea vs Alternatives
| Feature | Gitea | Forgejo | GitLab CE | OneDev |
|---|---|---|---|---|
| Language | Go | Go | Ruby/Go | Java |
| License | MIT | GPL 3.0 | MIT | MIT |
| GitHub Stars | ~46K | ~9K | ~23K | ~13K |
| RAM minimum | 150MB | 150MB | 4GB | 1GB |
| GitHub Actions compat. | ✅ | ✅ | ✅ (partial) | ✅ |
| Container registry | ✅ | ✅ | ✅ | ❌ |
| Kubernetes support | Via Helm | Via Helm | ✅ | Limited |
| Best for | Small teams, lightweight | Privacy-focused OSS | Enterprise | Integrated CI |
Gitea vs Forgejo: Gitea's development moved to a company structure (Gitea Limited) in 2022, prompting a community fork called Forgejo. Both remain fully open source and compatible. Use Forgejo if open governance matters to you; use Gitea for the original project.
Server Requirements
- Minimum: 1 vCPU, 512MB RAM, 10GB storage
- Recommended: 2 vCPU, 1GB RAM, 20GB storage (Hetzner CX22 at €4.35/month)
- Database: SQLite works for personal use; Postgres recommended for teams
Part 1: Docker Compose Setup
docker-compose.yml
# docker-compose.yml
version: '3.8'
services:
gitea:
image: gitea/gitea:latest
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=db:5432
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=${POSTGRES_PASSWORD}
restart: unless-stopped
volumes:
- gitea_data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000" # Web UI
- "2222:22" # SSH (avoids conflict with host SSH on 22)
depends_on:
- db
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
- POSTGRES_DB=gitea
- POSTGRES_USER=gitea
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
gitea_data:
postgres_data:
# .env file:
POSTGRES_PASSWORD=your-secure-password-here
docker compose up -d
Gitea is now running at http://your-server:3000.
Part 2: Web Installer
Visit http://your-server:3000 on first run. Gitea shows a setup wizard:
Key settings:
- Database: Select Postgres, confirm the values you set
- Site title: Your organization name
- Repository root path:
/data/gitea/repositories(default, inside container) - Git user:
git - SSH server domain:
your-server-ipor your domain - SSH server port:
2222(matching your docker-compose port mapping) - HTTP root URL:
https://git.yourdomain.com(your final domain) - Admin account: Create the first admin here
Click Install Gitea and you're set.
Part 3: DNS + Reverse Proxy
Put Gitea behind Nginx or Caddy for HTTPS.
With Caddy (simplest)
git.yourdomain.com {
reverse_proxy localhost:3000
}
With Nginx
server {
listen 443 ssl;
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;
# Increase for large repo pushes:
client_max_body_size 512m;
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;
}
}
Part 4: SSH Access
Gitea handles Git over SSH through port 2222. Users add their SSH key in Settings → SSH/GPG Keys, then clone:
# Clone using Gitea's SSH (port 2222):
git clone ssh://git@git.yourdomain.com:2222/username/repo.git
# Or configure SSH shorthand in ~/.ssh/config:
Host gitea
HostName git.yourdomain.com
Port 2222
User git
IdentityFile ~/.ssh/id_ed25519
# Then clone with:
git clone gitea:username/repo.git
SSH on Port 22 (Optional)
If you want standard SSH cloning (git@git.yourdomain.com:user/repo.git), forward port 22 to the container:
ports:
- "22:22" # Change host SSH to a different port first!
Or use Gitea's SSH passthrough mode — see the Gitea docs.
Part 5: Gitea Actions (CI/CD)
Gitea Actions is GitHub Actions-compatible. Your existing .github/workflows/*.yml files work with minor path adjustments.
Enable Gitea Actions
In Gitea admin panel → Site Administration → Configuration → Enable Actions.
Or via app.ini:
[actions]
ENABLED = true
Install act_runner
Gitea Actions requires an external runner (act_runner):
# Install act_runner on your VPS or a separate server:
curl -L https://gitea.com/gitea/act_runner/releases/download/latest/act_runner-linux-amd64 \
-o /usr/local/bin/act_runner
chmod +x /usr/local/bin/act_runner
# Register with your Gitea instance:
act_runner register \
--instance https://git.yourdomain.com \
--token YOUR_RUNNER_TOKEN \ # From Gitea Admin → Runners
--name my-runner \
--labels ubuntu-latest:docker://node:20
# Run as a service:
act_runner daemon
Or via Docker Compose, add a runner service:
gitea-runner:
image: gitea/act_runner:latest
environment:
GITEA_INSTANCE_URL: https://git.yourdomain.com
GITEA_RUNNER_REGISTRATION_TOKEN: ${GITEA_RUNNER_TOKEN}
GITEA_RUNNER_NAME: docker-runner
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- runner_data:/data
depends_on:
- gitea
Workflow Example
# .gitea/workflows/ci.yml (or .github/workflows/ci.yml — both work)
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install and test
run: |
npm ci
npm test
Part 6: Migrate from GitHub
Option A: Mirror a Repository
Gitea can mirror GitHub repos (stays in sync automatically):
- In Gitea: + New Repository → Migrate
- Select GitHub as source
- Enter repo URL and personal access token
- Enable Mirror checkbox
- Set sync interval (every hour, etc.)
Option B: One-Time Migration (Full History)
# Clone from GitHub with all history:
git clone --mirror https://github.com/username/repo.git
# Push to Gitea:
cd repo.git
git remote add gitea https://git.yourdomain.com/username/repo.git
git push gitea --mirror
Migrate Issues and Pull Requests
Gitea has a built-in GitHub migrator that transfers issues, pull requests, labels, and milestones:
- + New Repository → Migrate → GitHub
- Enter your GitHub personal access token
- Check Issues, Pull Requests, Labels, Milestones
- Click Migrate Repository
Part 7: Container Registry
Gitea includes a built-in container registry (Docker/OCI compatible):
# Login to Gitea's container registry:
docker login git.yourdomain.com -u your-username
# Tag and push an image:
docker tag myapp:latest git.yourdomain.com/username/myapp:latest
docker push git.yourdomain.com/username/myapp:latest
In a Gitea Actions workflow:
- name: Build and push Docker image
run: |
echo "${{ secrets.GITEA_TOKEN }}" | docker login git.yourdomain.com -u ${{ gitea.actor }} --password-stdin
docker build -t git.yourdomain.com/${{ gitea.repository }}:latest .
docker push git.yourdomain.com/${{ gitea.repository }}:latest
Maintenance
Updates
cd /path/to/gitea-compose
docker compose pull
docker compose up -d
Gitea has excellent backward compatibility — updates rarely require manual intervention.
Backups
# Backup Gitea data (inside running container):
docker exec -u git gitea gitea dump -c /data/gitea/conf/app.ini -f /tmp/gitea-backup.zip
# Copy backup out:
docker cp gitea:/tmp/gitea-backup.zip ./backups/gitea-$(date +%Y%m%d).zip
# Backup Postgres:
docker exec db pg_dump -U gitea gitea | gzip > ./backups/gitea-db-$(date +%Y%m%d).sql.gz
Monitoring
Gitea exposes Prometheus metrics at /metrics (enable in app.ini):
[metrics]
ENABLED = true
TOKEN = your-metrics-token
Resource Usage in Practice
| Instance Type | RAM Usage | Users | Repos |
|---|---|---|---|
| Personal | ~40MB | 1 | Any |
| Small team | ~150MB | 5–20 | Any |
| Medium org | ~400MB | 50–100 | Any |
| Large org | ~1GB | 500+ | Any |
Gitea is exceptionally lean. Compare this to GitLab CE's minimum of 4GB RAM.
See all open source GitHub alternatives at OSSAlt.com/alternatives/github.