Gitea auf einem VPS mit Docker Compose selbst hosten
Richten Sie eine produktionsreife Gitea-Instanz mit PostgreSQL, SSH-Passthrough auf Port 22, Gitea Actions CI/CD, Git LFS, GitHub-Mirroring und automatisierten Backups ein. Alles auf einem einzigen VPS mit Docker Compose.
Gitea ist ein leichtgewichtiger, selbst gehosteter Git-Dienst, geschrieben in Go. Er bietet Pull Requests, Issue-Tracking, CI/CD, Paketregistrierungen und Git LFS in einer einzigen Binary, die im Leerlauf etwa 150 MB RAM verbraucht. Vergleichen Sie das mit den 4 GB Minimum von GitLab und Sie verstehen, warum Gitea perfekt auf einen VPS passt.
Diese Anleitung richtet Gitea mit Docker Compose und PostgreSQL auf einem VPS ein. Sie konfigurieren SSH-Passthrough, damit git clone git@ihr-server:user/repo.git auf Port 22 funktioniert, richten Gitea Actions mit einem containerisierten Runner ein, aktivieren Git LFS, spiegeln Repositories von GitHub, konfigurieren Webhooks und automatisieren Backups.
Was brauchen Sie vor der Installation von Gitea?
Sie benötigen einen VPS mit Debian 12 oder Ubuntu 24.04, auf dem Docker und Docker Compose v2 installiert sind, einen Domainnamen, der auf Ihren Server zeigt, und einen Reverse Proxy (Nginx oder Caddy) mit konfiguriertem TLS. Diese Anleitung setzt voraus, dass Sie die grundlegende Serverhärtung durchgeführt haben: Nicht-Root-Benutzer mit sudo, SSH-Schlüsselauthentifizierung, Firewall aktiviert.
| Voraussetzung | Minimum |
|---|---|
| Betriebssystem | Debian 12 / Ubuntu 24.04 |
| RAM | 1 GB (2 GB empfohlen mit CI-Runner) |
| Docker | 27.x+ mit Compose v2 |
| Domain | A-Record, der auf die VPS-IP zeigt |
| Reverse Proxy | Nginx oder Caddy mit TLS |
Wie richten Sie Gitea mit Docker Compose und PostgreSQL ein?
Erstellen Sie ein Projektverzeichnis, generieren Sie Secrets in einer .env-Datei und definieren Sie die Gitea- und PostgreSQL-Dienste in docker-compose.yml. Die .env-Datei hält Zugangsdaten aus der Versionskontrolle und den Compose-Dateien heraus.
Projektverzeichnis erstellen
sudo mkdir -p /opt/gitea
sudo chown $USER:$USER /opt/gitea
cd /opt/gitea
Secrets generieren
openssl rand -base64 32 > /dev/null # test that openssl works
cat > .env << 'ENVFILE'
POSTGRES_USER=gitea
POSTGRES_PASSWORD=REPLACE_ME
POSTGRES_DB=gitea
GITEA_SECRET_KEY=REPLACE_ME
GITEA_INTERNAL_TOKEN=REPLACE_ME
GITEA_LFS_JWT_SECRET=REPLACE_ME
ENVFILE
Ersetzen Sie nun jedes REPLACE_ME durch ein echtes Secret:
sed -i "s/^POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=$(openssl rand -base64 32)/" .env
sed -i "s/^GITEA_SECRET_KEY=.*/GITEA_SECRET_KEY=$(openssl rand -base64 32)/" .env
sed -i "s/^GITEA_INTERNAL_TOKEN=.*/GITEA_INTERNAL_TOKEN=$(openssl rand -base64 32)/" .env
sed -i "s/^GITEA_LFS_JWT_SECRET=.*/GITEA_LFS_JWT_SECRET=$(openssl rand -base64 32)/" .env
chmod 600 .env
Docker-Compose-Datei
# /opt/gitea/docker-compose.yml
services:
gitea:
image: docker.gitea.com/gitea:1.25.5
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=db:5432
- GITEA__database__NAME=${POSTGRES_DB}
- GITEA__database__USER=${POSTGRES_USER}
- GITEA__database__PASSWD=${POSTGRES_PASSWORD}
- GITEA__server__DOMAIN=git.example.com
- GITEA__server__ROOT_URL=https://git.example.com/
- GITEA__server__SSH_DOMAIN=git.example.com
- GITEA__server__SSH_PORT=22
- GITEA__server__LFS_START_SERVER=true
- GITEA__server__LFS_JWT_SECRET=${GITEA_LFS_JWT_SECRET}
- GITEA__service__DISABLE_REGISTRATION=true
- GITEA__service__REQUIRE_SIGNIN_VIEW=false
- GITEA__security__SECRET_KEY=${GITEA_SECRET_KEY}
- GITEA__security__INTERNAL_TOKEN=${GITEA_INTERNAL_TOKEN}
- GITEA__actions__ENABLED=true
restart: always
volumes:
- gitea-data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "127.0.0.1:3000:3000"
depends_on:
db:
condition: service_healthy
networks:
- gitea
db:
image: docker.io/library/postgres:17
container_name: gitea-db
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
restart: always
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- gitea
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
networks:
gitea:
external: false
volumes:
gitea-data:
postgres-data:
Ersetzen Sie git.example.com durch Ihre tatsächliche Domain. Die wichtigsten Entscheidungen:
- PostgreSQL 17 statt des veralteten 14, den die meisten Anleitungen noch empfehlen
- Benannte Volumes (
gitea-data,postgres-data) statt Bind Mounts für sauberere Verwaltung - Health Check auf PostgreSQL, damit Gitea wartet, bis die Datenbank bereit ist
DISABLE_REGISTRATION=true, da offene Registrierung auf einer öffentlichen Instanz Missbrauch einlädtACTIONS__ENABLED=true, um Gitea Actions von Anfang an zu aktivieren- Port 3000 an localhost gebunden (
127.0.0.1:3000:3000), damit er nur über den Reverse Proxy erreichbar ist, nicht direkt aus dem Internet - Kein SSH-Port-Mapping am Container. SSH wird über Host-Passthrough gehandhabt (nächster Abschnitt)
Stack starten
docker compose up -d
[+] Running 3/3
✔ Network gitea_gitea Created
✔ Container gitea-db Healthy
✔ Container gitea Started
docker compose ps
NAME IMAGE STATUS PORTS
gitea docker.gitea.com/gitea:1.25.5 Up 2 minutes 127.0.0.1:3000->3000/tcp
gitea-db postgres:17 Up 2 minutes (healthy) 5432/tcp
Reverse-Proxy-Konfiguration
Fügen Sie Gitea zu Ihrer bestehenden Nginx-Konfiguration hinzu:
server {
listen 443 ssl http2;
server_name git.example.com;
ssl_certificate /etc/letsencrypt/live/git.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/git.example.com/privkey.pem;
server_tokens off;
client_max_body_size 512M;
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 $scheme;
}
}
client_max_body_size 512M erlaubt große Pushes, besonders nützlich bei aktivem Git LFS. server_tokens off verbirgt die Nginx-Version in den Antwort-Headern, da Versionsinformationen Angreifern helfen, bekannte Schwachstellen auszunutzen.
sudo nginx -t && sudo systemctl reload nginx
Admin-Benutzer erstellen
docker exec -it gitea gitea admin user create \
--username admin \
--password "$(openssl rand -base64 16)" \
--email admin@example.com \
--admin \
--must-change-password
New user 'admin' has been successfully created!
Speichern Sie das generierte Passwort an einem sicheren Ort (ein Passwortmanager, kein Notizzettel). Das Flag --must-change-password erzwingt eine Passwortänderung beim ersten Login. Aktivieren Sie nach dem Einloggen die Zwei-Faktor-Authentifizierung unter Settings > Security > Two-Factor Authentication.
Wie richten Sie SSH-Passthrough für Gitea in Docker ein?
Erstellen Sie einen git-Benutzer auf dem Host mit der gleichen UID wie im Container. Fügen Sie einen AuthorizedKeysCommand-Block in /etc/ssh/sshd_config hinzu, der docker exec gitea /usr/local/bin/gitea keys aufruft. Dies leitet SSH-Git-Operationen von Port 22 auf dem Host direkt in den Container weiter, ohne einen zweiten SSH-Port freizugeben.
Git-Benutzer auf dem Host erstellen
Der Benutzer benötigt UID 1000, um mit dem internen Benutzer des Containers übereinzustimmen:
sudo adduser --system --shell /bin/bash --group --disabled-password --home /home/git --uid 1000 git
Falls UID 1000 bereits von Ihrem regulären Benutzer belegt ist, ändern Sie entweder USER_UID/USER_GID in der Compose-Datei auf eine verfügbare UID oder passen Sie das --uid-Flag hier an.
Shell-Wrapper erstellen
Die Shell des Git-Benutzers muss Befehle in den Container weiterleiten:
sudo tee /usr/local/bin/gitea-shell > /dev/null << 'WRAPPER'
#!/bin/sh
/usr/bin/docker exec -i --env SSH_ORIGINAL_COMMAND="$SSH_ORIGINAL_COMMAND" gitea sh "$@"
WRAPPER
sudo chmod 755 /usr/local/bin/gitea-shell
sudo usermod -s /usr/local/bin/gitea-shell git
ls -la /usr/local/bin/gitea-shell
-rwxr-xr-x 1 root root 99 Mar 20 10:00 /usr/local/bin/gitea-shell
Git-Benutzer zur Docker-Gruppe hinzufügen
sudo usermod -aG docker git
Das gibt dem git-Benutzer die Berechtigung, docker exec auszuführen. Auf einem dedizierten Gitea-Server ist das akzeptabel. Auf einem geteilten Host sollten Sie stattdessen sudo-Regeln verwenden, die auf den spezifischen docker exec-Befehl beschränkt sind.
sshd konfigurieren
Fügen Sie diesen Block am Ende von /etc/ssh/sshd_config hinzu:
# Gitea SSH passthrough
Match User git
AuthorizedKeysCommandUser git
AuthorizedKeysCommand /usr/bin/docker exec -i gitea /usr/local/bin/gitea keys -c /etc/gitea/app.ini -e git -u %u -t %t -k %k
Der AuthorizedKeysCommand fragt den Gitea-Container, ob der vom SSH-Client präsentierte öffentliche Schlüssel zu einem registrierten Gitea-Benutzer gehört. Wenn er übereinstimmt, gewährt SSH den Zugang. Der Match User git-Block beschränkt dies auf den git-Benutzer und lässt Ihren regulären SSH-Zugang unberührt.
sudo sshd -t
Keine Ausgabe bedeutet, dass die Konfiguration gültig ist. Falls Sie Fehler sehen, prüfen Sie den Match-Block auf Tippfehler.
sudo systemctl restart sshd
SSH-Zugang testen
Fügen Sie Ihren öffentlichen SSH-Schlüssel über die Web-Oberfläche zu Ihrem Gitea-Konto hinzu (Settings > SSH/GPG Keys). Dann von Ihrem lokalen Rechner:
ssh -T git@git.example.com
Hi there, admin! You've successfully authenticated with the key named "my-laptop", but Gitea does not provide shell access.
Sie können jetzt über SSH auf Port 22 klonen, pushen und pullen:
git clone git@git.example.com:admin/my-repo.git
Wie aktivieren und konfigurieren Sie Gitea Actions für CI/CD?
Gitea Actions ist ein integriertes CI/CD-System mit einer Syntax, die mit GitHub Actions kompatibel ist. Sie aktivieren es mit einer Umgebungsvariable (bereits in unserer Compose-Datei gesetzt), stellen einen Runner als Docker-Compose-Dienst bereit und schreiben Workflow-YAML-Dateien in Ihren Repositories.
Wie registrieren Sie einen act_runner mit Docker Compose?
Generieren Sie zunächst ein Runner-Registrierungstoken über das Gitea-Admin-Panel:
docker exec -it gitea gitea actions generate-runner-token
NxxxxxxxxxxxxxxxxxxxxxxxN
Kopieren Sie dieses Token. Fügen Sie es Ihrer .env-Datei hinzu:
echo "GITEA_RUNNER_TOKEN=YOUR_TOKEN_HERE" >> /opt/gitea/.env
chmod 600 /opt/gitea/.env
Fügen Sie nun den Runner-Dienst zu Ihrer docker-compose.yml hinzu:
runner:
image: docker.io/gitea/act_runner:0.3.0
container_name: gitea-runner
environment:
- GITEA_INSTANCE_URL=http://gitea:3000
- GITEA_RUNNER_REGISTRATION_TOKEN=${GITEA_RUNNER_TOKEN}
- GITEA_RUNNER_NAME=vps-runner
volumes:
- runner-data:/data
- /var/run/docker.sock:/var/run/docker.sock
depends_on:
- gitea
restart: always
networks:
- gitea
Fügen Sie runner-data: zur volumes:-Sektion am Ende der Datei hinzu. Starten Sie dann den Runner:
docker compose up -d runner
docker compose logs runner --tail 20
level=info msg="Starting runner daemon"
level=info msg="Runner registered successfully"
Der Runner mountet den Docker-Socket (/var/run/docker.sock), damit er für jeden Job Container starten kann. Das ist Standardpraxis für CI-Runner, bedeutet aber, dass Jobs auf den Docker-Daemon des Hosts zugreifen können. Für isolierte Umgebungen sollten Sie den Runner mit Docker-in-Docker (DinD) betreiben.
| Umgebungsvariable | Zweck |
|---|---|
GITEA_INSTANCE_URL |
Interne URL, über die der Runner Gitea erreicht (verwenden Sie den Dienstnamen, nicht die öffentliche Domain) |
GITEA_RUNNER_REGISTRATION_TOKEN |
Einmaliges Token von gitea actions generate-runner-token |
GITEA_RUNNER_NAME |
Anzeigename im Gitea-Admin-Panel |
Wie sieht ein Gitea-Actions-Workflow aus?
Gitea Actions verwendet die gleiche YAML-Syntax wie GitHub Actions, mit einigen Unterschieden. Erstellen Sie .gitea/workflows/build.yml in Ihrem Repository:
name: Build and Test
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
- name: Build
run: go build -v ./...
- name: Test
run: go test -v ./...
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Deploy via SSH
env:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
run: |
mkdir -p ~/.ssh
echo "$DEPLOY_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh -o StrictHostKeyChecking=accept-new user@production "cd /app && git pull && systemctl restart myapp"
Speichern Sie das DEPLOY_KEY-Secret in Ihren Repository-Einstellungen unter Settings > Actions > Secrets.
| Funktion | Gitea Actions | GitHub Actions |
|---|---|---|
| Workflow-Verzeichnis | .gitea/workflows/ |
.github/workflows/ |
uses: actions/* |
Funktioniert (wird standardmäßig von GitHub abgerufen) | Nativ |
| Container-Services | Unterstützt | Unterstützt |
| Matrix-Builds | Unterstützt | Unterstützt |
| Wiederverwendbare Workflows | Unterstützt seit Gitea 1.24 | Unterstützt |
| Marketplace-Actions | Die meisten funktionieren, manche benötigen Anpassung | Nativ |
Wie aktivieren Sie Git LFS in Gitea?
Git Large File Storage ermöglicht das Tracking von Binärdateien (Bilder, Modelle, Datensätze), ohne Ihr Repository aufzublähen. Gitea hat eine integrierte LFS-Unterstützung. Wir haben sie bereits mit LFS_START_SERVER=true in der Compose-Datei aktiviert. Die LFS-Daten werden standardmäßig im gitea-data-Volume gespeichert.
Um den Speicherpfad explizit zu konfigurieren oder Limits zu erhöhen, bearbeiten Sie die Gitea-Konfiguration:
docker exec -i gitea sh -c 'cat >> /data/gitea/conf/app.ini' << 'EOF'
[lfs]
PATH = /data/git/lfs
EOF
Starten Sie Gitea neu, um die Änderungen zu übernehmen:
docker compose restart gitea
Auf der Clientseite installieren Sie git-lfs und tracken Dateitypen:
git lfs install
git lfs track "*.bin" "*.h5" "*.onnx"
git add .gitattributes
git commit -m "Track model files with LFS"
git push
LFS-Dateien werden auf dem Server unter dem konfigurierten PATH im Container gespeichert. Für große Deployments können Sie S3-kompatiblen Speicher in app.ini unter der [lfs]-Sektion konfigurieren.
Wie spiegeln Sie GitHub-Repositories zu Gitea?
Pull-Mirroring erstellt eine schreibgeschützte Kopie eines GitHub-Repositorys auf Ihrer Gitea-Instanz. Die Synchronisation erfolgt automatisch nach einem konfigurierbaren Zeitplan. Nützlich für Backups, als lokaler Cache hinter Ihrem CI-Runner oder um die Abhängigkeit von GitHub zu reduzieren.
In der Gitea-Web-Oberfläche:
- Klicken Sie auf + > New Migration
- Wählen Sie GitHub als Quelle
- Geben Sie die Repository-URL ein (z. B.
https://github.com/owner/repo.git) - Für private Repos geben Sie Ihren GitHub-Benutzernamen und ein Personal Access Token als Passwort ein
- Aktivieren Sie This repository will be a mirror
- Legen Sie das Spiegelungsintervall fest (Standard: 8 Stunden)
- Klicken Sie auf Migrate Repository
Der Mirror synchronisiert Branches, Tags und Releases. Issues, Pull Requests und Wikis können beim initialen Import migriert werden, werden aber nicht fortlaufend synchronisiert.
Zum Spiegeln über die API:
curl -X POST "https://git.example.com/api/v1/repos/migrate" \
-H "Authorization: token YOUR_GITEA_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"clone_addr": "https://github.com/owner/repo.git",
"mirror": true,
"mirror_interval": "8h",
"repo_name": "repo-mirror",
"repo_owner": "admin",
"service": "github"
}'
Wie konfigurieren Sie Webhooks für das Deployment?
Webhooks senden HTTP-POST-Anfragen an eine URL, wenn Ereignisse in einem Repository auftreten. Sie sind eine einfache Möglichkeit, Deployments, Benachrichtigungen oder externe CI-Systeme auszulösen.
In Ihrem Repository gehen Sie zu Settings > Webhooks > Add Webhook > Gitea:
- Target URL:
https://deploy.example.com/hooks/gitea - HTTP Method: POST
- Content Type: application/json
- Secret: generieren Sie mit
openssl rand -hex 32 - Trigger events: Push events (oder anpassen)
Auf der Empfängerseite muss Ihr Deployment-Skript die Webhook-Signatur überprüfen:
# The webhook sends a X-Gitea-Signature header
# Verify it with the shared secret before acting on the payload
EXPECTED=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" | awk '{print $2}')
if [ "$SIGNATURE" != "$EXPECTED" ]; then
echo "Invalid signature"
exit 1
fi
Ein minimaler Webhook-Empfänger mit einem leichtgewichtigen Tool wie webhook:
[
{
"id": "deploy",
"execute-command": "/opt/deploy.sh",
"command-working-directory": "/opt/app",
"trigger-rule": {
"match": {
"type": "payload-hmac-sha256",
"secret": "your-webhook-secret",
"parameter": {
"source": "header",
"name": "X-Gitea-Signature"
}
}
}
}
]
Wie sichern Sie eine Gitea-Instanz?
Gitea liefert einen gitea dump-Befehl, der Repositories, die Datenbank, Konfiguration und LFS-Objekte in eine einzelne Zip-Datei packt. Für PostgreSQL benötigen Sie zusätzlich einen separaten pg_dump für Point-in-Time-Recovery.
Manuelles Backup
docker exec -it gitea /usr/local/bin/gitea dump -c /data/gitea/conf/app.ini --file /data/gitea-backup.zip
docker cp gitea:/data/gitea-backup.zip /opt/gitea/backups/
Das Dump enthält:
| Inhalt | Im Dump enthalten |
|---|---|
| Git-Repositories | Ja |
| Datenbank (SQLite oder Dump) | Ja |
| Konfiguration (app.ini) | Ja |
| LFS-Objekte | Ja |
| Anhänge, Avatare | Ja |
| Pakete | Nein (separat sichern) |
Für PostgreSQL zusätzlich:
docker exec gitea-db pg_dump -U gitea gitea | gzip > /opt/gitea/backups/gitea-db-$(date +%Y%m%d).sql.gz
Automatisiertes Backup mit Cron
sudo mkdir -p /opt/gitea/backups
sudo chown root:root /opt/gitea/backups
sudo chmod 700 /opt/gitea/backups
Erstellen Sie das Backup-Skript:
sudo tee /opt/gitea/backup.sh > /dev/null << 'BACKUP'
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/opt/gitea/backups"
DATE=$(date +%Y%m%d-%H%M)
# Gitea dump
docker exec gitea /usr/local/bin/gitea dump -c /data/gitea/conf/app.ini --file /data/gitea-backup.zip --quiet
docker cp gitea:/data/gitea-backup.zip "$BACKUP_DIR/gitea-dump-$DATE.zip"
docker exec gitea rm /data/gitea-backup.zip
# PostgreSQL dump
docker exec gitea-db pg_dump -U gitea gitea | gzip > "$BACKUP_DIR/gitea-db-$DATE.sql.gz"
# Keep last 7 daily backups
find "$BACKUP_DIR" -name "gitea-*" -mtime +7 -delete
echo "Backup completed: $DATE"
BACKUP
sudo chmod 700 /opt/gitea/backup.sh
ls -la /opt/gitea/backup.sh
-rwx------ 1 root root 523 Mar 20 10:00 /opt/gitea/backup.sh
Planen Sie es täglich um 3 Uhr morgens:
echo "0 3 * * * root /opt/gitea/backup.sh >> /var/log/gitea-backup.log 2>&1" | sudo tee /etc/cron.d/gitea-backup
sudo chmod 644 /etc/cron.d/gitea-backup
Testen Sie das Skript einmal manuell:
sudo /opt/gitea/backup.sh
Backup completed: 20260320-1030
ls -lh /opt/gitea/backups/
-rw-r--r-- 1 root root 2.3M Mar 20 10:30 gitea-dump-20260320-1030.zip
-rw-r--r-- 1 root root 48K Mar 20 10:30 gitea-db-20260320-1030.sql.gz
Kopieren Sie Backups vom Server weg. Ein lokales Backup, das mit der Festplatte stirbt, ist kein Backup.
Wie aktualisieren Sie Gitea sicher?
Ziehen Sie das neue Image, erstellen Sie den Container neu und lassen Sie Gitea die Datenbankmigrationen automatisch durchführen. Pinnen Sie Image-Versionen, damit Updates beabsichtigt sind, nicht versehentlich.
cd /opt/gitea
# Back up first
sudo /opt/gitea/backup.sh
# Update the image tag in docker-compose.yml, then:
docker compose pull gitea
docker compose up -d gitea
docker compose logs gitea --tail 30
Achten Sie auf Migrationsmeldungen:
2026/03/20 10:35:00 ...les/migration.go:67:Migrate() [I] Migration completed
Nachdem Sie bestätigt haben, dass Gitea sauber startet, aktualisieren Sie den Runner, falls eine neue Version verfügbar ist:
docker compose pull runner
docker compose up -d runner
Was ist der Unterschied zwischen Gitea und Forgejo?
Forgejo hat sich Ende 2022 von Gitea abgespalten, nachdem ein gewinnorientiertes Unternehmen (Gitea Ltd.) die Kontrolle über das Gitea-Projekt übernommen hatte. Forgejo wird von Codeberg e.V. geleitet, einem deutschen gemeinnützigen Verein. Anfang 2026 ist Forgejo ein Hard Fork mit auseinanderlaufenden Codebases. Beide funktionieren mit einem ähnlichen Docker-Setup, aber die Migration zwischen den beiden ist nicht mehr nahtlos.
| Gitea | Forgejo | |
|---|---|---|
| Governance | Gitea Ltd. (gewinnorientiert) | Codeberg e.V. (gemeinnützig) |
| Lizenz | MIT | GPL-3.0+ (seit Forgejo v9.0) |
| Docker-Image | docker.gitea.com/gitea |
codeberg.org/forgejo/forgejo |
| Actions-Unterstützung | Ja (act_runner) | Ja (kompatibler Runner) |
| API-Kompatibilität | GitHub-kompatibel | GitHub-kompatibel |
| Einzigartige Funktionen | Gitea Enterprise, MCP-Server | Föderation (ForgeFed), Moderationstools |
Wenn Sie ein gemeinschaftlich verwaltetes Projekt mit einer Copyleft-Lizenz möchten, wählen Sie Forgejo. Wenn Sie das Originalprojekt mit kommerzieller Unterstützung möchten, wählen Sie Gitea. Das Docker-Compose-Setup aus dieser Anleitung funktioniert für beide mit minimalen Änderungen (Image tauschen, Pfade anpassen).
Wenn Sie mit Gitea starten und später wechseln möchten: Exportieren Sie Ihre Daten mit gitea dump, richten Sie Forgejo ein und importieren Sie. Testen Sie gründlich. Die Codebases haben sich so weit auseinander entwickelt, dass einige Datenbankschemata unterschiedlich sind.
Wie viel RAM und CPU braucht Gitea?
Eine Gitea-Instanz mit PostgreSQL verbraucht im Leerlauf etwa 150-250 MB RAM. Bei aktiver Nutzung mit 5-10 Benutzern und CI-Runnern rechnen Sie mit 300-500 MB insgesamt. Das ist etwa 10-mal leichter als eine GitLab-Instanz, die mindestens 4 GB benötigt.
Diese Zahlen stammen von einer laufenden Gitea-Instanz auf einem Virtua Cloud VPS (4 vCPU, 8 GB RAM):
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
NAME CPU % MEM USAGE / LIMIT
gitea 0.15% 148.2MiB / 7.77GiB
gitea-db 0.08% 45.3MiB / 7.77GiB
gitea-runner 0.02% 32.1MiB / 7.77GiB
| Szenario | RAM (gesamter Stack) | CPU |
|---|---|---|
| Leerlauf, wenige Repos | ~230 MB | < 1 % |
| Aktiv, 5-10 Benutzer | ~400 MB | 2-5 % |
| CI-Build läuft | ~600 MB (Spitzen während der Builds) | 20-50 % pro Job |
| GitLab-Äquivalent | 4.000+ MB | 10 %+ im Leerlauf |
Gitea läuft problemlos auf einem 2-GB-VPS. Mit CI-Runnern geben Ihnen 4 GB Spielraum für gleichzeitige Builds.
Fehlerbehebung
SSH-Verbindung abgelehnt:
Prüfen Sie, ob der git-Benutzer existiert, sshd_config den Match-Block enthält und sshd neu gestartet wurde. Prüfen Sie die Logs:
journalctl -u sshd -f
Runner nimmt keine Jobs an: Bestätigen Sie, dass der Runner im Admin-Panel registriert ist (Site Administration > Actions > Runners). Prüfen Sie die Runner-Logs:
docker compose logs runner --tail 50
Datenbankverbindungsfehler beim Start: Der Health Check sollte das verhindern, aber falls Gitea vor PostgreSQL startet:
docker compose restart gitea
LFS-Push schlägt mit 413 fehl:
Erhöhen Sie client_max_body_size in Ihrer Nginx-Konfiguration. 512M reicht normalerweise, aber passen Sie es an Ihre größten Dateien an.
Gitea-Logs:
docker compose logs gitea --tail 100
Oder live verfolgen:
docker compose logs gitea -f
Copyright 2026 Virtua.Cloud. Alle Rechte vorbehalten. Dieser Inhalt ist ein Originalwerk des Virtua.Cloud-Teams. Vervielfältigung, Wiederveröffentlichung oder Weiterverbreitung ohne schriftliche Genehmigung ist untersagt.
Bereit, es selbst auszuprobieren?
Stellen Sie Ihren eigenen Server in Sekunden bereit. Linux, Windows oder FreeBSD. →