Forgejo nativ auf Ubuntu installieren: Binary, systemd, Nginx und MariaDB
Forgejo – den community-getriebenen Gitea-Fork – als einzelnes Go-Binary direkt auf Ubuntu deployen: ohne Docker-Overhead, mit dediziertem Systemuser, MariaDB als Produktionsdatenbank, systemd-Service und Nginx als Reverse Proxy mit SSL.

Wer einen eigenen Git-Server betreiben will, stößt früher oder später auf Forgejo – den community-getriebenen Fork von Gitea, der seit 2022 unter dem Dach von Codeberg.org entwickelt wird. Anders als GitLab CE kommt Forgejo als einzelnes statisches Go-Binary daher: keine Runtime-Abhängigkeiten, kein aufgeblähter Container-Stack, kein Kubernetes. Diese Anleitung zeigt dir, wie du Forgejo 15.0.3 direkt auf einem Ubuntu-Server deployest – mit systemd-Unit, Nginx-Reverse-Proxy, MariaDB als Produktionsdatenbank und optionalem Let's-Encrypt-SSL. Der Ansatz eignet sich besonders für KMU-Admins und Selfhoster, die volle Kontrolle über jeden Layer behalten wollen und auf den Docker-Overhead verzichten möchten.
Voraussetzungen
- Ubuntu-Server 20.04, 22.04 oder 24.04 LTS (amd64 oder arm64)
- Root- oder sudo-Zugriff auf den Server
- Domain oder Subdomain (z. B.
git.example.com) mit DNS-A-Record auf die Server-IP - Offene Ports: 22 (SSH), 80 (HTTP), 443 (HTTPS) – Port 22 muss vor dem Aktivieren der Firewall freigeschaltet sein
- Mindestens 1 GB RAM (empfohlen: 2 GB+), 10 GB freier Speicher für Repositories
- Forgejo-Binary 15.0.3 von
codeberg.org/forgejo/forgejo/releases
| Komponente | Version / Paket | Zweck |
|---|---|---|
| Forgejo Binary | 15.0.3 (Go, statisch) | Anwendungsserver auf Port 3000 |
| MariaDB | 11.4 LTS (apt) | Produktionsdatenbank mit utf8mb4_bin |
| Nginx | apt (stable) | Reverse Proxy, TLS-Terminierung |
| Certbot | python3-certbot-nginx | Let's Encrypt SSL automatisch |
| systemd | Ubuntu built-in | Service-Management, Autostart |
| git + git-lfs | apt | Pflichtabhängigkeit von Forgejo |
Schritt 1: System vorbereiten und Abhängigkeiten installieren
Aktualisiere zunächst das System und installiere alle notwendigen Pakete. git und git-lfs müssen vor dem ersten Forgejo-Start vorhanden sein – der Dienst prüft die Verfügbarkeit beim Start und schlägt andernfalls stumm fehl.
sudo apt update && sudo apt upgrade -y
sudo apt install -y git git-lfs wget curl ufw ca-certificates nginx mariadb-serverVerifizieren: git --version und git-lfs --version müssen jeweils eine Versionsnummer ausgeben.
Schritt 2: Forgejo-Binary herunterladen und installieren
Forgejo wird als einzelnes statisches Binary ausgeliefert – keine Go-Runtime, keine zusätzlichen Bibliotheken nötig. Lade die aktuelle Version herunter und prüfe die SHA256-Prüfsumme, bevor du das Binary produktiv einsetzt.
cd /tmp
wget -O forgejo \
https://codeberg.org/forgejo/forgejo/releases/download/v15.0.3/forgejo-15.0.3-linux-amd64
# SHA256-Prüfsumme verifizieren (empfohlen)
wget https://codeberg.org/forgejo/forgejo/releases/download/v15.0.3/forgejo-15.0.3-linux-amd64.sha256
sha256sum -c forgejo-15.0.3-linux-amd64.sha256
sudo cp forgejo /usr/local/bin/forgejo
sudo chmod 755 /usr/local/bin/forgejoFür ARM64-Server (z. B. Hetzner CAX, Raspberry Pi 4/5) ersetzt du linux-amd64 durch linux-arm64 bzw. linux-arm-6 – offizielle Binaries existieren für alle gängigen Architekturen.
Verifizieren: forgejo --version gibt Forgejo version 15.0.3 aus.
Schritt 3: Systemuser und Verzeichnisstruktur anlegen
Forgejo läuft unter einem dedizierten Systemuser git. Der Name ist kein Zufall: SSH-Clone-URLs haben dann die vertraute Form git@git.example.com:Org/Repo.git – genauso wie bei GitHub oder Codeberg. Der User hat kein Passwort und keine interaktive Shell nach außen.
sudo adduser --system --shell /bin/bash --gecos 'Git Version Control' \
--group --disabled-password --home /home/git git
# Verzeichnisstruktur
sudo mkdir -p /var/lib/forgejo/{data,custom,log}
sudo mkdir -p /etc/forgejo
# Berechtigungen setzen
sudo chown git:git /var/lib/forgejo && sudo chmod 750 /var/lib/forgejo
sudo chown git:git /var/lib/forgejo/log
# /etc/forgejo: während des Web-Setups braucht git Schreibrecht (770)
sudo chown root:git /etc/forgejo && sudo chmod 770 /etc/forgejo| Pfad | Eigentümer | Rechte (Setup) | Rechte (nach Setup) |
|---|---|---|---|
| /usr/local/bin/forgejo | root:root | 755 | 755 |
| /var/lib/forgejo | git:git | 750 | 750 |
| /etc/forgejo | root:git | 770 | 750 |
| /etc/forgejo/app.ini | root:git | (vom Wizard angelegt) | 640 |
| /etc/systemd/system/forgejo.service | root:root | 644 | 644 |
Schritt 4: MariaDB vorbereiten
MariaDB ist die empfohlene Produktionsdatenbank für Forgejo. Entscheidend ist die Collation utf8mb4_bin – Forgejo erwartet case-sensitive String-Vergleiche und bricht die Ersteinrichtung ab, wenn die Datenbank mit utf8mb4_unicode_ci angelegt wurde. Dieser Fallstrick kostet Zeit; leg die Datenbank daher von Anfang an korrekt an.
sudo mysql_secure_installation
sudo mysql -u root -p
CREATE USER 'forgejo'@'localhost' IDENTIFIED BY 'StarkesPasswort!';
CREATE DATABASE forgejo CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_bin';
GRANT ALTER, CREATE, DELETE, DROP, INDEX, INSERT, REFERENCES, SELECT, UPDATE
ON forgejo.* TO 'forgejo'@'localhost';
FLUSH PRIVILEGES;
EXIT;Verifizieren: sudo mysql -u forgejo -p forgejo -e "SHOW CREATE DATABASE forgejo\G" – in der Ausgabe muss utf8mb4_bin stehen.
| Datenbank | Empfehlung | Besonderheit |
|---|---|---|
| SQLite3 | Nur Entwicklung/Test | Kein Extra-Setup, aber kein Multi-User-Betrieb |
| MariaDB/MySQL | Produktion (Standard) | utf8mb4_bin Pflicht, unbegrenzte Nutzer |
| PostgreSQL | Produktion (Alternative) | Vollständig unterstützt, ebenfalls empfohlen |
Schritt 5: systemd-Service einrichten
Der offizielle systemd-Service liegt auf Codeberg als Vorlage bereit. Für eine MariaDB-Abhängigkeit müssen die Wants=- und After=-Zeilen im [Unit]-Block aktiviert werden – sonst startet Forgejo beim Booten vor der Datenbank und schlägt fehl. Diesen Schritt vergessen viele, und dann wundert man sich über sporadische Start-Fehler nach Reboots.
sudo wget -O /etc/systemd/system/forgejo.service \
https://codeberg.org/forgejo/forgejo/raw/branch/forgejo/contrib/systemd/forgejo.serviceÖffne die Service-Datei und stelle sicher, dass im [Unit]-Block die MariaDB-Abhängigkeit aktiv ist (Kommentarzeichen entfernen):
[Unit]
Description=Forgejo Git Service
After=network.target mariadb.service
Wants=mariadb.serviceFalls der Download fehlschlägt, kannst du die minimale Vorlage direkt anlegen:
[Unit]
Description=Forgejo Git Service
After=network.target mariadb.service
Wants=mariadb.service
[Service]
User=git
Group=git
WorkingDirectory=/var/lib/forgejo
Environment=FORGEJO_WORK_DIR=/var/lib/forgejo
ExecStart=/usr/local/bin/forgejo web --config /etc/forgejo/app.ini
Restart=on-failure
RestartSec=2s
WatchdogSec=30s
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable forgejo.service
sudo systemctl start forgejo.service
sudo systemctl status forgejo.serviceVerifizieren: sudo systemctl status forgejo.service zeigt active (running). Mit sudo journalctl -n 50 --unit forgejo.service kannst du die letzten Log-Zeilen prüfen – nach dem ersten Start ohne app.ini erscheint eine Meldung über den Web-Setup-Wizard.
Schritt 6: Nginx als Reverse Proxy konfigurieren
Forgejo lauscht intern auf 127.0.0.1:3000 – Nginx übernimmt die öffentliche Erreichbarkeit auf Port 80/443. Zwei Details sind hier wichtig: merge_slashes off verhindert, dass URL-kodierte Schrägstriche in Repository-Pfaden zusammengefasst werden (ohne diese Direktive gibt es API-404-Fehler). Die WebSocket-Header Upgrade und Connection sind für die Live-Aktualisierungen im Web-Interface nötig.
sudo nano /etc/nginx/sites-available/forgejo
server {
listen 80;
listen [::]:80;
server_name git.example.com;
merge_slashes off;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Connection $http_connection;
proxy_set_header Upgrade $http_upgrade;
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;
}
}
sudo ln -s /etc/nginx/sites-available/forgejo /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginxDer client_max_body_size 512M-Wert ist keine Empfehlung für kleine Projekte, sondern eine Notwendigkeit: Nginx verwirft große Git-Pushes und LFS-Uploads standardmäßig mit HTTP 413. Für sehr große Repositories kannst du den Wert noch weiter erhöhen.
Verifizieren: sudo nginx -t gibt syntax is ok und test is successful aus.
Schritt 7: SSL mit Let's Encrypt (Certbot)
Certbot konfiguriert Nginx automatisch für HTTPS und richtet die Zertifikatserneuerung per Cronjob ein. Stelle sicher, dass der DNS-A-Record für git.example.com bereits auf die Server-IP zeigt – Certbot validiert per HTTP-Challenge.
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d git.example.comAlternativ kannst du SSL auch in Forgejo selbst konfigurieren (integrierter ACME-Client) – für die meisten Setups ist Certbot via Nginx jedoch die flexiblere Lösung, da Nginx bereits als Reverse Proxy läuft. Wer lieber Caddy als automatischen Reverse Proxy nutzt, findet die entsprechende Anleitung weiter unten verlinkt.
Verifizieren: sudo certbot certificates zeigt das Zertifikat mit Ablaufdatum. Ein Aufruf von https://git.example.com im Browser sollte das grüne Schloss zeigen.
Schritt 8: Firewall konfigurieren
Wichtig: Port 22 (SSH) muss vor dem Aktivieren von UFW freigeschaltet sein – sonst sperrst du dich vom Server aus.
sudo ufw allow OpenSSH
sudo ufw allow 80,443/tcp
sudo ufw enableSSH-Port 22 läuft direkt am git-User und wird nicht über Nginx geleitet. Forgejo verwaltet ~/.ssh/authorized_keys des git-Users automatisch, wenn Nutzer ihren öffentlichen Schlüssel im Web-Interface hinterlegen.
Schritt 9: Web-Setup abschließen
Rufe http://git.example.com (oder bereits https:// nach Certbot) im Browser auf. Die Ersteinrichtungsseite erscheint nur beim allerersten Aufruf und speichert die Konfiguration in /etc/forgejo/app.ini.
Fülle das Formular wie folgt aus:
- Datenbanktyp: MySQL/MariaDB
- Host:
127.0.0.1:3306 - Datenbankname:
forgejo, User:forgejo, Passwort: wie in Schritt 4 gesetzt - Domain:
git.example.com - Base URL:
https://git.example.com/(mit abschließendem Slash!) - Admin-Konto anlegen
Nach dem Abschicken startet Forgejo ggf. kurz neu und leitet auf das Dashboard weiter.
Schritt 10: Post-Install-Härtung und Produktions-app.ini
Nach dem Web-Setup sind zwei Maßnahmen zwingend: Erstens müssen /etc/forgejo und app.ini auf sichere Berechtigungen gesetzt werden (der Wizard hat temporär Schreibrecht benötigt). Zweitens muss INSTALL_LOCK = true gesetzt sein – sonst kann jeder mit Zugriff auf die URL die Instanz neu konfigurieren oder übernehmen.
sudo systemctl stop forgejo.service
sudo chmod 750 /etc/forgejo
sudo chmod 640 /etc/forgejo/app.ini
sudo systemctl start forgejo.serviceEmpfohlene Produktions-Einstellungen für /etc/forgejo/app.ini (Auszug, ergänze oder passe an was der Web-Wizard bereits gesetzt hat):
[security]
INSTALL_LOCK = true
SECRET_KEY = <generiert via: sudo -u git forgejo generate secret SECRET_KEY>
INTERNAL_TOKEN = <generiert via: sudo -u git forgejo generate secret INTERNAL_TOKEN>
MIN_PASSWORD_LENGTH = 12
PASSWORD_COMPLEXITY = lower,upper,digit,spec
[server]
DOMAIN = git.example.com
ROOT_URL = https://git.example.com/
HTTP_ADDR = 127.0.0.1
HTTP_PORT = 3000
APP_DATA_PATH = /var/lib/forgejo/data
OFFLINE_MODE = true
[database]
DB_TYPE = mysql
HOST = 127.0.0.1:3306
NAME = forgejo
USER = forgejo
PASSWD = StarkesPasswort!
CHARSET_COLLATION = utf8mb4_bin
[repository]
ROOT = /var/lib/forgejo/repositories
[log]
MODE = file
LEVEL = info
ROOT_PATH = /var/lib/forgejo/log
[service]
DISABLE_REGISTRATION = true
[repository.upload]
FILE_MAX_SIZE = 4095
MAX_FILES = 20Wichtig: SECRET_KEY und INTERNAL_TOKEN niemals manuell erfinden oder leer lassen – immer über sudo -u git forgejo generate secret SECRET_KEY erzeugen. Der Web-Wizard setzt diese Werte normalerweise automatisch; prüfe dennoch, ob sie vorhanden sind.
Für Admin-Befehle auf der Kommandozeile ist ein kleiner CLI-Wrapper praktisch:
sudo tee /usr/local/bin/forgejo-cli << 'EOF'
#!/bin/sh
sudo -u git forgejo -w /var/lib/forgejo -c /etc/forgejo/app.ini "$@"
EOF
sudo chmod 755 /usr/local/bin/forgejo-cli
# Beispiel: forgejo-cli admin user listVerifizieren: forgejo-cli admin user list listet alle vorhandenen Benutzer auf.
Troubleshooting / Typische Fehler
Ersteinrichtung schlägt mit Datenbankfehler fehl
Ursache: Datenbank mit falscher Collation angelegt (utf8mb4_unicode_ci statt utf8mb4_bin). Lösung: Datenbank droppen und mit korrekter Collation neu anlegen. Ein späterer Wechsel ist aufwändig – von Anfang an richtig machen.
Forgejo startet nach Reboot nicht
Ursache: Wants=mariadb.service und After=mariadb.service fehlen im systemd-Unit. Forgejo versucht, sich vor MariaDB zu verbinden. Lösung: Service-Datei anpassen (Schritt 5), dann sudo systemctl daemon-reload.
HTTP 413 bei großen Git-Pushes
Ursache: client_max_body_size in Nginx ist zu klein (Standard: 1 MB). Lösung: in der Nginx-Konfiguration auf mindestens 512M erhöhen und Nginx neu starten.
404-Fehler bei bestimmten API-Requests
Ursache: merge_slashes off fehlt in der Nginx-Konfiguration. URL-kodierte Schrägstriche in Repository-Pfaden werden zusammengefasst. Lösung: Direktive zum server {}-Block hinzufügen.
Web-Setup-Wizard erzeugt keine app.ini
Ursache: /etc/forgejo hat chmod 750 statt 770, der git-User kann nicht schreiben. Lösung: sudo chmod 770 /etc/forgejo während des Setups, danach auf 750 zurücksetzen.
Binary startet nicht
Ursache: chmod 755 vergessen. Das Binary muss ausführbar sein. Prüfen mit ls -la /usr/local/bin/forgejo.
app.ini ROOT_URL falsch
Ursache: ROOT_URL enthält http:// statt https:// oder fehlt der abschließende Slash. Alle generierten Links (z. B. Clone-URLs, E-Mail-Links) sind dann falsch. Lösung: app.ini anpassen und Forgejo neu starten.
Häufige Fragen
Kann ich später von SQLite auf MariaDB migrieren?
Ja, Forgejo bietet ein integriertes Migrations-Tool (forgejo convert) sowie den Export über das Admin-Panel. Der Aufwand ist aber nicht gering – die Empfehlung lautet: gleich mit MariaDB starten, wenn der Produktivbetrieb geplant ist.
Wie update ich Forgejo auf eine neue Version?
Service stoppen, das alte Binary als Backup sichern (sudo cp /usr/local/bin/forgejo /usr/local/bin/forgejo.bak), neues Binary herunterladen, nach /usr/local/bin/forgejo kopieren, chmod 755 setzen, Service starten. Datenbank-Migrationen laufen automatisch beim ersten Start der neuen Version. Immer vorher ein MariaDB-Backup erstellen.
Läuft Forgejo auch auf ARM-Servern (z. B. Hetzner CAX, Raspberry Pi)?
Ja, offizielle Binaries gibt es für linux-arm64 und linux-arm-6 auf der Release-Seite bei Codeberg. Dieselbe Anleitung funktioniert; nur den Binary-Download-Link anpassen.
Wie richte ich SSH-Key-Authentifizierung ein?
SSH läuft direkt über den git-User auf Port 22 – vollständig außerhalb von Nginx. Nutzer laden ihren öffentlichen SSH-Schlüssel einfach im Forgejo-Web-Interface unter „Einstellungen → SSH-Schlüssel" hoch. Forgejo verwaltet /home/git/.ssh/authorized_keys automatisch.
Wie konfiguriere ich E-Mail-Versand?
Im app.ini unter [mailer]: ENABLED = true, PROTOCOL = smtp, SMTP_ADDR = mail.example.com, SMTP_PORT = 587, USER = ..., PASSWD = ..., FROM = forgejo@example.com. Anschließend Forgejo neu starten.
Wie sperre ich die öffentliche Registrierung?
In app.ini unter [service]: DISABLE_REGISTRATION = true. Danach können Nutzerkonten nur noch vom Admin über das Web-Interface oder via forgejo-cli admin user create angelegt werden.
Fazit
Forgejo als natives Binary zu betreiben hat gegenüber der Docker-Variante einen echten Charme: Die Ressourcen-Overhead fällt weg, das Debugging ist direkter, und systemd übernimmt das Service-Management genau so, wie man es von anderen Server-Diensten kennt. Der Einrichtungsaufwand ist überschaubar – die kritischen Punkte sind die korrekte MariaDB-Collation, die temporären Schreibrechte auf /etc/forgejo während des Web-Setups und das abschließende Setzen von INSTALL_LOCK = true. Mit diesen Grundlagen hast du einen produktionstauglichen, eigenständigen Git-Server, der in 60 Minuten einsatzbereit ist.
Weiterführende Anleitungen und Quellen
- Nginx als Reverse Proxy mit TLS manuell einrichten – Grundlagen für erweiterte Nginx-Konfigurationen
- Caddy als Reverse-Proxy mit automatischem HTTPS – wann er Nginx und Traefik schlägt – Alternative zu Certbot/Nginx
- systemd-Service selbst erstellen und verwalten – Hintergrundwissen zu Unit-Dateien und Abhängigkeiten
- MariaDB / MySQL installieren und absichern – Vertiefte MariaDB-Konfiguration und Härtung
- Nextcloud nativ auf Ubuntu installieren (LEMP ohne Docker) – gleicher Stack-Ansatz für eine andere Plattform
- Gitea mit Docker: eigener Git-Server als GitHub-Alternative – Docker-Variante zum Vergleich
Offizielle Quellen: