Gitea vs GitHub: Self-Hosted Git 2026
Gitea vs GitHub: Self-Hosted Git 2026
TL;DR
Gitea is a lightweight self-hosted Git forge written in Go that runs on 256MB RAM and deploys in under 15 minutes with Docker Compose. GitHub is a SaaS platform with the largest developer community in the world, unlimited public repos for free, and deeply integrated CI/CD through GitHub Actions. The decision is not about which tool is technically better — it's about what you're optimizing for. Teams that need code to stay on-premises (compliance, air-gapped networks, sovereignty), want to cut GitHub Team/Enterprise costs at scale, or simply want ownership of their development infrastructure should look seriously at Gitea. Teams building open source projects, needing the GitHub ecosystem (Actions marketplace, Packages, Copilot, Dependabot), or without capacity to manage infrastructure should stay on GitHub.
Key Takeaways
- Resource footprint: Gitea idles at 100–256MB RAM on a $6/month VPS — GitHub SaaS has no server to manage but costs $4/user/month (Team) or $21/user/month (Enterprise)
- Gitea Actions: Gitea 1.19+ ships GitHub Actions-compatible YAML workflows — many GitHub Actions workflows run on Gitea with minimal changes
- Migration: Gitea's built-in migration tool imports repos, issues, pull requests, milestones, labels, and releases directly from GitHub via API
- CI/CD gap: GitHub Actions has 20,000+ community actions; Gitea Actions runs the same YAML but has a smaller ecosystem —
act_runneris the execution engine - Feature parity: Gitea covers the 80% of GitHub features most teams use — missing GitHub Copilot, Codespaces, advanced security (GHAS), Projects v2 advanced features, and the community discovery layer
- Total cost at scale: 20 developers on GitHub Team costs $960/year; a Hetzner CX22 running Gitea costs ~$90/year
Why Teams Are Leaving GitHub in 2026
GitHub's acquisition by Microsoft in 2018 went smoothly for most teams. The service improved significantly: GitHub Actions replaced third-party CI, Packages added container and package registries, Copilot added AI code completion, and the free tier became genuinely competitive.
But several pressures are pushing teams toward self-hosted alternatives in 2026:
Cost at scale: GitHub Free is competitive for open source. GitHub Team at $4/seat/month adds nothing for small teams that don't need advanced code review or team permissions. GitHub Enterprise at $21/seat/month is significant — a 100-person engineering org pays $25,200/year. At that scale, a self-hosted Gitea instance with 4GB RAM on Hetzner runs at ~$300/year, a 98% cost reduction on infrastructure alone.
Data residency requirements: Financial services, healthcare, and government contractors often face regulatory requirements to keep source code within specific geographic boundaries or on-premises. GitHub offers GitHub Enterprise Server (a self-hosted version of GitHub), but it starts at the Enterprise license tier. Gitea achieves equivalent data residency goals at a fraction of the cost.
AI training concerns: GitHub's terms of service allow Microsoft/GitHub to use code for AI model training (opt-out possible but not always practical for organizations). Self-hosted Gitea eliminates this concern entirely.
Vendor lock-in: GitHub's ecosystem lock-in has deepened — Actions workflows, Packages, GitHub-specific CODEOWNERS, Actions secrets, and Environments all tie projects to the platform. Teams prioritizing portability prefer forges that support open standards.
Resource Footprint Comparison
Gitea on a $6/month VPS
Gitea is written in Go — it compiles to a single binary, starts in under a second, and has extremely low memory overhead.
| Team Size | RAM | CPU | Monthly VPS Cost |
|---|---|---|---|
| Solo / 1–5 devs | 256MB–512MB | 1 vCPU | $4–6 (Hetzner CAX11) |
| 5–20 devs | 512MB–1GB | 2 vCPU | $6–12 (Hetzner CX22) |
| 20–100 devs | 1–2GB | 2 vCPU | $12–20 (Hetzner CX32) |
| 100–500 devs | 2–4GB | 4 vCPU | $20–40 (Hetzner CX42) |
These numbers include Gitea + PostgreSQL. If you run Gitea Actions runners on the same machine, add ~500MB–1GB per concurrent runner.
Gitea's Go binary idle memory is under 100MB. The memory growth is driven primarily by Git operations (large repos, many concurrent clones) and the database.
GitHub SaaS Pricing
| Plan | Cost | Notes |
|---|---|---|
| Free | $0 | 500MB package storage, 2,000 Actions minutes/month |
| Team | $4/user/month | Unlimited private repos, 50GB storage, 3,000 Actions minutes |
| Enterprise | $21/user/month | Compliance, SAML SSO, audit log streaming, GHAS |
| Enterprise Server | Custom + $21/user/month | Self-hosted GitHub — at Gitea's target users, this is overkill |
For a team of 10 on GitHub Team: $480/year. The equivalent Gitea setup on a Hetzner CX22: $86/year. The ROI comparison becomes clear at 20+ seats.
Feature Comparison: Gitea vs GitHub
Core Git Hosting
| Feature | Gitea | GitHub |
|---|---|---|
| Private/public repos | ✅ | ✅ |
| Repo size limit | Configurable (no hard limit) | 5GB soft limit (practical) |
| LFS support | ✅ | ✅ (1GB free, then billed) |
| Protected branches | ✅ | ✅ |
| Branch rules | Basic | Advanced (CODEOWNERS, required status checks, bypass lists) |
| Pull requests | ✅ | ✅ |
| Code review / inline comments | ✅ | ✅ |
| Draft PRs | ✅ | ✅ (Team+) |
| PR templates | ✅ | ✅ |
| Merge strategies | Merge, rebase, squash | Merge, rebase, squash |
| Auto-delete branch on merge | ✅ | ✅ |
| Signed commits verification | ✅ | ✅ |
| Commit signature display | ✅ | ✅ |
| Dependency graph | ❌ | ✅ |
| Vulnerability alerts | ❌ | ✅ (Dependabot) |
Issue Tracking
| Feature | Gitea | GitHub |
|---|---|---|
| Issues | ✅ | ✅ |
| Labels | ✅ | ✅ |
| Milestones | ✅ | ✅ |
| Projects / Kanban | Basic (Kanban board) | ✅ (Projects v2 — advanced tables, roadmaps) |
| Issue templates | ✅ | ✅ |
| Issue forms (YAML) | ✅ | ✅ |
| Cross-repo references | Within same instance | ✅ (across all GitHub) |
| Issue search | Basic | ✅ (advanced filters) |
| Saved issue searches | ❌ | ✅ |
CI/CD
| Feature | Gitea Actions | GitHub Actions |
|---|---|---|
| YAML syntax | GitHub-compatible | Native |
act_runner | ✅ (self-hosted only) | N/A (managed + self-hosted) |
| Managed runners (cloud) | ❌ | ✅ (Linux, macOS, Windows) |
| Matrix builds | ✅ | ✅ |
| Reusable workflows | ✅ | ✅ |
| Marketplace actions | ~500+ compatible | 20,000+ (official marketplace) |
| Workflow caching | ✅ (actions/cache) | ✅ |
| Environments + secrets | ✅ | ✅ |
| OIDC tokens | ✅ | ✅ |
| Artifact storage | Local (configurable) | GitHub-managed (500MB–50GB) |
| Status checks on PRs | ✅ | ✅ |
Ecosystem and Integrations
| Feature | Gitea | GitHub |
|---|---|---|
| Package registry (npm, PyPI, etc.) | ✅ | ✅ |
| Container registry | ✅ | ✅ (GHCR) |
| Webhooks | ✅ | ✅ |
| REST API | ✅ | ✅ |
| GraphQL API | ❌ | ✅ |
| Third-party integrations | Limited | Vast (Slack, Jira, Linear, etc.) |
| AI code assistant | ❌ | ✅ (Copilot) |
| Codespaces / Cloud IDE | ❌ | ✅ |
| Advanced Security (GHAS) | ❌ | ✅ (Enterprise) |
| Code scanning / SAST | ❌ | ✅ (CodeQL) |
| Secret scanning | ❌ | ✅ |
| Audit log streaming | ✅ | ✅ (Enterprise) |
| SAML SSO | ✅ | ✅ (Enterprise) |
| LDAP | ✅ | ❌ |
| OAuth providers | ✅ (GitHub, Google, etc.) | Limited |
| ActivityPub/federation | ❌ | ❌ |
| Gitea-to-Gitea migration | ✅ | N/A |
| Mirror from GitHub | ✅ | N/A |
Gitea Actions: Running GitHub Workflows on Gitea
Gitea 1.19 (released March 2023) shipped Gitea Actions, which uses the same YAML workflow syntax as GitHub Actions. If your team is moving from GitHub to Gitea, most workflows will run unchanged or with minimal edits.
What's Compatible
# This GitHub Actions workflow runs on Gitea Actions with no changes
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest # Maps to a Gitea act_runner with ubuntu label
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- run: npm ci
- run: npm test
- uses: actions/upload-artifact@v4
with:
name: test-results
path: test-results/
What Requires Changes
1. runs-on labels: GitHub's managed runners use ubuntu-latest, macos-latest, windows-latest. Gitea uses whatever labels you assign to act_runner instances. You must provision your own runners and assign matching labels.
# If your Gitea runner is labeled 'ubuntu-22.04':
runs-on: ubuntu-22.04 # instead of ubuntu-latest
2. GitHub-specific actions: Some marketplace actions directly interact with GitHub APIs:
# These GitHub-specific actions don't work on Gitea:
- uses: actions/github-script@v7 # Requires GitHub API
- uses: github/codeql-action@v3 # GitHub-only
- uses: actions/stale@v9 # GitHub Issues API
# These work fine on Gitea:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/setup-python@v5
- uses: docker/build-push-action@v5
- uses: actions/upload-artifact@v4
- uses: actions/cache@v4
3. GITHUB_TOKEN vs GITEA_TOKEN: Workflows that use ${{ secrets.GITHUB_TOKEN }} for authenticated API calls need to reference ${{ secrets.GITEA_TOKEN }} or the built-in Gitea token:
# GitHub:
token: ${{ secrets.GITHUB_TOKEN }}
# Gitea:
token: ${{ secrets.GITEA_TOKEN }}
# or use the built-in:
token: ${{ gitea.token }}
Setting Up act_runner
act_runner is the Gitea-maintained daemon that executes workflow jobs. Each act_runner instance registers with your Gitea instance and picks up jobs via polling.
# docker-compose.yml — add act_runner to your Gitea stack
act_runner:
image: gitea/act_runner:latest
restart: always
environment:
GITEA_INSTANCE_URL: "https://git.yourdomain.com"
GITEA_RUNNER_REGISTRATION_TOKEN: "${RUNNER_REGISTRATION_TOKEN}"
GITEA_RUNNER_NAME: "primary-runner"
GITEA_RUNNER_LABELS: "ubuntu-latest:docker://node:20-bullseye,ubuntu-22.04:docker://ubuntu:22.04"
volumes:
- act_runner_data:/data
- /var/run/docker.sock:/var/run/docker.sock
Get the registration token from your Gitea instance: Site Administration → Actions → Runners → Create new runner.
For each label in GITEA_RUNNER_LABELS, the format is <label>:<executor>. Docker executors pull images on demand — the runner needs Docker access. For native execution (running jobs directly on the host), use <label>:host.
Docker Compose Setup: Gitea
Full Production Stack
# docker-compose.yml
services:
gitea:
image: gitea/gitea:latest
restart: always
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: ${DB_PASSWORD}
GITEA__server__DOMAIN: git.yourdomain.com
GITEA__server__ROOT_URL: https://git.yourdomain.com/
GITEA__server__HTTP_PORT: 3000
GITEA__server__SSH_PORT: 22
GITEA__server__SSH_LISTEN_PORT: 22
GITEA__mailer__ENABLED: "true"
GITEA__mailer__SMTP_ADDR: smtp.resend.com
GITEA__mailer__SMTP_PORT: "587"
GITEA__mailer__USER: resend
GITEA__mailer__PASSWD: ${SMTP_PASSWORD}
GITEA__mailer__FROM: git@yourdomain.com
GITEA__service__DISABLE_REGISTRATION: "false"
GITEA__service__REQUIRE_SIGNIN_VIEW: "false"
volumes:
- gitea_data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "127.0.0.1:3000:3000"
- "22:22"
depends_on:
- db
db:
image: postgres:16-alpine
restart: always
environment:
POSTGRES_DB: gitea
POSTGRES_USER: gitea
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
gitea_data:
postgres_data:
# .env
DB_PASSWORD=generate-a-strong-password-here
SMTP_PASSWORD=your-smtp-api-key
Nginx Reverse Proxy
server {
listen 80;
server_name git.yourdomain.com;
return 301 https://$host$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.key;
client_max_body_size 512m; # Allow large repo pushes
location / {
proxy_pass http://127.0.0.1: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 https;
}
}
# Deploy
docker compose up -d
# Get SSL certificate
certbot --nginx -d git.yourdomain.com
# Initial setup: visit https://git.yourdomain.com to complete web installer
# (or configure all settings via environment variables — no web installer runs
# when DB is already configured)
Post-Install Configuration
After the web installer completes (or on first run with environment variables):
- Create admin account: The first registered user becomes admin by default
- Configure SSH keys: Users add SSH keys under their profile settings
- Set up organization: Mirror GitHub's org structure under Administration → Organizations
- Enable Gitea Actions: Administration → Site Administration → Settings → Actions → Enable Gitea Actions
Migrating from GitHub to Gitea
Gitea includes a built-in migration tool that pulls from GitHub via API. It preserves:
- Repository content (all branches, tags, commits)
- Issues (title, body, comments, labels, milestones, state)
- Pull requests (with associated comments and review threads)
- Releases and release assets
- Wiki pages
- Labels and milestones
Step 1: Generate a GitHub Personal Access Token
Go to GitHub → Settings → Developer Settings → Personal Access Tokens → Fine-grained tokens.
Required permissions:
Contents: ReadIssues: ReadPull requests: ReadMetadata: Read
For organizations, also:
Members: Read
Step 2: Run the Migration in Gitea
Via web UI:
- In Gitea, click + (new repository) → Migrate an external repository
- Select GitHub as the source
- Enter:
- GitHub URL:
https://github.com - Personal Access Token: your PAT from Step 1
- Owner: your GitHub username or org
- Repository: the repository name
- GitHub URL:
- Under "Migration Items", check: Issues, Pull Requests, Labels, Milestones, Releases, Wiki
- Click Migrate Repository
Via API (for bulk migration):
#!/bin/bash
# migrate-from-github.sh
# Migrate all repos from a GitHub organization to Gitea
GITEA_URL="https://git.yourdomain.com"
GITEA_TOKEN="your-gitea-admin-token"
GITHUB_TOKEN="your-github-pat"
GITHUB_ORG="your-github-org"
GITEA_ORG="your-gitea-org"
# Get list of GitHub repos
repos=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \
"https://api.github.com/orgs/$GITHUB_ORG/repos?per_page=100&type=all" \
| jq -r '.[].name')
for repo in $repos; do
echo "Migrating: $repo"
curl -s -X POST "$GITEA_URL/api/v1/repos/migrate" \
-H "Authorization: Bearer $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"clone_addr\": \"https://github.com/$GITHUB_ORG/$repo\",
\"auth_token\": \"$GITHUB_TOKEN\",
\"repo_name\": \"$repo\",
\"repo_owner\": \"$GITEA_ORG\",
\"mirror\": false,
\"private\": true,
\"issues\": true,
\"pull_requests\": true,
\"releases\": true,
\"labels\": true,
\"milestones\": true,
\"wiki\": true
}"
echo ""
sleep 2 # Rate limit consideration
done
Step 3: Migrate Team Members
GitHub users are identified by their GitHub username during migration — Gitea creates placeholder accounts. You'll need to map GitHub usernames to actual Gitea accounts.
# List all migrated users (placeholders) via Gitea API
curl -H "Authorization: Bearer $GITEA_TOKEN" \
"$GITEA_URL/api/v1/admin/users?limit=50" | jq '.[].login'
For each team member:
- Have them register on your Gitea instance
- Use Admin → Users to find their account
- Reassign issues and PRs via Gitea admin UI or API
For organizations with LDAP/Active Directory, configure LDAP authentication before migration so users can log in with existing credentials:
Administration → Authentication Sources → Add Authentication Source → LDAP
Step 4: Update CI/CD References
GitHub Actions workflow files (.github/workflows/*.yml) need minor updates:
# Before (GitHub)
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to production
uses: appleboy/ssh-action@v1 # Works on Gitea too
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /app && git pull && npm install && pm2 restart app
Move workflow files from .github/workflows/ to .gitea/workflows/ (Gitea also supports .github/workflows/ for compatibility).
Step 5: Set Up Mirroring (Optional Transition Period)
During the transition, you can keep GitHub as a push mirror. New commits to Gitea automatically propagate to GitHub, keeping both in sync until the team is fully migrated.
In Gitea: Repository Settings → Mirrors → Add Push Mirror
- Remote URL:
https://github.com/your-org/repo.git - Interval: Every hour
- Username: your GitHub username
- Password/Token: your GitHub PAT with
Contents: Writepermission
GitHub Features Gitea Cannot Replace
GitHub Copilot
GitHub Copilot is integrated directly into VS Code, JetBrains, Neovim, and other editors and requires a GitHub account. Gitea has no AI assistant. Teams that move to Gitea can continue using Copilot (it works independently of the remote repository host) — the GitHub integration is for the IDE extension, not the Git server.
Alternative: JetBrains AI Assistant, Cursor, Codeium (free tier available), or self-hosted Ollama with Continue.dev.
GitHub Codespaces
Cloud development environments that launch a VS Code instance with your repo pre-loaded. No Gitea equivalent. Self-hosted alternatives:
- Gitpod Community — open source workspace manager, self-hostable
- code-server — VS Code in a Docker container, accessible via browser
- Devcontainer — VS Code Remote Containers (local, not cloud)
Advanced Security (GHAS)
GitHub Advanced Security includes CodeQL (SAST), secret scanning, dependency review, and code scanning. These are GitHub-only features available on Enterprise plans.
Self-hosted alternatives:
- Semgrep OSS — static analysis, runs in CI as a workflow step
- Trivy — container and dependency vulnerability scanning
- Gitleaks — secret detection in Git history, runs as a pre-commit hook or CI step
- OWASP Dependency-Check — dependency vulnerability scanner
# Add secret scanning to Gitea Actions workflow
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for Gitleaks
- name: Scan for secrets
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ gitea.token }}
- name: Dependency vulnerability scan
run: |
docker run --rm -v $(pwd):/src \
aquasec/trivy:latest fs /src \
--exit-code 1 --severity HIGH,CRITICAL
GitHub Packages + GHCR at Scale
Gitea includes a container registry and package registry (npm, PyPI, Maven, NuGet, RubyGems, Helm, etc.), but GitHub's GHCR has significant CDN infrastructure behind it. For public container images pulled millions of times, GitHub's global CDN is meaningfully faster than a self-hosted registry.
For internal/private images, Gitea's registry performs comparably.
Cost Analysis: Gitea vs GitHub at Scale
10-Person Team
| Option | Annual Cost | Notes |
|---|---|---|
| GitHub Free | $0 | 2,000 Actions minutes/month limit — hits ceiling quickly |
| GitHub Team | $480 | $4 × 10 × 12 |
| Gitea on Hetzner CAX11 | $57 | 2 vCPU, 4GB RAM, ARM64 — comfortable for 10 devs |
| Gitea + act_runner on CX22 | $86 | Separate CI runner instance |
50-Person Team
| Option | Annual Cost | Notes |
|---|---|---|
| GitHub Team | $2,400 | $4 × 50 × 12 |
| GitHub Enterprise | $12,600 | $21 × 50 × 12 |
| Gitea on Hetzner CX32 | $216 | 8 vCPU, 16GB RAM — handles 50+ devs |
| Gitea + 2× act_runners | $432 | Separate dedicated runner machines |
100-Person Team
| Option | Annual Cost | Notes |
|---|---|---|
| GitHub Team | $4,800 | |
| GitHub Enterprise | $25,200 | |
| Gitea cluster (CX42 + dedicated DB) | $800–1,200 | Includes redundancy |
| GitLab CE (if feature parity needed) | $1,200–2,000 | Higher resource requirements |
At 50+ seats, the annual savings from Gitea vs GitHub Team typically exceeds $2,000 — well beyond what annual maintenance costs in engineer time (typically 2–4 hours/month for a stable Gitea instance).
When to Stay on GitHub
The self-hosting savings don't apply to every situation. GitHub remains the right choice when:
You're building open source: GitHub is where developers discover projects. Stars, forks, contributor networks, and GitHub Sponsors all depend on being on GitHub. A self-hosted Gitea instance is invisible to the open source community. You can mirror to GitHub, but the primary development experience should stay where your audience is.
You rely on GitHub Actions ecosystem: The 20,000+ marketplace actions represent years of community investment. GitHub-specific actions for deploying to AWS, Azure, and Google Cloud, managing GitHub Issues, triggering Dependabot, and integrating with external services often have no direct Gitea equivalent. Evaluating which actions you use before migrating is essential.
You need Copilot: GitHub Copilot works on any repository you can access, but the editor integration and context features deepen when the repo is on GitHub. Enterprise Copilot features (code referencing, policy control, org-wide management) require GitHub Enterprise.
Compliance requires managed infrastructure: GitHub SOC 2, ISO 27001, FedRAMP Moderate, and HIPAA BAA attestations are mature. Self-hosted Gitea means you own the compliance posture — valid for teams building toward those certifications themselves, but a significant burden for teams without dedicated security staff.
You don't have infrastructure capacity: Gitea is low-maintenance by self-hosting standards, but it still requires a server, backups, updates, and someone responsible for uptime. If engineering time costs more than $480/year (10 seats), the economics can favor GitHub Team.
Backup and Disaster Recovery
Self-hosted Gitea means you own your backup strategy.
#!/bin/bash
# backup-gitea.sh
DATE=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR="/var/backups/gitea"
mkdir -p "$BACKUP_DIR"
# Gitea's built-in dump — archives all repos, config, attachments, and DB
docker exec gitea gitea dump -c /data/gitea/conf/app.ini \
-f "/tmp/gitea-dump-$DATE.zip" --type zip
# Copy dump out of container
docker cp "gitea:/tmp/gitea-dump-$DATE.zip" "$BACKUP_DIR/"
# PostgreSQL dump (belt-and-suspenders — also captured by Gitea dump)
docker exec gitea-db-1 pg_dump -U gitea gitea \
> "$BACKUP_DIR/postgres-$DATE.sql"
# Optional: push to S3-compatible storage
aws s3 cp "$BACKUP_DIR/gitea-dump-$DATE.zip" \
"s3://your-backup-bucket/gitea/"
# Cleanup local backups older than 7 days
find "$BACKUP_DIR" -name "*.zip" -mtime +7 -delete
find "$BACKUP_DIR" -name "*.sql" -mtime +7 -delete
echo "Backup complete: gitea-dump-$DATE.zip"
# Add to cron (daily at 3 AM)
0 3 * * * /opt/gitea/backup-gitea.sh >> /var/log/gitea-backup.log 2>&1
Restore from Backup
# Stop Gitea
docker compose down gitea
# Extract backup archive
unzip gitea-dump-YYYYMMDD-HHMMSS.zip -d /tmp/gitea-restore/
# Restore repos (git data)
cp -r /tmp/gitea-restore/repos/* /path/to/gitea_data/gitea/repositories/
# Restore PostgreSQL
docker compose up -d db
docker exec -i gitea-db-1 psql -U gitea gitea < /tmp/gitea-restore/gitea-db.sql
# Restart Gitea — it will regenerate hooks and indexes
docker compose up -d gitea
docker exec gitea gitea admin regenerate hooks
docker exec gitea gitea admin regenerate keys
Decision Framework
Choose Gitea self-hosted if:
- Your team is 10+ people paying GitHub Team/Enterprise and cost reduction is a priority
- Code must remain on-premises or within a specific jurisdiction
- You have an air-gapped or restricted network environment
- LDAP/Active Directory authentication integration is required (Gitea supports it; GitHub SaaS does not)
- You value independence from GitHub's roadmap and platform decisions
- Server administration is manageable — Gitea is the least complex self-hosted Git forge to operate
Choose GitHub if:
- You're building or contributing to open source and need community visibility
- Your CI/CD pipelines depend heavily on the Actions marketplace ecosystem
- GitHub Copilot is part of your development workflow
- You need GitHub Advanced Security (CodeQL, secret scanning, Dependabot)
- Managed compliance certifications (SOC 2, FedRAMP) are a hard requirement
- The team lacks bandwidth to manage infrastructure
Consider a hybrid approach if:
- You need Gitea for internal/private projects and GitHub for open source contributions
- You want Gitea as primary but mirror public repos to GitHub for discoverability
- You're in a transition period — run both with push mirroring from Gitea to GitHub during cutover
Related Guides
For teams building self-hosted infrastructure beyond Git:
- Self-Host Plausible vs Google Analytics 2026 — the same data ownership philosophy applied to analytics
- Self-Host Nextcloud on Docker 2026 — replace Google Drive and cloud file storage with a self-hosted alternative
Related: Gitea vs Forgejo vs GitLab 2026 · Self-Host Woodpecker CI 2026 · Complete Self-Hosting Stack 2026