DISABLE_REGISTRATION = true. Sie legen Konten an; niemand bedient sich selbst. Falls Sie gelegentlich Registrierungen brauchen, nutzen Sie den Einladungs-Ablauf, statt die Tür zu öffnen.

Ihr eigener Git-Server.
Das Repository ist das Organigramm.
Ein privates Repository ist eine datierte Aufzeichnung dessen, was Sie bauen, mit wem Sie es bauen, und wie weit Sie damit sind — und auf einer Plattform wird diese Aufzeichnung von einem Unternehmen in einem fremden Rechtsraum gehalten. Sechs Schritte, um sie auf eine Maschine umzuziehen, die Sie anonym mieten: Forgejo hinter Caddy, PostgreSQL darunter, SSH-Push auf einem eigenen Port, ein Actions-Runner, der Ihre Produktions-Secrets nie zu Gesicht bekommt, und ein Backup, das Sie tatsächlich wiederhergestellt haben. Die Maschine kostet $3.90 im Monat. Getestet auf Debian 12.
- 01
Wählen
Bare-Repo, Forgejo, GitLab
- 02
Bereitstellen
Ports vor dem Boot festgelegt
- 03
Bereitstellen
Forgejo, Postgres, Caddy
- 04
Push
SSH, Deploy-Keys, Signaturen
- 05
Build
Ein Runner, auf Abstand gehalten
- 06
Behalten
Mirrors, Backups, Upgrades
Der Code ist der uninteressanteste Teil. Die Metadaten sind das Kapital.
Fast jedes Argument über das Self-Hosting von Git ist ein Argument über den Quellcode, und der Quellcode ist der Teil, der am wenigsten zählt. Das meiste davon ist unauffällig, ein großer Teil davon ist nur eine dünne Schicht über Open-Source-Bibliotheken, und würde er morgen durchsickern, wäre der praktische Schaden meist Peinlichkeit statt echter Schaden. Deshalb fühlt sich „mein Code ist nicht geheim“ wie eine vollständige Antwort an — und deshalb ist es keine.
Was den Code umgibt, ist der interessante Teil. Ein Repository ist eine datierte, zugeordnete, hochauflösende Aufzeichnung von Arbeit: Commit-Zeitstempel, die verraten, wann Sie schlafen, Co-Autor-Zeilen, die verraten, mit wem Sie arbeiten, Branch-Namen, die Produkte verraten, die niemand angekündigt hat, ein Issue-Tracker, der verrät, was kaputt ist und wie schlimm, und ein erster Commit, der das ganze Projekt datiert. Lesen Sie hundert Repositories auf diese Weise, und Sie lesen keine Software mehr — Sie lesen ein Unternehmen, seine Roadmap, seinen Personalstand und seine Schwierigkeiten.
Auf einer Plattform gehört Ihnen diese Aufzeichnung in keinem nennenswerten Sinn. Sie liegt auf Infrastruktur, die Sie nicht kontrollieren, unter einem Konto, das an eine von Ihnen verifizierte Identität gebunden ist, in einem Rechtsraum, dessen Rechtsweg sie erreicht, gescannt von Systemen, deren Umfang von einem Dokument festgelegt wird, das geändert werden kann. Nichts davon erfordert bösen Willen von irgendjemandem: Es ist einfach das, was es bedeutet, den Tracker zu mieten. Vier Konstellationen, und was jede davon tatsächlich preisgibt:
| Wo das Repository liegt | Wer die Historie hält | An Ihre rechtliche Identität gebunden | Wie es schiefgeht |
|---|---|---|---|
| Eine Plattform, persönliches Konto | Die Plattform, in ihrem eigenen Rechtsraum | Ja — E-Mail, Rechnungsdaten, oft eine Telefonnummer | Eine Sperrung reißt jeden Remote mit sich |
| Eine Plattform, Organisationskonto | Die Plattform, plus wer auch immer die Organisation verwaltet | Ja, und auch an Ihren Arbeitgeber | Offboarding entzieht Ihnen den Zugriff auf Ihre eigene Arbeit |
| Selbstgehostet, bei einem Hyperscaler | Sie, auf der Anwendungsebene | Ja — das Abrechnungskonto ist die Identität | Der Snapshot liegt in dem Konto, das geschlossen werden kann |
| Selbstgehostet, auf einem KYC-freien VPS | Sie, und nur Sie | Nein — keine ID, keine Karte, mit Krypto beglichen | Sie sind jetzt der Sysadmin. Ein schlechtes Backup ist endgültig |
Zeile drei ist die Zeile, die Teams erwischt, die glauben, das Problem schon gelöst zu haben. Eine Forge, die Sie selbst installiert haben, auf einer Maschine, die mit einer Firmenkarte unter einem verifizierten Firmenkonto gemietet wurde, hat die Anwendung von der Plattform weggeholt und die Identität exakt dort belassen, wo sie war. Das ist ein echter Gewinn an Kontrolle und überhaupt kein Gewinn bei der Exposition.
Und Zeile vier benennt ihre eigenen Kosten unverblümt, denn der Rest dieses Leitfadens handelt größtenteils davon, sie zu bezahlen. Der Fehlermodus des Self-Hostings ist nicht Überwachung, sondern Verlust. Eine Plattform führt Backups, über die Sie nie nachdenken. Ihr Server führt die Backups, die Sie konfiguriert haben. Schritt 06 ist nicht der optionale Feinschliff am Ende — er ist der Schritt, der die anderen fünf sicher macht.
Wie viel Forge brauchen Sie? Die ehrliche Antwort lautet oft „keine“.
Beginnen Sie mit der kleinsten Sache, die Ihr Problem löst, denn jede Schicht darüber ist ein Dienst, den Sie betreiben müssen. Ein Bare-Repository über SSH ist ein einziger Befehl auf einem Server, den Sie bereits haben, und es gibt Ihnen einen echten Git-Remote mit Authentifizierung, Historie und Push-Zugriff. Keine Webanwendung, keine Datenbank, keine Upgrade-Tretmühle, nichts zu patchen außer sshd. Wenn Sie eine einzelne Person sind, die ihre Commits vom Laptop und von einer Plattform weghaben will, ist das eine vollständige Antwort, und Sie können nach Schritt 02 aufhören zu lesen.
# On the server, as an unprivileged user:
mkdir -p ~/git/project.git && cd ~/git/project.git
git init --bare
# On your laptop:
git remote add origin ssh://user@git.example.com/~/git/project.git
git push -u origin main
Eine Forge brauchen Sie, wenn eine zweite Person beteiligt ist, oder wenn Sie eines der vier Dinge wollen, die ein Verzeichnis aus Bare-Repositories nicht kann: die Historie im Browser durchsehen, Änderungen per Pull Request begutachten, Issues direkt neben dem Code verfolgen, und einer Person Zugriff auf ein Repository gewähren. Das ist ein echter Schritt nach oben bei den Fähigkeiten und ein echter Schritt nach oben beim Betriebsaufwand. Wählen Sie bewusst:
| Eigenschaft | Bare-Repo über SSH | Forgejo | Gitea | GitLab CE |
|---|---|---|---|---|
| Web-UI, Pull Requests, Issues | Keine | Ja | Ja | Ja, und noch viel mehr |
| RAM im Leerlauf | Praktisch null | Ein paar Hundert MB | Ein paar Hundert MB | Mehrere GB, ohne CI |
| Eingebaute CI | Git-Hooks, wenn Sie sie schreiben | Forgejo Actions | Gitea Actions | GitLab CI, die ausgereifte |
| Wer das Projekt steuert | Das Git-Projekt | Codeberg e.V., gemeinnützig | Ein gewinnorientiertes Unternehmen, Open Core | Ein börsennotiertes Unternehmen, Open Core |
| Lizenz | GPLv2 | GPLv3+ seit v9.0 (davor MIT) | MIT, mit proprietären Add-ons | MIT-Kern, proprietäre Tarife |
| Wählen Sie sie, wenn | Sie sind allein und wollen null Wartungsaufwand | Sie wollen eine echte Forge auf einer kleinen Maschine | Sie betreiben es bereits, und es funktioniert | Sie brauchen den Enterprise-Funktionsumfang |
Forgejo und Gitea teilen einen gemeinsamen Vorfahren und sehen im täglichen Gebrauch noch immer fast identisch aus. Forgejo wurde Anfang 2024 zu einem Hard Fork und wird von der Codeberg e.V. gehalten, einem gemeinnützigen eingetragenen Verein in Berlin; Gitea wird von einem gewinnorientierten Unternehmen entwickelt, das seine Marke besitzt und von Mitwirkenden die Abtretung des Copyrights verlangt. Beide sind gute Software. Nur eines der beiden hat einen Hüter, dessen Interessen zu dem Grund passen, aus dem Sie diese Seite lesen.
Der Rest dieses Leitfadens verwendet Forgejo. Alles in den Schritten 02 bis 06 gilt fast wortgleich auch für Gitea — der Containername und ein paar Umgebungsvariablen ändern sich, die Grundform nicht.
Entscheiden Sie über die Ports, bevor die Forge sie für Sie entscheidet. Zwei davon sind SSH.
Bestellen Sie zuerst die Maschine. Im Panel: Bestellen → VPS → Sentinel, Image Debian 12, in welcher Bastion auch immer die Historie leben soll — Helsinki, Stockholm, Oslo oder Reykjavík. Für die Kontoeröffnung ist keine E-Mail-Adresse nötig, und die Rechnung wird in Monero, Bitcoin, Lightning oder jeder anderen unterstützten Kryptowährung beglichen. Das zählt hier mehr als bei den meisten Workloads: Eine Forge ist der eine Dienst, dessen gesamtes Wertversprechen darin besteht, dass die Aufzeichnung Ihrer Arbeit an keinen Namen gebunden ist.
Durchlaufen Sie dann die Checkliste zur Härtung der ersten Stunde — SSH nur mit Schlüssel auf einem verlegten Port, unbeaufsichtigte Updates, eine Default-Deny-Firewall — denn alles Folgende setzt eine Maschine voraus, die nicht schon jemand anderem gehört.
Eine Forge ist keine statische Seite mit Login. Sie ist eine öffentliche Webanwendung mit einer daran angeflanschten Code-Ausführungs-Engine. Zwei Standardeinstellungen machen sie, wenn man sie unangetastet lässt, zu einem kostenlosen Dienst für Fremde: offene Registrierung, die die Instanz innerhalb weniger Tage, nachdem das Zertifikat in den Transparency-Logs erscheint, mit Spam-Konten füllt, und CI, die Workflows von jedem ausführt, der einen Pull Request öffnen kann, was der Weg ist, auf dem ein selbstgehosteter Runner am Ende für irgendjemand anderen Kryptowährung schürft. Keines von beiden ist ein raffinierter Angriff, und keines von beiden ist selten. Beide werden mit je einer Einstellung geschlossen, in Schritt 03 und Schritt 05 — aber entscheiden Sie jetzt schon, dass Sie sie schließen werden, denn das Fenster zwischen „es funktioniert“ und „es wird benutzt“ wird in Tagen gemessen.
Richten Sie einen Hostnamen auf die Maschine — einen A-Record, und einen AAAA, wenn sie über IPv6 erreichbar sein soll — und lassen Sie ihn propagieren, bevor Sie beginnen, denn der Reverse Proxy wird beim ersten Hochfahren versuchen, ein Zertifikat für diesen Namen zu beziehen, und eine fehlgeschlagene Challenge ist ein verwirrender Einstieg. Schreiben Sie dann die Firewall-Regeln, solange noch nichts lauscht.
Das Zwei-SSH-Problem. Eine Forge spricht SSH für Git, und Ihr Server spricht bereits SSH für die Administration. Beide können nicht Port 22 besitzen, und sie zu vermischen ist der Weg, auf dem Menschen bei einem administrativen Daemon landen, der auch noch Git verstehen muss. Halten Sie sie getrennt: Administration auf dem verlegten Port aus der Härtungs-Checkliste, Git auf einem eigenen öffentlichen Port, der in den Container gemappt wird. 2222 ist die konventionelle Wahl, und daran ist nichts Heiliges.
ufw default deny incoming
ufw allow 47222/tcp # administrative SSH, moved
ufw allow 80/tcp # ACME HTTP-01 challenge
ufw allow 443/tcp # the web interface
ufw allow 2222/tcp # git over SSH, into the container
ufw enable
ufw status numbered
# Nothing else is open. In particular: PostgreSQL stays on the Docker
# network and is never published to the host, let alone the internet.
Installieren Sie Docker aus dem offiziellen Repository statt aus dem Distributionspaket, damit Compose v2 vorhanden und die Version aktuell ist. Legen Sie dann ein Verzeichnis für den Stack an, und machen Sie es zur einzigen Stelle auf der Maschine, die Zustand hält — es ist das Verzeichnis, das Schritt 06 sichert.
curl -fsSL https://get.docker.com | sh
docker compose version
install -d -m 0750 /srv/forgejo
cd /srv/forgejo
Drei Container. Einer von ihnen darf nie erreichbar sein.
Forgejo, PostgreSQL und Caddy. Forgejo hält die Repositories und liefert die Oberfläche aus, PostgreSQL hält alles, was kein Git-Objekt ist, und Caddy terminiert TLS und bezieht das Zertifikat, ohne zweimal gefragt werden zu müssen. PostgreSQL wird bewusst nicht auf den Host veröffentlicht: Es ist nur über das Compose-Netzwerk erreichbar, per Name, von dem einen Container aus, der es braucht.
/srv/forgejo/.env
FORGEJO_DOMAIN=git.example.com
FORGEJO_VERSION=15 # the LTS line; 16 is current stable
POSTGRES_PASSWORD=$(openssl rand -base64 32) # paste the value, do not leave the $( )
/srv/forgejo/docker-compose.yml
services:
forgejo:
image: codeberg.org/forgejo/forgejo:${FORGEJO_VERSION}
container_name: forgejo
environment:
USER_UID: 1000
USER_GID: 1000
FORGEJO__database__DB_TYPE: postgres
FORGEJO__database__HOST: db:5432
FORGEJO__database__NAME: forgejo
FORGEJO__database__USER: forgejo
FORGEJO__database__PASSWD: ${POSTGRES_PASSWORD}
FORGEJO__server__DOMAIN: ${FORGEJO_DOMAIN}
FORGEJO__server__ROOT_URL: https://${FORGEJO_DOMAIN}/
FORGEJO__server__SSH_DOMAIN: ${FORGEJO_DOMAIN}
FORGEJO__server__SSH_PORT: "2222" # what clones will say
FORGEJO__server__SSH_LISTEN_PORT: "22" # what the container listens on
FORGEJO__service__DISABLE_REGISTRATION: "true"
FORGEJO__repository__DEFAULT_PRIVATE: private
FORGEJO__security__INSTALL_LOCK: "true"
restart: unless-stopped
volumes:
- ./data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "127.0.0.1:3000:3000" # web, reached only by Caddy
- "2222:22" # git over SSH, public
depends_on:
- db
db:
image: postgres:16-alpine
container_name: forgejo-db
environment:
POSTGRES_USER: forgejo
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: forgejo
restart: unless-stopped
volumes:
- ./postgres:/var/lib/postgresql/data
# No ports: block. This is the point.
caddy:
image: caddy:2-alpine
container_name: forgejo-caddy
restart: unless-stopped
network_mode: host
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./caddy-data:/data
- ./caddy-config:/config
/srv/forgejo/Caddyfile
git.example.com {
encode zstd gzip
request_body {
max_size 512MB # raise it if you push large repositories
}
reverse_proxy 127.0.0.1:3000
}
Die beiden SSH-Zeilen sind die, bei denen sich die meisten vertun, daher lohnt es sich, sie zweimal zu lesen. SSH_LISTEN_PORT ist der Port innerhalb des Containers, an den sich Forgejos eingebauter SSH-Server bindet. SSH_PORT ist der Port, den Forgejo in den Clone-URLs ausgibt, die es Ihnen zeigt — er muss zur öffentlichen Seite des Port-Mappings passen. Bringen Sie die beiden aus dem Takt, und alles funktioniert, außer der einen Sache, die Nutzer kopieren und einfügen.
Fahren Sie es hoch, und legen Sie dann den Administrator über die Kommandozeile an. Verwenden Sie nicht den Web-Installer. Auf einer frischen Instanz ist er für jeden erreichbar, der den Hostnamen findet, und das erste über ihn angelegte Konto wird zum Administrator — ein Wettlauf, an dem Sie nicht teilnehmen müssen. INSTALL_LOCK in der obigen Datei schließt ihn, bevor der Container überhaupt startet.
docker compose up -d
docker compose logs -f forgejo # wait for "Starting new Web server"
docker compose exec -u git forgejo forgejo admin user create \
--admin --username you --email you@example.com --random-password
Öffnen Sie den Hostnamen in einem Browser, melden Sie sich mit dem ausgegebenen Passwort an und ändern Sie es. Gehen Sie dann die Einstellungen durch, die aus einer funktionierenden eine geschlossene Instanz machen — die meisten sind bereits durch den obigen Environment-Block gesetzt, aber es lohnt sich, jede davon zu bestätigen, statt es anzunehmen:
DEFAULT_PRIVATE = private. Neue Repositories starten geschlossen. Veröffentlichen ist dann ein bewusster Akt statt die Folge einer Voreinstellung.
REQUIRE_SIGNIN_VIEW = true verbirgt selbst öffentliche Repositories vor abgemeldeten Besuchern. Richtig für eine private Team-Forge, falsch, wenn Sie irgendetwas veröffentlichen wollen.
Aktivieren Sie es beim Administratorkonto, bevor Sie irgendetwas anderes tun. Die Forge ist jetzt die Maschine, die jeden Branch, den Sie haben, umschreiben kann.
Ein Pfad, den man sich merken sollte: Die aktive Konfiguration liegt auf dem Host unter ./data/gitea/conf/app.ini. Forgejo hat den Verzeichnisnamen gitea aus Kompatibilitätsgründen mit bestehenden Installationen beibehalten, was jeden genau einmal überrascht. Umgebungsvariablen der Form FORGEJO__section__KEY werden beim Start in diese Datei geschrieben, sodass Sie die gesamte Konfiguration über die Compose-Datei verwalten können — bearbeiten Sie app.ini jedoch von Hand, überschreibt eine Umgebungsvariable für denselben Schlüssel Ihre Änderung beim nächsten Neustart. Entscheiden Sie sich für eines der beiden und bleiben Sie dabei konsistent.
Über SSH pushen. Dann signieren, damit die Historie nicht vom Server abhängt.
Fügen Sie Ihren öffentlichen Schlüssel in die Profileinstellungen ein, und bringen Sie Ihrem Client dann bei, wo die Forge liegt. Weil Git auf einem nicht standardmäßigen Port läuft, lohnt sich ein Eintrag in Ihrer SSH-Konfiguration die dreißig Sekunden — er hält den Port aus jeder URL heraus, die Sie je eintippen werden, und macht die Remotes portabel.
# ~/.ssh/config
Host git.example.com
Port 2222
User git
IdentityFile ~/.ssh/id_ed25519
# Then, in any repository:
git remote set-url origin git@git.example.com:you/project.git
git push -u origin main
ssh -T git@git.example.com # expect a greeting, not a shell
Dieser letzte Befehl ist derjenige, der Ihnen verrät, ob das Port-Mapping, der SSH-Server im Container und Ihr Schlüssel zusammenpassen. Eine Begrüßung von Forgejo bedeutet, dass die ganze Kette funktioniert. Eine Passwortabfrage bedeutet, dass Sie stattdessen den administrativen sshd des Hosts erreicht haben, und dass das Port-Mapping falsch ist.
Signieren Sie jetzt Ihre Commits. Das ist der Schritt, den die meisten Self-Hosting-Leitfäden auslassen, und auf einer selbstgehosteten Forge zählt er mehr als auf einer Plattform. Das Autor-Feld eines Commits ist Freitext — jeder, der pushen kann, kann Ihren Namen hineinschreiben. Eine Signatur ist der Beweis, dass ein bestimmter Schlüssel einen bestimmten Commit erzeugt hat, und sie bleibt Beweis, auch nachdem der Server, zu dem gepusht wurde, kompromittiert, migriert oder weggeworfen wurde. Git kann mit dem SSH-Schlüssel signieren, den Sie bereits haben, sodass es keinen Umweg über PGP mehr braucht:
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
git config --global tag.gpgsign true
git commit --allow-empty -m "signed"
git log --show-signature -1
Fügen Sie dann denselben öffentlichen Schlüssel ein zweites Mal in der Forge hinzu, als Signaturschlüssel statt als Authentifizierungsschlüssel. Forgejo verifiziert die Signatur serverseitig und zeigt den Commit als verifiziert an; ohne diesen zweiten Eintrag hat es keine Möglichkeit, die Signatur mit Ihrem Konto zu verknüpfen, und jeder Commit erscheint als nicht verifiziert, obwohl er korrekt signiert ist.
Deploy-Keys, für die Maschinen, die nur lesen. Ein Produktionsserver, eine Build-Maschine oder ein Mirror braucht kein Benutzerkonto auf der Forge. Geben Sie ihm stattdessen einen Deploy-Key: ein Schlüsselpaar, das zu einem Repository hinzugefügt wird, schreibgeschützt, sofern Sie nicht ausdrücklich Schreibrechte gewähren, und das keinen Zugriff auf sonst irgendetwas auf der Instanz gewährt. Ihn zu widerrufen bedeutet, einen einzigen Eintrag zu löschen. Das ist die mit Abstand wirksamste Gewohnheit, um eine selbstgehostete Forge aufgeräumt zu halten, denn Benutzerkonten sammeln Berechtigungen an, Deploy-Keys können das nicht.
Die Migration von einer Plattform ist ein guter Moment, das Ganze richtig zu machen. Forgejos New-Migration-Ablauf nimmt eine Repository-URL und ein Access Token entgegen und überträgt Branches, Tags, Issues, Kommentare, Labels, Meilensteine, Releases und Pull Requests. Lassen Sie zuerst ein Repository laufen, um die Form des Ergebnisses zu sehen, rechnen Sie damit, dass API-Rate-Limits große Stapel verlangsamen, und lassen Sie die Quelle einige Wochen lang schreibgeschützt, statt sie noch am selben Tag zu löschen.
Ein CI-Runner ist eine Maschine, die den Code von Fremden ausführt. Platzieren Sie ihn entsprechend.
Forgejo Actions liest Workflow-Dateien im selben Format wie GitHub Actions, aus .forgejo/workflows/, und übergibt sie an ein separates Programm namens Forgejo Runner. Der Runner ist nicht Teil der Forge und muss nicht auf derselben Maschine leben — das ist die ganze Konstruktionsidee, und der Grund, warum dieser Schritt mit einer Warnung statt einem Achselzucken daherkommt.
Aktivieren Sie Actions auf der Instanz, und holen Sie sich dann ein Registrierungstoken aus dem Administrationspanel — instanzweit, oder beschränkt auf eine Organisation oder ein Repository, was die bessere Voreinstellung ist, wenn Sie sich nicht sicher sind. Der Runner verbindet sich nach außen zur Forge, braucht also überhaupt keine eingehenden Ports und kann hinter jeder beliebigen Firewall sitzen, auch auf einer Maschine ohne öffentliche Adresse.
# In the Compose environment of the forge, or app.ini:
# FORGEJO__actions__ENABLED: "true"
# Then: Site administration -> Actions -> Runners -> Create new runner
# On the runner machine:
forgejo-runner generate-config > config.yml
forgejo-runner register --no-interactive \
--instance https://git.example.com \
--token <REGISTRATION_TOKEN> \
--name runner-01 \
--labels docker:docker://node:22-bookworm,debian:docker://debian:12
forgejo-runner --config config.yml daemon
Die Labels sind der Vertrag zwischen dem Workflow und dem Runner: Ein Job, der runs-on: docker verlangt, bekommt einen Container, der aus dem Image auf der rechten Seite gebaut wird. Deklarieren Sie nur die Labels, die Sie tatsächlich bedienen können, denn ein Job, der ein Label verlangt, das niemand anbietet, wartet ewig statt fehlzuschlagen — verwirrend zu debuggen am Ende eines langen Tages.
.forgejo/workflows/ci.yml
on: [push, pull_request]
jobs:
test:
runs-on: docker
container:
image: node:22-bookworm
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test
Ein Runner führt aus, was auch immer die Workflow-Datei sagt, und die Workflow-Datei kommt in einem Pull Request an. Wenn jemand außerhalb Ihres Teams einen solchen öffnen kann, gehört der Runner auf eine andere Maschine als die Forge — ein zweiter Sentinel für $3.90 im Monat, ohne Zugriff auf das Datenverzeichnis der Forge, ohne Datenbank-Anmeldedaten und ohne irgendetwas in seiner Umgebung, das Sie in einem Build-Log ungern lesen würden. Verlangen Sie auf der Forge selbst eine Freigabe, bevor Workflows für Erstbeitragende laufen. Die beiden Standardfehler sind beide banal und beide teuer: eine in einem Pull Request vergrabene Mining-Last, und ein Secret, das von einem Schritt ausgegeben wird, den jemand absichtlich hinzugefügt hat.
Beschränken Sie die Secrets auf dieselbe Weise. Forgejo speichert sie auf Repository-, Organisations- und Instanzebene; ein Deployment-Schlüssel, den nur ein Repository braucht, sollte auch nur bei diesem Repository existieren, damit ein kompromittierter Workflow in einem fremden Projekt ihn nicht lesen kann. Bevorzugen Sie kurzlebige Tokens gegenüber langlebigen, wo immer das Ziel das unterstützt, und legen Sie niemals ein Secret in eine Workflow-Datei — die Workflow-Datei liegt im Repository, und das Repository hat eine Historie.
Wenn Ihre Builds bei jedem Lauf dieselben Basis-Images ziehen, cachen Sie sie. Ein lokaler Registry-Mirror oder ein persistenter Runner-Cache macht aus einem zweiminütigen Image-Pull einen zweisekündigen, und es ist die höfliche Sache gegenüber den vorgelagerten Registries, die Sie da bombardieren. Beachten Sie außerdem das Muster, zu dem die ganze Konstruktion einlädt, und das ist der Grund, warum dieser Leitfaden neben dem Leitfaden zur Agenten-Laufzeitumgebung steht: Der Runner ist ein Allzweck-Worker, der sich Jobs von Ihrer Forge holt, also gehören dort auch geplante Wartung, Datenpipelines und Agenten-Workloads hin.
Jeder Klon ist ein Backup des Codes. Nichts ist ein Backup der Konversation.
Git ist hier ungewöhnlich nachsichtig, und es lohnt sich, genau zu sein, warum. Jeder Klon ist eine vollständige Kopie der Historie, solange also ein Entwickler eine aktuelle Arbeitskopie hat, überlebt der Code den Server. Das ist wirklich beruhigend, und es deckt genau eines der beiden Dinge ab, die Ihre Forge hält. Das andere — Issues, Pull-Request-Diskussionen, Review-Threads, Releases, Wiki-Seiten, Nutzer, Teams, Berechtigungen, CI-Historie — existiert nur in der Datenbank, niemand hat davon einen Klon, und es ist meist der Teil, der drei Jahre gebraucht hat, um sich anzusammeln.
Zuerst ein Push-Mirror. Er ist kostenlos und läuft von selbst. In einem Repository: Einstellungen → Mirror-Einstellungen → einen Push-Mirror hinzufügen mit der URL und einem Access Token für das Ziel. Forgejo repliziert dann nach jedem Push jeden Branch und Tag — zu einer zweiten Forgejo, zu Codeberg, oder zu einer Plattform, die Sie als Schaufenster statt als Zuhause behalten wollen. Ein Vorbehalt, den man klar aussprechen sollte: Ein Push-Mirror veröffentlicht die Historie originalgetreu erneut, einschließlich eines Secrets, das Sie eigentlich entfernen wollten.
Dann ein echtes Backup, das nicht das eingebaute Archiv ist. Forgejo liefert einen forgejo dump-Befehl, der Konfiguration, Repositories, Daten und einen Datenbank-Dump in eine einzige Zip-Datei packt. Das ist wirklich nützlich, um es einem Maintainer beim Melden eines Bugs in die Hand zu drücken. Es ist nicht das, worauf Sie sich verlassen sollten, um von den Toten zurückzukehren: Das erzeugte SQL hat seit Langem bekannte Probleme beim Re-Import, und das Archiv schreibt bei jedem Lauf den gesamten Repository-Bestand neu, was auf einer echten Instanz eine große, jede Nacht von Grund auf neu geschriebene Datei bedeutet. Verwenden Sie stattdessen den langweiligen, verifizierbaren Weg — die Datenbank mit dem eigenen Werkzeug der Datenbank dumpen, und das Datenverzeichnis als Dateien sichern:
#!/bin/sh
# /usr/local/bin/forgejo-backup — run from a systemd timer, nightly
set -eu
cd /srv/forgejo
# 1. Consistent database dump, written next to the data.
docker compose exec -T db pg_dump -U forgejo -Fc forgejo > ./db.dump
# 2. Everything that matters, to an encrypted offsite repository.
restic backup ./data ./db.dump ./docker-compose.yml ./.env ./Caddyfile
# 3. Retention, then prove the repository is still readable.
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune
restic check
Schicken Sie es an eine Maschine in einem anderen Rechtsraum als die Forge, über ein Append-only-Repository, damit eine Kompromittierung der Forge nicht ihre eigene Historie löschen kann — der Leitfaden zu verschlüsselten Backups ist die lange Version der drei Zeilen oben, samt systemd-Timer und der Append-only-Schlüsselbeschränkung.
Stellen Sie es dann einmal absichtlich wieder her. Bestellen Sie eine Test-VPS, spielen Sie den Snapshot darauf ein, fahren Sie dieselbe Compose-Datei gegen das wiederhergestellte Datenverzeichnis hoch, laden Sie den Datenbank-Dump, richten Sie einen Hosts-Datei-Eintrag darauf aus und melden Sie sich an. Stoppen Sie die Zeit für das Ganze. Diese Zahl — nicht die bloße Existenz des Backup-Jobs — ist Ihre tatsächliche Wiederherstellungsposition, und jeder, der diese Übung gemacht hat, hat beim ersten Mal mindestens eine Sache gefunden, die in der Include-Liste fehlte.
# restic keeps absolute paths, so the stack reappears under the target.
restic restore latest --target /srv/restore
cd /srv/restore/srv/forgejo
docker compose up -d db # empty volume: the entrypoint creates the database
docker compose exec -T db pg_restore -U forgejo -d forgejo \
--clean --if-exists --no-owner < db.dump
docker compose up -d # then sign in and walk the issue tracker
Upgrades, kurz. Binden Sie das Image an eine Hauptversion statt an :latest, damit ein unbeaufsichtigter Pull Sie nicht im Schlaf über eine Breaking Change hinwegträgt. Lesen Sie die Release Notes, bevor Sie eine Hauptversionsgrenze überschreiten, machen Sie zuerst einen Snapshot, und denken Sie daran, dass eine Datenbankmigration beim ersten Start der neuen Version läuft — was eine Einbahnstraße ist. Automatisches Aktualisieren im Watchtower-Stil ist eine gute Idee für eine statische Seite und eine schlechte für die Maschine, die jeden Branch hält, den Sie haben.
Vier Prüfungen, bevor Sie es für erledigt erklären. Melden Sie sich ab und bestätigen Sie, dass die Instanz keine Möglichkeit bietet, ein Konto anzulegen. Bestätigen Sie aus einem Browser ohne Sitzung, dass ein privates Repository unsichtbar ist, statt nur verboten. Bestätigen Sie, dass der Runner das Datenverzeichnis der Forge nicht lesen kann. Und bestätigen Sie, dass der letzte restic check erfolgreich war — den Backup-Job, der lautlos fehlschlägt, hat jeder, genau bis zu dem Tag, an dem er ihn braucht.
Sie haben eine Partei entfernt. Sie haben nicht das Gebäude entfernt.
Eine Forge selbst zu hosten ist eine echte Veränderung dessen, wer Ihre Arbeit hält, und es ist keine Unsichtbarkeit. Vier Dinge bleiben wahr, nachdem alles oben Genannte erledigt ist, und sie zu kennen ist der Unterschied zwischen einer Verteidigung und einer Geschichte, die Sie sich selbst erzählen.
Die Festplatte steht in jemandes Gebäude. Ein gemieteter Server läuft auf Hardware in einem Rechenzentrum mit Türen und Personal. NordBastion inspiziert keine Kunden-Workloads, und der Transparenzbericht und der Warrant Canary beschreiben, was passieren würde, wenn man uns fragte — aber die architektonische Aussage ist die ehrliche: Was sich ändert, ist, dass es keinen produktweiten Index Ihrer Repositories gibt, keine Analyse-Pipeline über Ihre Commits und keine Datenschutzrichtlinie, die geändert werden kann. Was sich nicht ändert, ist, dass die Maschine physisch ist und nicht in Ihrem Haus steht.
Der Hostname wird öffentlich, sobald das Zertifikat ausgestellt wird. Jedes öffentlich vertrauenswürdige Zertifikat wird in Certificate-Transparency-Logs veröffentlicht, die jeder durchsuchen kann, sodass git.yourproject.com auffindbar ist, selbst wenn Sie nie darauf verlinken. Das ist kein Leck Ihres Codes, aber es ist ein Leck der Tatsache, dass das Projekt existiert und ungefähr, wann es begonnen hat. Verwenden Sie eine Subdomain eines Namens, den Sie bereits veröffentlichen, oder lassen Sie das öffentliche Zertifikat ganz weg und erreichen Sie die Forge über WireGuard oder einen Onion Service.
Sie patchen jetzt eine Webanwendung. Die Forge ist eine Login-Seite, eine Rendering-Pipeline für nicht vertrauenswürdiges Markup, ein SSH-Server und eine CI-Engine, alle vom Internet aus erreichbar. Das ist eine erheblich größere Angriffsfläche als das Bare-Repository aus Schritt 01, und es ist eine, die Sie aktuell halten müssen. Abonnieren Sie die Release-Ankündigungen, und behandeln Sie ein Sicherheits-Release als Aufgabe für dieselbe Woche, nicht für dasselbe Quartal.
Föderation ist noch nicht da. Forgejo leistet echte Arbeit an der Föderation zwischen Forges und veröffentlicht Fortschritte dazu, aber Stand heute können Sie von Ihrer eigenen Instanz aus keinen Pull Request auf der Instanz von jemand anderem öffnen, so wie Sie es auf einer gemeinsamen Plattform könnten. Praktisch bedeutet das: Ein externer Beitragender braucht weiterhin ein Konto auf Ihrer Forge, oder Sie nehmen Patches per E-Mail an, oder Sie behalten einen öffentlichen Mirror für Gelegenheitsbeiträge. Planen Sie für das der drei Szenarien, das zu Ihrem Projekt passt, statt davon überrascht zu werden.
Was Sie tatsächlich erkauft haben, genau genommen: Die datierte Aufzeichnung dessen, was Sie bauen, mit wem, und wann, wird nicht mehr von einem Unternehmen gehalten, zu dessen Geschäftsmodell es gehört, sie zu analysieren. Das ist eine enge Behauptung, und es ist eine echte. Sie ist ihren Preis nur wert, wenn Schritt 06 erledigt ist — eine selbstgehostete Forge ohne getestete Wiederherstellung ist keine Verbesserung der Privatsphäre, sie ist ein Single Point of Failure mit besserem Branding.
Die Forge ist klein. Die CI ist es nicht.
| Stufe | Monatlich | Ressourcen | Was sie komfortabel stemmt |
|---|---|---|---|
| Sentinel | $3.90 | 2 vCPU · 4 GB · 120 GB | Forgejo + PostgreSQL für eine Person oder ein kleines Team. Der richtige Anfang. |
| Ein zweiter Sentinel | $3.90 | Der Runner, auf eigener Maschine | Die Isolation, die wirklich zählt, für den Preis eines Kaffees |
| Garrison | $7.90 | 4 vCPU · 8 GB · 240 GB | Eine Forge für zehn Personen, eine Container-Registry und Artefakte |
| Ravelin | $16.90 | 8 vCPU · 16 GB · 480 GB | Vielbeschäftigte CI, große Repositories, mehrere parallele Jobs |
Nichts in dieser Tabelle ist auf Git dimensioniert. Forgejo und PostgreSQL zusammen laufen im Leerlauf mit ein paar Hundert Megabyte; was eine Maschine füllt, ist CI — Container-Layer, Build-Caches, Artefakte — und Repository-Inhalte, die kein Quellcode sind. Dimensionieren Sie zuerst nach der Platte, dann nach den Kernen, und zuletzt nach dem Speicher. Zwei kleine Maschinen schlagen hier eine große, weil die zweite eine Sicherheitsgrenze ist und keine Kapazität.
Sechs Arten, wie es schiefgeht. Zwei davon kosten Geld.
Offene Registrierung, und eine Forge voller Fremder
Das Zertifikat erscheint in den Transparency-Logs, die Scanner folgen innerhalb von Tagen, und eine Instanz mit aktivierten Registrierungen füllt sich mit Spam-Konten und Spam-Repositories. Setzen Sie DISABLE_REGISTRATION vor dem ersten Boot, nicht nach dem ersten Vorfall.
Der Runner, der für jemand anderen Kryptowährung schürft
CI, die Workflows aus jedem Pull Request ausführt, ist kostenlose Rechenleistung für jeden, der sie findet. Verlangen Sie eine Freigabe für Erstbeitragende, halten Sie den Runner auf einer eigenen Maschine, und schauen Sie sich gelegentlich die Job-Laufzeiten an.
ROOT_URL falsch, und alles funktioniert nur halb
Clone-URLs zeigen auf den falschen Host, Webhooks feuern an die falsche Adresse, OAuth-Callbacks schlagen fehl und Avatare brechen. Die Oberfläche sieht in Ordnung aus, also verdächtigt niemand eine einzelne Zeile. Setzen Sie DOMAIN, ROOT_URL, SSH_DOMAIN und SSH_PORT gemeinsam, und prüfen Sie eine Clone-URL in der Oberfläche.
Zwei SSH-Server, ein Port
Der Container will Port 22, und der Host benutzt ihn bereits, sodass das Mapping stillschweigend irgendwo Unerwartetem landet und git push nach einem Passwort fragt. Verlegen Sie zuerst das administrative SSH, veröffentlichen Sie den Container auf einem eigenen Port, und überprüfen Sie es mit ssh -T.
Ein Dump, der nie wiederhergestellt wurde
Das eingebaute Archiv ist ein Support-Artefakt, kein getesteter Wiederherstellungsweg, und sein SQL hat bekannte Probleme beim Re-Import. Dumpen Sie die Datenbank mit pg_dump, sichern Sie das Datenverzeichnis als Dateien, und stellen Sie das Ganze einmal auf einer Test-Maschine wieder her.
Die Platte füllt sich mit Build-Layern, nicht mit Code
Quellcode ist winzig; Container-Layer, Artefakte und CI-Caches sind es nicht. Eine 120-GB-Maschine mit einer eifrigen Pipeline ist innerhalb von Wochen voll. Räumen Sie Images per Timer auf, begrenzen Sie die Aufbewahrung von Artefakten, und behalten Sie die Platte im Auge statt die CPU.
Fragen, beantwortet.
Elf Fragen, die beim Aufbau davon aufkommen — angefangen mit den beiden, die entscheiden, ob Sie es überhaupt aufbauen sollten.
Forgejo oder Gitea — was sollte ich tatsächlich installieren?
Für die meisten Forgejo, und der Grund ist Governance, nicht Funktionsumfang. Die beiden Projekte teilen einen gemeinsamen Vorfahren und fühlen sich in der Nutzung noch immer fast identisch an: dieselbe Go-Binary, derselbe schlanke Ressourcenbedarf, dieselbe Weboberfläche, vergleichbare CI. Der Unterschied liegt darin, wer sie steuert. Forgejo wird von der Codeberg e.V. gehalten, einem gemeinnützigen eingetragenen Verein in Berlin, und wurde Anfang 2024 zu einem Hard Fork; es veröffentlicht alles als freie Software und verlangt von Mitwirkenden keine Abtretung des Copyrights. Gitea wird von einem gewinnorientierten Unternehmen entwickelt, das die Marke und die Domain besitzt, von Mitwirkenden die Abtretung des Copyrights verlangt und ein Open-Core-Modell mit kostenpflichtigen Funktionen um die freie Basis herum betreibt. Keine dieser Tatsachen macht Gitea als Software schlechter. Aber der ganze Sinn davon, Ihre Repositories von einer Plattform wegzuziehen, besteht darin, nicht mehr von der Roadmap eines Unternehmens abhängig zu sein — und deshalb passt die Wahl des Forks, dessen Hüter gemeinnützig ist, zu dem Grund, aus dem Sie hier sind. Ein praktischer Hinweis für einen späteren Wechsel: Forgejo lässt sich über ein bestehendes Gitea-Datenverzeichnis installieren, der umgekehrte Weg wird nicht unterstützt.
Brauche ich überhaupt eine Forge? Reicht ein Bare-Repository über SSH nicht aus?
Für eine einzelne Person mit einem Laptop und der Gewohnheit, gute Commit-Messages zu schreiben, ist ein Bare-Repository wirklich genug, und es ist die robusteste Lösung in diesem Leitfaden: git init --bare auf dem Server, ein normaler SSH-Account, und es gibt keine Webanwendung zu patchen, keine Datenbank zu sichern und keine Angriffsfläche jenseits von sshd. Dieser Aufbau wird niemals end-of-life gehen. Eine Forge brauchen Sie in dem Moment, in dem eine zweite Person beteiligt ist, oder in dem Sie eines der vier Dinge wollen, von denen ein Bare-Repository nichts weiß — eine im Browser einsehbare Historie, Pull Requests mit Review, einen Issue-Tracker direkt neben dem Code, und Zugriffskontrolle pro Repository. Das ist kein kleiner Schritt nach oben, sondern der Unterschied zwischen Speicherung und Zusammenarbeit. Seien Sie ehrlich, welches der beiden Probleme Sie eigentlich lösen wollen, denn eine Forge ist ein Dienst, den Sie jetzt betreiben, und ein Bare-Repository ist ein Verzeichnis.
Kann ich meine GitHub-Repositories migrieren, einschließlich Issues und Pull Requests?
Ja, und das ist eine der besseren Migrationsgeschichten im Self-Hosting. Forgejos eingebautes Migrationswerkzeug nimmt eine Repository-URL und ein persönliches Access Token entgegen und holt weit mehr als nur den Code: Branches und Tags, Issues samt ihren Kommentaren, Pull Requests, Labels, Meilensteine und Releases. Geben Sie ihm ein Token mit Lesezugriff, lassen Sie zuerst ein Repository laufen, um zu sehen, wie das Ergebnis aussieht, und arbeiten Sie sich dann durch den Rest. Drei Dinge sind zu erwarten. Große oder zahlreiche Migrationen stoßen an die API-Rate-Limits der Ausgangsplattform und dauern schlicht länger — starten Sie sie und lassen Sie sie laufen. Anhänge an Issues werden über das Netzwerk geladen, sodass eine Instanz hinter einer restriktiven Egress-Firewall Issues mit kaputten Bildern erzeugt. Und Review-Threads landen nicht immer exakt dort, wo sie waren, weil die beiden Datenmodelle nicht identisch sind. Planen Sie, die Ausgangs-Repositories einige Wochen lang schreibgeschützt zu lassen, statt sie noch am selben Nachmittag zu löschen.
Ist eine selbstgehostete Forge wirklich privater, oder nur anders?
Sie ist auf eine bestimmte, wichtige Weise wirklich anders: Niemand außer Ihnen hält den Index. Auf einer Plattform ist das private Repository privat gegenüber anderen Nutzern — nicht privat gegenüber der Plattform selbst, die es speichert, scannt, daraus einen Abhängigkeitsgraphen berechnet und von einem Gericht in ihrem eigenen Rechtsraum zur Herausgabe gezwungen werden kann. Dieser Index ist ungewöhnlich aufschlussreich, selbst wenn der Code langweilig ist: Commit-Zeitstempel verraten Ihre Arbeitszeiten, Co-Autoren verraten Ihr Team, Branch-Namen verraten unveröffentlichte Produkte, und der erste Commit datiert ein Projekt, das noch nicht angekündigt wurde. Der Umzug auf den eigenen Server entfernt die Plattform vollständig aus diesem Bild. Was er nicht tut: die Maschine verschwinden lassen. Sie mieten weiterhin Hardware in einem Rechenzentrum, die Festplatte bleibt physisch erreichbar für die Menschen, denen das Gebäude gehört, und der Hostname, den Sie gewählt haben, steht weiterhin in Certificate-Transparency-Logs. Die ehrliche Zusammenfassung: Sie haben ein Unternehmen mit einer vertraglichen Datenschutzrichtlinie durch einen Server ersetzt, dessen einzige Richtlinie Ihre eigene Konfiguration ist, in einem Rechtsraum, den Sie bewusst gewählt haben — und das Konto, das dafür bezahlt, muss nicht Ihren Namen tragen.
Wie groß muss der VPS sein?
Kleiner, als die GitLab-Dokumentation alle Welt erwarten lässt. Forgejo und Gitea sind einzelne Go-Binaries, die im Leerlauf ein paar Hundert Megabyte RAM verbrauchen; fügen Sie PostgreSQL und einen Reverse Proxy hinzu, und die Forge eines Einzelentwicklers sitzt bequem in einem Sentinel — 2 vCPU, 4 GB, 120 GB NVMe, $3.90 im Monat — mit Raum übrig. Der Druck kommt von drei anderen Stellen, und jede davon lohnt eine bewusste Dimensionierung. CI ist die große: Ein Runner, der Container baut, nutzt jeden Kern, den Sie ihm geben, und füllt die Platte mit Layern. Die Repository-Größe ist die zweite: Game-Assets, Notebooks und gebündelte Abhängigkeiten wachsen weit schneller als Quellcode. Artefakte und eine Container-Registry sind die dritte. Eine Regel, die sich in der Praxis bewährt hat — die Forge auf einen Sentinel, den Runner auf einen eigenen zweiten Sentinel, und einen Tarif erst höherstufen, wenn die Platte es verlangt, nicht die CPU.
Ist Forgejo Actions wirklich kompatibel mit meinen GitHub-Actions-Workflows?
Kompatibel genug, dass die meisten einfachen Workflows unverändert laufen, und nicht so kompatibel, dass Sie eine komplexe Pipeline migrieren sollten, ohne sie zu lesen. Das Workflow-Dateiformat ist dasselbe, es liegt in .forgejo/workflows/ (auch .github/workflows/ wird gelesen), und die gängigen Bausteine — Checkout, Sprach-Setup, Cache, Matrix-Builds — verhalten sich wie erwartet, weil der Runner sie auf dieselbe Weise in Containern ausführt. Was bricht, ist alles, was GitHub als Plattform voraussetzt statt Actions als Format: Marketplace-Actions, die an github.com-URLs gebunden sind, die Ihre Instanz erreichen dürfen muss, Actions, die die GitHub-REST-API für Release- oder PR-Metadaten aufrufen, von GitHub gehostete Runner-Images mit einer sehr spezifischen vorinstallierten Toolchain, und OIDC-basierte Cloud-Authentifizierung. Das Migrationsmuster, das funktioniert: mit einem Workflow beginnen, der nur baut und testet, den grün bekommen, und dann die Deployment-Schritte einzeln wieder hinzufügen.
Was passiert mit meinem Code, wenn der VPS ausfällt?
Der Code selbst ist der eine Teil, der in Ordnung ist, und das ist der stille Vorteil von Git gegenüber fast jedem anderen selbstgehosteten Dienst: Jeder Klon ist eine vollständige Kopie der Historie, solange also ein Entwickler eine aktuelle Arbeitskopie hat, ist das Repository nicht verloren. Was mit dem Server stirbt, ist alles rund um den Code — Issues, Pull-Request-Diskussionen, Review-Kommentare, Releases, Wiki, Nutzer, Zugriffsregeln, CI-Historie. Dieses Material lebt nur in der Datenbank, niemand besitzt einen Klon davon, und es ist meist der Teil, der Jahre gebraucht hat, um sich anzusammeln. Behandeln Sie die beiden also unterschiedlich. Für den Code richten Sie einen Push-Mirror an einen zweiten Ort ein, und Sie haben kontinuierlichen, automatischen Schutz außer Haus, ohne jeden Aufwand. Für die Metadaten brauchen Sie ein echtes Backup: einen PostgreSQL-Dump plus das Datenverzeichnis, verschlüsselt und per Timer an eine Maschine in einem anderen Rechtsraum geschickt. Schritt 06 deckt beides ab, und die Wiederherstellungsübung am Ende ist der Teil, der daraus eine Garantie macht statt nur eine Absicht.
Kann ich GitHub als Spiegel behalten, statt es ganz zu verlassen?
Ja, und für viele Projekte ist das die richtige Antwort und kein Kompromiss. Forgejo unterstützt Push-Mirrors, sodass Ihre Instanz der Origin ist, gegen den Ihr Team arbeitet, und jeder Push automatisch auf ein Repository bei GitHub, Codeberg oder einer zweiten Forgejo repliziert wird. Öffentliche Sichtbarkeit, Gelegenheitsbeiträge, Paket-Registries, die eine GitHub-URL erwarten, und der Issue-Tracker-Link in einem alten README funktionieren alle weiter, während die Quelle der Wahrheit — sowie die privaten Repositories, die CI-Logs und die Issue-Historie von allem, was Sie nicht veröffentlichen — auf Ihrer Maschine bleibt. Es funktioniert auch in die andere Richtung: Pull-Mirrors halten eine lokale, stets aktuelle Kopie von Upstream-Projekten, von denen Sie abhängen, was sich für alles lohnt, ohne das Sie aufgeschmissen wären, wenn es morgen gelöscht würde. Das eine, worauf Sie bewusst achten sollten, ist, was Sie spiegeln: Ein Push-Mirror eines Repositorys, dessen Historie ein Secret enthält, das Sie eigentlich entfernen wollten, veröffentlicht dieses Secret sofort und automatisch erneut.
Wie gebe ich einem Auftragnehmer Zugriff auf ein Repository, ohne ihm auch die übrigen zu geben?
Verwenden Sie die Ebene, die zur vorgesehenen Dauer des Zugriffs passt. Für eine Person, die lesen, committen und Pull Requests öffnen wird, legen Sie ein Benutzerkonto an und fügen sie als Mitarbeiter genau zu den Repositories hinzu, die sie braucht, mit einer expliziten Berechtigungsstufe — Lesen, Schreiben oder Admin — statt sie in ein Organisationsteam zu stecken, das im Stillen mehr gewährt. Für ein automatisiertes System, das nur abrufen muss — ein Deployment-Ziel, eine Build-Maschine, ein Mirror — verwenden Sie statt eines Benutzerkontos einen schreibgeschützten Deploy-Key: Er ist auf ein Repository beschränkt, gewährt sonst nichts, und ihn zu widerrufen bedeutet, eine einzige Zeile zu löschen. Für Skripte, die mit der API sprechen, stellen Sie ein beschränktes Access Token aus, statt ein Passwort weiterzugeben. Zwei Gewohnheiten halten das Ganze zusammen: Halten Sie die Registrierung geschlossen, sodass die einzigen existierenden Konten die sind, die Sie selbst angelegt haben, und tragen Sie sich ein Datum in den Kalender ein, wenn Sie temporären Zugriff gewähren, denn nichts in irgendeiner Forge wird Sie je daran erinnern, dass ein Vertrag geendet hat.
Braucht die Forge einen Domainnamen, und verrät das meine Identität?
Sie braucht einen Hostnamen für die Weboberfläche, denn TLS-Zertifikate werden für Namen ausgestellt, und kein Browser sollte hier einer IP-Adresse vertrauen. Dieser Name muss nicht zu Ihnen zurückverfolgbar sein. Drei Dinge, bei denen Sie bewusst vorgehen sollten. Registrieren Sie die Domain dort, wo man nicht die Identität verlangt, auf die Sie andernorts geachtet haben, und lassen Sie den WHOIS-Datenschutz aktiviert — der Registrar ist eine vom Host getrennte Vertrauensentscheidung, und es ist die, die man vergisst. Rechnen Sie damit, dass der Hostname in dem Moment öffentlich wird, in dem das Zertifikat ausgestellt wird, denn jedes öffentlich vertrauenswürdige Zertifikat wird in Certificate-Transparency-Logs veröffentlicht, die jeder durchsuchen kann; ein Wildcard-Zertifikat auf einer Domain, die Sie bereits verwenden, vermeidet einen zusätzlichen durchsuchbaren Namen. Und wenn Sie lieber gar keinen öffentlichen Namen hätten, ist die Alternative real: die Forge auf einen Tor-Onion-Service legen oder nur über WireGuard erreichbar machen, das öffentliche Zertifikat ganz weglassen, und akzeptieren, dass damit auch die Bequemlichkeit verschwindet, von überall einen Browser zu öffnen.
Stört es NordBastion, wenn ich eine Git-Forge und CI-Runner auf einem VPS betreibe?
Überhaupt nicht — eine Forge und ihre Runner sind eine ganz gewöhnliche Last, und es ist eines der häufigeren Dinge, die unsere Kunden aufbauen. Die Nutzungsrichtlinie hat nichts dazu zu sagen, Quellcode zu hosten oder zu bauen. Zwei betriebliche Hinweise statt Richtlinien-Hinweise. Erstens: Ein CI-Runner, der Workflows von Fremden akzeptiert, ist ein kostenloser Rechendienst für jeden, der ihn findet: Der Standardmissbrauch ist Kryptowährungs-Mining in einem Pull Request, und er wird innerhalb weniger Tage gefunden, nachdem er offensteht. Halten Sie die Registrierung geschlossen, und verlangen Sie eine Freigabe, bevor Workflows für jeden laufen, der nicht bereits Mitarbeiter ist. Zweitens: Wenn Ihre Builds bei jedem Lauf große Container-Images ziehen, ist das normaler Traffic und im Preis enthalten — aber es lohnt sich trotzdem, sie lokal zu cachen, weil es Ihre Pipeline schneller macht und es die höfliche Sache gegenüber den vorgelagerten Registries ist, von denen Sie abhängen.
Eine eigene Forge, auf einem Konto, das nicht Ihr Name ist. KYC-frei, mit Krypto bezahlt.
Sentinel — 2 vCPU und 4 GB für $3.90 im Monat, in Helsinki, Stockholm, Oslo oder Reykjavík. Genug für Forgejo, PostgreSQL und den Reverse Proxy, mit Raum für die Repositories, die Sie schon lange umziehen wollten. Fügen Sie einen zweiten für den Runner hinzu. Keine E-Mail bei der Anmeldung, kein Ausweisdokument, und kein produktweiter Index von irgendetwas, das Sie pushen.
Zuletzt überprüft · 2026-10-12 · Quellen · Forgejo-Administratordokumentation (Installation, Actions, Kommandozeile), der Forgejo-Vergleich mit Gitea, die Governance-Seiten der Codeberg e.V., PostgreSQL- und Caddy-Dokumentation, git-config(1) · Rhythmus · jährlich
Anonymous VPS hosting in 2026 — the cluster.
This guide is one spoke of a larger series. The pillar walks the three privacy layers end to end — the sibling spokes below dive into the specifics.
Three independent layers — signup, payment, network — explained, legal context included, common mistakes flagged.
Bitwarden-compatible password vault under your own control.
Files, calendar, contacts, photos — owned, not rented.
Postfix, Dovecot, SPF/DKIM/DMARC — deliverability included.
A meta search engine that does not log you — because you own it.