Zum Inhalt springen
Serverküche
Suche

Die Suche wird geladen … (nur in der veröffentlichten Seite verfügbar).

Anwendungen Schwierigkeit: Fortgeschritten

Forgejo: der eigene Git-Server hinter Traefik

Forgejo mit Docker & Traefik aufsetzen: eigener Git-Server mit HTTPS, Repos per Web-UI, Klonen über HTTPS und SSH – die selbstgehostete GitHub-Alternative.

· 10 Min. Lesezeit ·Dauer: ca. 40 Minuten
Inhaltsverzeichnis

GitHub ist bequem – aber dein Code liegt dann auf fremden Servern. Mit Forgejo hostest du deine Repositories selbst: eine schlanke, komplett quelloffene Git-Plattform, die auf dem kleinsten VPS läuft und trotzdem Issues, Pull-Requests, Wiki und CI mitbringt.

Was bauen wir?

Am Ende läuft Forgejo 16.0.3 als einzelner Container hinter deinem Traefik, erreichbar unter https://DEINE_DOMAIN mit automatischem Let’s-Encrypt-Zertifikat. Du legst Repositories über die Web-Oberfläche an und klonst/pushst sie wahlweise über HTTPS oder über SSH. Als Datenbank nutzen wir SQLite – für einen persönlichen oder kleinen Team-Git-Server reicht das locker und spart einen zusätzlichen Datenbank-Container. Forgejo ist der Community-Fork von Gitea und in der Selfhosting-Szene klar im Aufwind.

Warum überhaupt selbst hosten? Dein Code, deine Issues und dein Projektverlauf liegen dann ausschließlich auf deinem Server – keine Abhängigkeit von den Konditionen, Rate-Limits oder Übernahmen eines externen Anbieters, und volle Datenhoheit. Forgejo ist dabei kein abgespecktes Spielzeug: Es kann fast alles, was du von GitHub kennst (siehe Schritt 7), bleibt aber schlank genug für einen kleinen VPS.

Voraussetzungen

  • Ein Server mit Debian 13 und laufendem Docker (getestet auf einem netcup VPS).
  • Ein Reverse Proxy mit Traefik (das proxy-Netz und der Resolver le daraus werden vorausgesetzt) – Forgejo bringt kein eigenes HTTPS mit, Traefik übernimmt die Verschlüsselung.
  • Eine (Sub-)Domain, die per A-/AAAA-Record auf deinen Server zeigt (DEINE_DOMAIN).
  • Für echte Backups: verschlüsselte Backups mit Restic.

Forgejo ist genügsam und läuft schon auf dem kleinsten vServer. Wie viel Server dein geplantes Setup insgesamt braucht, schätzt dir der Server-Rechner.

🍳 Empfehlung Anzeige

VPS 1000 G12

4 vCore · 8 GB RAM · 256 GB NVMe

ab 10,36 €/Monat

Forgejo mit SQLite läuft entspannt auf dem VPS 1000.

Zu netcup →

💶 5 € Gutschein für netcup-Neukunden:36nc17844976032 (nur Neukunden, keine Domains)

Schritt für Schritt

Schritt 1: Compose-Datei anlegen

Lege einen Ordner für den Stack an und wechsle hinein:

Terminal
mkdir -p /opt/forgejo && cd /opt/forgejo

Erstelle die Datei compose.yaml. Ersetze DEINE_DOMAIN durch deine echte Domain:

YAML
services:
  forgejo:
    image: codeberg.org/forgejo/forgejo:16.0.3
    container_name: forgejo
    restart: unless-stopped
    environment:
      USER_UID: 1000
      USER_GID: 1000
      FORGEJO__server__DOMAIN: DEINE_DOMAIN
      FORGEJO__server__ROOT_URL: https://DEINE_DOMAIN/
      FORGEJO__server__SSH_DOMAIN: DEINE_DOMAIN
      FORGEJO__server__START_SSH_SERVER: "true"
      FORGEJO__server__SSH_PORT: "2222"
      FORGEJO__server__SSH_LISTEN_PORT: "2222"
      FORGEJO__service__DISABLE_REGISTRATION: "true"
      FORGEJO__database__DB_TYPE: sqlite3
    volumes:
      - forgejo_data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "2222:2222"
    healthcheck:
      test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://localhost:3000/api/healthz"]
      start_period: 30s
      start_interval: 2s
      interval: 30s
      timeout: 5s
      retries: 3
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.forgejo.rule=Host(`DEINE_DOMAIN`)"
      - "traefik.http.routers.forgejo.entrypoints=websecure"
      - "traefik.http.routers.forgejo.tls.certresolver=le"
      - "traefik.http.services.forgejo.loadbalancer.server.port=3000"
    networks: [proxy]
volumes:
  forgejo_data: {}
networks:
  proxy:
    external: true

Die wichtigsten Stellen im Detail:

  • FORGEJO__…-Variablen konfigurieren Forgejo direkt über Umgebungsvariablen (Abschnitt und Schlüssel per doppeltem Unterstrich). ROOT_URL muss exakt die öffentliche HTTPS-Adresse sein, sonst zeigen Klon-Links und Weiterleitungen ins Leere.
  • Git über SSH übernimmt Forgejos eingebauter SSH-Server (START_SSH_SERVER=true) – so musst du den gehärteten SSH-Zugang deines Hosts (Port 22) nicht anfassen. Er lauscht im Container auf 2222 und wird auf denselben Host-Port veröffentlicht.
  • loadbalancer.server.port=3000 sagt Traefik, dass die Weboberfläche intern auf Port 3000 läuft. Nur SSH (2222) wird direkt als Port veröffentlicht – der Web-Zugriff geht ausschließlich über Traefik.
  • Der Healthcheck mit start_interval: 2s ist bewusst so gesetzt (mehr dazu in „Wenn es nicht funktioniert").
  • DISABLE_REGISTRATION: "true" schließt die offene Selbst-Registrierung von Anfang an – dein Git-Server steht damit nicht für Fremde offen. Dein Admin-Konto legst du trotzdem ganz normal im Erstinstallations-Wizard an (Schritt 3); alternativ könntest du es über FORGEJO__admin__*-Variablen automatisiert erzeugen.

SSH-Port: unbedingt beide Werte setzen

SSH_PORT (die im Klon-Link angezeigte Portnummer) und SSH_LISTEN_PORT (der Port, auf dem der Server tatsächlich lauscht) müssen zusammenpassen. Setzt du nur SSH_PORT und lässt SSH_LISTEN_PORT weg, versucht Forgejo auf dem alten Standard-Port zu lauschen und stürzt beim Start mit bind: address already in use in eine Neustart-Schleife.

Schritt 2: Starten und auf „healthy" warten

Starte den Container:

Terminal
docker compose up -d

Prüfe den Status:

Terminal
docker compose ps

Nach wenigen Sekunden steht der Container auf healthy:

Ausgabe
NAME      IMAGE                                 SERVICE   STATUS                    PORTS
forgejo   codeberg.org/forgejo/forgejo:16.0.3   forgejo   Up 12 seconds (healthy)   22/tcp, 3000/tcp, 0.0.0.0:2222->2222/tcp, [::]:2222->2222/tcp

Das 22/tcp in der Portliste ist nur ein vom Image deklarierter Port ohne Veröffentlichung – nach außen erreichbar ist ausschließlich der 2222er, den du selbst gemappt hast.

Traefik holt jetzt im Hintergrund das Zertifikat. Prüfe von deinem Rechner aus, dass die Weboberfläche über HTTPS antwortet:

Terminal
curl -s https://DEINE_DOMAIN/api/healthz

Erwartete Ausgabe – Forgejo meldet sich gesund. Solange die Erstinstallation (Schritt 3) noch nicht durch ist, antwortet der Endpunkt knapp mit Status und Slogan:

Ausgabe
{
  "status": "pass",
  "description": "Forgejo: Beyond coding. We forge."
}

Nach dem Abschluss des Wizards listet dieselbe URL zusätzlich die Einzelprüfungen cache:ping und database:ping auf – dann steht dort auch dein Instanzname statt des Slogans.

Schritt 3: Erstinstallation & Admin-Konto

Rufe https://DEINE_DOMAIN im Browser auf. Beim ersten Start zeigt Forgejo die Erstinstallation. Die Datenbank- und Server-Einstellungen sind dank der Umgebungsvariablen bereits korrekt vorbelegt – du musst hier nur noch das Administrator-Konto anlegen (klapp den entsprechenden Abschnitt auf und trage Benutzername, E-Mail und ein starkes Passwort ein). Ein Klick auf Forgejo installieren, und nach einem kurzen Neustart landest du auf deinem leeren Dashboard:

Forgejo-Dashboard direkt nach der Ersteinrichtung – noch ohne Repositories.
Das Dashboard nach dem ersten Login.

Tipp

Die Install-Seite bleibt offen, bis der Wizard einmal abgeschlossen ist – schließe ihn deshalb sofort nach docker compose up -d ab und lege dabei dein Admin-Konto an. Die offene Selbst-Registrierung ist bereits über FORGEJO__service__DISABLE_REGISTRATION: "true" in der Compose deaktiviert, sodass sich niemand von außen ein Konto anlegen kann.

Schritt 4: Erstes Repository anlegen

Klicke oben rechts auf das + und dann auf Neues Repository. Vergib einen Namen und setze den Haken bei Repository initialisieren (legt direkt eine README an, damit das Repo nicht leer ist):

Formular „Neues Repository" in Forgejo mit Namensfeld und Initialisierungs-Option.
Ein neues Repository anlegen.

Nach dem Anlegen siehst du die Repository-Ansicht. Über den Umschalter HTTPS / SSH bekommst du die passende Klon-Adresse:

Repository-Ansicht in Forgejo mit README und dem HTTPS/SSH-Umschalter für die Klon-URL.
Die Klon-URL gibt es wahlweise für HTTPS oder SSH.

Schritt 5: Klonen und pushen über HTTPS

Am schnellsten geht es über HTTPS – es funktioniert sofort, ohne Schlüssel einzurichten:

Terminal
git clone https://DEINE_DOMAIN/DEIN_BENUTZER/mein-erstes-repo.git

Ein öffentliches Repository klonst du ohne Anmeldung – erwartete Ausgabe:

Ausgabe
Cloning into 'mein-erstes-repo'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (3/3), done.

Bei einem privaten Repository fragt Git schon hier nach Benutzername und Passwort. Damit ist der Kreis geschlossen – du legst eine Datei an, committest sie und schiebst sie zurück auf deinen Server:

Terminal
cd mein-erstes-repo
echo "Meine erste Notiz" >> notizen.txt
git add notizen.txt
git commit -m "Erste Notiz"
git push

Spätestens beim git push will Forgejo wissen, wer du bist: Git fragt nach Benutzername und Passwort. Am Ende bestätigt Git den neuen Stand auf dem Server:

Ausgabe
To https://DEINE_DOMAIN/DEIN_BENUTZER/mein-erstes-repo.git
   f398f79..7bad57b  main -> main

Tipp

Auf einem frischen Client scheitert der erste Commit mit Please tell me who you are, solange Git deinen Namen und deine E-Mail nicht kennt. Setze sie einmalig global: git config --global user.name "DEIN NAME" und git config --global user.email "DEINE_MAIL".

Sekunden später taucht der Commit in der Weboberfläche auf – dein Code liegt jetzt versioniert auf deinem eigenen Server.

Tipp

Nutze für HTTPS statt deines Passworts einen Zugangstoken (unter Einstellungen → Anwendungen). Der lässt sich einzeln widerrufen und ist Pflicht, sobald du für dein Konto Zwei-Faktor-Auth aktivierst.

Schritt 6: Klonen und pushen über SSH

Für den täglichen Gebrauch ist SSH bequemer (kein Passwort pro Push). Hinterlege dazu deinen öffentlichen SSH-Schlüssel unter Einstellungen → SSH- / GPG-Schlüssel → Schlüssel hinzufügen:

Forgejo-Einstellungsseite „SSH-Schlüssel verwalten" mit Button „Schlüssel hinzufügen".
Den öffentlichen SSH-Schlüssel im Konto hinterlegen.

Deinen öffentlichen Schlüssel zeigst du dir lokal mit cat ~/.ssh/id_ed25519.pub an (falls noch keiner existiert: ssh-keygen -t ed25519). Danach klonst du über SSH – beachte den Port 2222:

Terminal
git clone ssh://git@DEINE_DOMAIN:2222/DEIN_BENUTZER/mein-erstes-repo.git

Damit das klappt, muss der SSH-Port in beiden Firewalls offen sein – in der UFW-Firewall (ufw allow 2222/tcp) und, falls genutzt, in der netcup-Firewall.

Schritt 7: Mehr als nur Git-Hosting

Ein Repository und Klonen sind erst der Anfang – Forgejo bringt die komplette Werkbank drumherum mit. Diese Funktionen findest du in der Navigationsleiste jedes Repositories bzw. deines Kontos:

  • Issues & Pull-Requests: vollwertiges Ticket-System und Code-Review inklusive Labels, Meilensteinen und Zuweisungen – die Basis für Zusammenarbeit im Team.
  • Forgejo Actions: eine zu GitHub Actions kompatible CI/CD-Engine. Bestehende .github/workflows-Dateien laufen oft ohne Änderung. Actions brauchen allerdings einen separaten Runner, der die Jobs ausführt – der gehört aus Sicherheitsgründen nicht auf den Produktiv-Host und hat ein eigenes Tutorial: Forgejo Actions: eigener CI/CD-Runner mit Docker (führt dedizierte Kerne vor: ein guter Anlass für einen Root-Server, siehe netcup-Empfehlung).
  • Wiki & Releases: Dokumentation direkt am Projekt und versionierte Release-Downloads.
  • Paket-Registry: Forgejo kann Container-Images, npm-, Maven-, PyPI- und weitere Pakete hosten – praktisch, wenn du eigene Artefakte ablegen willst, ohne einen weiteren Dienst zu betreiben.
  • Organisationen & Teams: bündele Repositories und steuere Zugriffsrechte pro Team.

Du musst deine Projekte auch nicht von Hand umziehen: Über + → Migration importiert Forgejo ein bestehendes Repository von GitHub, GitLab oder einer anderen Forgejo-/Gitea-Instanz – inklusive Issues, Pull-Requests und Releases, nicht nur der Git-Historie. So wechselst du in wenigen Minuten von GitHub auf deinen eigenen Server, ohne etwas zu verlieren.

Wenn es nicht funktioniert

Der Container startet immer wieder neu (Restarting), im Log steht bind: address already in use. Der eingebaute SSH-Server kollidiert mit sich selbst, weil SSH_PORT und SSH_LISTEN_PORT nicht zusammenpassen. Setze beide auf denselben Wert (hier 2222) – dann startet Forgejo sauber.

Der Container braucht ewig, bis er healthy ist. Docker führt den ersten Healthcheck standardmäßig erst nach dem interval (30 s) aus – der Container sieht also 30 s+ „ungesund" aus, obwohl Forgejo längst in ~2 s bereit ist. Die Lösung steckt schon oben in der Compose: start_interval: 2s prüft während der Startphase im 2-Sekunden-Takt und schaltet sofort auf healthy, sobald die App antwortet. (Braucht Docker 25+ / Compose v2.20+ – auf Debian 13 gegeben.)

Traefik liefert 502 Bad Gateway. Fast immer der falsche Port: Forgejos Weboberfläche lauscht intern auf 3000, deshalb muss loadbalancer.server.port=3000 gesetzt sein und der Container im proxy-Netz hängen.

Klon-Links zeigen localhost oder den falschen Port. Dann stimmen ROOT_URL, SSH_DOMAIN oder SSH_PORT nicht. Korrigiere die Werte in der Compose und starte mit docker compose up -d neu.

SSH-Klon scheitert mit Permission denied (publickey). Der SSH-Server läuft, aber dein öffentlicher Schlüssel ist noch nicht im Konto hinterlegt (Schritt 6) – oder du hast den Port 2222 vergessen.

Wartung & Backups

Alles liegt im Volume forgejo_data (/data): die SQLite-Datenbank, deine Repositories und die Host-Keys des SSH-Servers. Genau dieses Volume gehört gesichert – am saubersten mit restic. Weil eine SQLite-Datei im Betrieb beschrieben wird, sicherst du sie konsistent, indem du entweder den Container kurz stoppst (docker compose stop) oder Forgejos eingebauten Dump nutzt:

Terminal
docker compose exec -u git forgejo forgejo dump -t /tmp -f /tmp/forgejo-dump.zip

-u git lässt den Dump als git-Benutzer statt als root laufen (als root verweigert Forgejo den Dump). Nach /tmp statt /data, weil das git-Konto in /data selbst nicht schreiben darf (das Wurzelverzeichnis /data gehört root, nur die Unterordner wie /data/gitea und /data/git gehören dem git-Benutzer) und der Dump sich sonst rekursiv in das gepackte Datenverzeichnis schreiben würde. Die fertige Zip holst du anschließend aus dem Container heraus – dorthin, wo restic sie mitsichert:

Terminal
docker compose cp forgejo:/tmp/forgejo-dump.zip ./forgejo-dump.zip

Die Zip enthält den Datenbank-Dump (forgejo-db.sql), die app.ini und alle Repository-Daten – ein vollständiger, für sich stehender Wiederherstellungspunkt.

Updates: Forgejo veröffentlicht regelmäßig neue Versionen (aktuell die 16er-Reihe). Für ein Update setzt du den neuen Tag in der compose.yaml (statt 16.0.3) und ziehst ihn:

Terminal
docker compose pull && docker compose up -d

Weil die Daten im Volume liegen, bleiben Repos und Konten dabei erhalten. Pinne die Version bewusst auf einen festen Tag statt latest und wirf vor einem Major-Sprung einen Blick in die Release-Notes – dort stehen eventuelle Migrations-Hinweise. Mache außerdem vor jedem Update ein frisches Backup: ein Git-Server ohne funktionierendes Backup ist ein Klumpenrisiko für deinen gesamten Code.

Absichern: Ein öffentlich erreichbarer Git-Server ist ein lohnendes Ziel. Drei Dinge solltest du gleich erledigen: die offene Registrierung deaktivieren (siehe Schritt 3), für dein Admin-Konto unter Einstellungen → Sicherheit die Zwei-Faktor-Authentifizierung einschalten und für den Alltag ein normales Konto statt des Administrators nutzen. Halte Forgejo zeitnah aktuell – die schnellen Releases enthalten auch Sicherheitsfixes. Und veröffentliche nur die Ports, die du wirklich brauchst: nach außen genügen 443 (Traefik) und dein SSH-Git-Port 2222.

Zuletzt aktualisiert: 29.08.2026

Feedback per E-Mail: feedback@serverkueche.de

Wie geht's weiter?

Das könnte dir auch schmecken