Opengist mit Docker Compose: eigener Gist-Server mit Git über HTTP und SSH
Opengist ist ein selbst gehosteter Gist-Server, bei dem jeder Snippet ein Git-Repository ist. Die Anleitung zeigt Docker Compose mit 1.15.2, Admin und geschlossene Registrierung, Git über HTTP und SSH, Backup und Restore sowie typische Fehler.
Werbung Mit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Kurze Skripte und Konfigurationsschnipsel landen oft in Chatverläufen oder Textdateien. Opengist ist ein selbst gehosteter Gist-Server: Jeder Snippet ist ein Git-Repository, bearbeitbar im Browser und per git clone und git push über HTTP oder SSH.
Vorweg: OG_SSH_PORT ändert auch den Port des SSH-Servers im Container, nicht nur die Clone-URL. Ohne angepasstes Port-Mapping kommt nur Connection closed.
Nutzen und Grenzen von Opengist
Opengist (Go, AGPL-3.0) orientiert sich an GitHub Gist. Getestet wurde v1.15.2 vom 30.08.2026. Das Projekt hat im Kern einen Hauptentwickler.
Was Opengist gut kann:
- Snippets mit mehreren Dateien, Syntaxhervorhebung, Revisionen, Sichtbarkeit öffentlich, ungelistet oder privat
- Git über HTTP und eingebauten SSH-Server, auch zum Anlegen neuer Gists per
git push - Metadaten per Push-Option ändern, etwa
git push -o visibility=private - Volltextsuche über einen eingebauten Bleve-Index, optional Meilisearch
- Anmeldung lokal, per OAuth (GitHub, GitLab, Gitea), OpenID Connect oder LDAP, dazu TOTP und WebAuthn
- REST-API mit Personal Access Tokens und ein Admin-Panel mit Benutzer-, Gist- und Einladungsverwaltung
Grenzen: keine Pull Requests, Issues oder Organisationen. Die Standardkonfiguration ist offen: Nach dem ersten Konto bleibt die Registrierung für jeden erreichbar, bis Sie sie im Admin-Panel schließen.
Voraussetzungen, Ressourcen und Plattformen
- Linux-Server (amd64 oder arm64) mit Docker Engine und Docker Compose v2, 1 CPU-Kern, 512 MB freier RAM
- Freier HTTP-Port (intern 6157), optional freier SSH-Port
- DNS-Name und Reverse Proxy mit TLS für Zugriff außerhalb von localhost
- Ein unprivilegierter Host-Benutzer als Eigentümer des Datenverzeichnisses
Im Test: Image 142 MB, rund 25 MiB RAM nach dem Start, rund 103 MiB nach Git-Operationen. amd64 und arm64 verfügbar.
Standard ist SQLite im Datenverzeichnis, das vereinfacht das Backup. PostgreSQL und MySQL/MariaDB gehen laut Doku über OG_DB_URI.
Getestete Version und Image-Tags
GHCR-Tags tragen kein v: 1.15.2, 1.15, 1 und latest existieren, v1.15.2 nicht. Die Doku nutzt 1. Für planbaren Betrieb pflegen Sie den exakten Tag 1.15.2 in der .env.
docker manifest inspect ghcr.io/thomiceli/opengist:1.15.2 liefert ein Manifest, der Tag mit v dagegen manifest unknown.
Schritt 1: Vollständige compose.yaml und .env
Basis ist das offizielle Beispiel, ergänzt um Versionsvariable, Healthcheck, Bindung an 127.0.0.1 und abweichenden SSH-Port (Ablage etwa in /opt/opengist):
services:
opengist:
image: ghcr.io/thomiceli/opengist:${OPENGIST_VERSION:-1.15.2}
container_name: opengist
restart: unless-stopped
ports:
# Weboberfläche und Git über HTTP, nur lokal für den Reverse Proxy
- "127.0.0.1:6157:6157"
# Git über SSH: Host-Port und Container-Port müssen OG_SSH_PORT entsprechen
# Bewusst auf allen Schnittstellen (0.0.0.0), da SSH nicht über den Proxy läuft
- "2222:2222"
volumes:
- ./opengist-data:/opengist
environment:
UID: ${OPENGIST_UID:-1000}
GID: ${OPENGIST_GID:-1000}
OG_EXTERNAL_URL: ${OG_EXTERNAL_URL}
OG_SECRET_KEY: ${OG_SECRET_KEY}
OG_LOG_LEVEL: info
OG_SSH_PORT: 2222
OG_SSH_EXTERNAL_DOMAIN: ${OG_SSH_EXTERNAL_DOMAIN}
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:6157/healthcheck"]
interval: 30s
timeout: 5s
retries: 3
start_period: 20sDazu die .env im selben Verzeichnis. Die Werte sind Beispiele, der Schlüssel wird lokal erzeugt:
OPENGIST_VERSION=1.15.2
OPENGIST_UID=1000
OPENGIST_GID=1000
OG_EXTERNAL_URL=https://gist.example.com
OG_SSH_EXTERNAL_DOMAIN=gist.example.com
OG_SECRET_KEY=HIER_EIGENEN_SCHLUESSEL_EINTRAGEN# Zufälligen Schlüssel erzeugen und in die .env übernehmen
openssl rand -hex 32
# Datei nur für den Besitzer lesbar machen
chmod 600 .envdocker compose config -q gibt nichts aus, ls -l .env zeigt -rw-------.
Dateipfade und Parameter erklärt
| Parameter | Bedeutung |
|---|---|
./opengist-data:/opengist | Gesamter Zustand: opengist.db (SQLite), repos/ (ein Git-Repository pro Gist), ssh/ (Host-Key des SSH-Servers), opengist.index (Suchindex), sessions/, log/, custom/ |
UID, GID | Der Entrypoint startet als root, setzt die Rechte von /opengist rekursiv auf diese IDs und startet Opengist dann als Benutzer opengist |
OG_EXTERNAL_URL | Öffentliche Adresse, wird für Links und Clone-URLs verwendet |
OG_SECRET_KEY | Schlüssel für Sessions und MFA-Daten. Ohne Angabe erzeugt Opengist einen Zufallswert |
OG_SSH_PORT | Port des SSH-Servers im Container und in der Clone-URL, Standard 2222 |
OG_SSH_EXTERNAL_DOMAIN | Hostname in der SSH-Clone-URL, falls abweichend |
OG_HTTP_GIT_ENABLED, OG_SSH_GIT_ENABLED | Git über HTTP ein oder aus, SSH als builtin, host oder disabled |
Variablen mit Präfix OG_ überschreiben die Standardwerte der /config.yml, alle stehen im Configuration Cheat Sheet.
Schritt 2: Installation und Start
# Verzeichnis anlegen und Dateien ablegen
sudo mkdir -p /opt/opengist
cd /opt/opengist
# Container starten, das Image wird dabei geladen
docker compose up -d
# Status prüfen, nach etwa 20 Sekunden sollte "healthy" erscheinen
docker compose ps
# Startbanner und Konfigurationsquellen prüfen
docker compose logs --no-log-prefix | head -12Das Log meldete nach wenigen Sekunden die Migrationen, Bleve indexer initialized, Starting HTTP server on http://0.0.0.0:6157 und Starting SSH server. Das Datenverzeichnis gehörte danach UID 1000.
docker compose ps zeigt healthy, das Log enthält Opengist v1.15.2 und Starting HTTP server.
Schritt 3: Funktionsprüfung und Healthcheck
# Healthcheck mit Datenbankstatus
curl -s http://127.0.0.1:6157/healthcheck
# Nur Statuscode, geeignet für Uptime-Monitore
curl -sI http://127.0.0.1:6157/healthcheck | head -1Im Test kam {"database":"ok","opengist":"ok","time":"..."} mit HTTP 200, der wget-Healthcheck setzte den Container auf healthy.
Der Healthcheck liefert "database":"ok" mit HTTP 200.
Schritt 4: Erstkonfiguration: Admin-Konto und Registrierung schließen
Registrierung: /-/register, Login: /-/login. Das erste Konto wird Administrator (im Test is_admin = 1, zweiter Nutzer 0). /-/admin-panel lieferte für den Admin 200, für den zweiten Nutzer 404.
Die Registrierung bleibt offen, im Test entstand ohne Einladung ein zweites Konto. Schließen Sie sie sofort:
- Als Admin anmelden, oben rechts auf den Benutzernamen klicken und Admin wählen
- Den Reiter Configuration öffnen
- Den Schalter Disable signup aktivieren
- Optional Require login aktivieren (auch öffentliche Gists nur für Angemeldete)
Die Einstellungen liegen in der Datenbank, nicht in der Konfigurationsdatei. Im Test stand danach disable-signup = 1 in admin_settings, ein weiterer Versuch endete mit HTTP 403 (Signing up is disabled). Sie überstand Neuerstellen und Restore. Einladungslinks im Admin-Panel umgehen die Sperre gezielt.
CLI-Befehle für automatisierte Installationen laufen im Container als Benutzer opengist:
# Passwort eines Nutzers zurücksetzen
docker exec opengist sh -c 'cd /app/opengist && su opengist -c "OG_OPENGIST_HOME=/opengist ./opengist --config /config.yml admin reset-password bob NeuesPasswort"'
# Konto per CLI anlegen, mit --admin als Administrator
docker exec opengist sh -c 'cd /app/opengist && su opengist -c "OG_OPENGIST_HOME=/opengist ./opengist --config /config.yml admin create-user --username ci --password GeheimesPasswort"'Im Test quittierte Opengist Password for user bob has been reset. und User ci has been created. Für Skripte nutzen Sie --password-stdin (Passwort nicht in der Prozessliste).
Ein Aufruf von /-/register in einem privaten Browserfenster zeigt Signing up is disabled (HTTP 403).
Schritt 5: Gists per Git über HTTP und SSH
Neue Gists entstehen aus einem lokalen Repository, Pfad: Benutzername und frei wählbarer Name:
# Lokales Repository mit einem Snippet anlegen
mkdir backup-snippet && cd backup-snippet
git init -b master
printf '#!/bin/sh\necho "backup ok"\n' > backup.sh
git add . && git commit -m "Erster Snippet"
# Gist per Push anlegen, Git fragt nach Benutzer und Passwort
git remote add origin https://gist.example.com/admin/backup-snippet
git push -u origin master
# Sichtbarkeit per Push-Option auf privat setzen
git push -o visibility=privateDer Gist entstand beim ersten Push. Öffentliche Gists ließen sich ohne Anmeldung klonen; nach -o visibility=private verlangte Git Zugangsdaten, die Webseite lieferte anonym 404. Ein falsches Passwort meldet irreführend fatal: repository '...' not found.
Für SSH hinterlegen Sie Ihren öffentlichen Schlüssel unter Settings, SSH:
# Clone über SSH, Port entspricht OG_SSH_PORT
git clone ssh://gist.example.com:2222/admin/backup-snippet.git
# Änderung committen und zurückspielen
cd backup-snippet
git commit -am "Update" && git pushIm Test gelangen Clone und Push über SSH, ein nicht hinterlegter Schlüssel wurde mit Permission denied (publickey). abgewiesen. Port 22 des Hosts bleibt frei. OG_SSH_GIT_ENABLED=host delegiert an das Host-OpenSSH.
Der Gist erscheint nach dem Push in der Weboberfläche, und git clone über SSH zeigt alle Commits.
Persistente Daten und Rechte
Alles Wichtige liegt in opengist-data. Der Entrypoint setzt bei jedem Start per chown -R UID und GID. Ohne root laut Doku mit user: "1000:1000" und vorab angelegtem Verzeichnis.
ssh/ enthält den Host-Key. Geht er verloren, melden alle Clients eine geänderte Host-Identität.
ls -ln /opt/opengist/opengist-data zeigt opengist.db, repos und ssh mit UID 1000.
Schritt 6: Netzwerkfreigabe, Reverse Proxy und TLS
Binden Sie Port 6157 an 127.0.0.1 und stellen Sie einen Reverse Proxy mit TLS davor. Kern für Traefik mit Docker-Labels:
labels:
- traefik.http.routers.opengist.rule=Host(`gist.example.com`)
- traefik.http.routers.opengist.entrypoints=websecure
- traefik.http.routers.opengist.tls.certresolver=lets-encrypt
- traefik.http.services.opengist.loadBalancer.server.port=6157Git über HTTP läuft durch den Proxy, Git über SSH nicht: Der SSH-Port ist am Host auf allen Schnittstellen veröffentlicht. Beschränken Sie ihn per Firewall oder binden Sie ihn intern. OG_EXTERNAL_URL gehört auf die HTTPS-Adresse. Laut Doku gibt es eine fail2ban-Anbindung.
curl -sI https://gist.example.com/healthcheck liefert HTTP 200 mit gültigem Zertifikat, ss -ltn zeigt 6157 nur auf 127.0.0.1.
Schritt 7: Backup und Restore
Stoppen Sie den Container kurz, sichern Sie das Datenverzeichnis und starten Sie wieder (konsistentes SQLite-Backup).
cd /opt/opengist
# Container anhalten, damit SQLite und Git-Repositories konsistent sind
docker compose stop
# Datenverzeichnis inklusive Datenbank, Repositories und SSH-Host-Key sichern
tar czf /backup/opengist-$(date +%F).tgz -C opengist-data .
# Wieder starten
docker compose startSichern Sie auch die .env mit OG_SECRET_KEY. Ein neuer Schlüssel machte im Test alle Sessions ungültig; MFA-Daten sind laut Doku ebenfalls verschlüsselt.
Restore im Test: Container stoppen, Datenverzeichnis löschen, Archiv zurückspielen, starten.
cd /opt/opengist
docker compose stop
# Achtung: löscht den aktuellen Stand
rm -rf opengist-data
mkdir opengist-data
tar xzf /backup/opengist-2026-09-28.tgz -C opengist-data
docker compose start
# Prüfen
curl -s http://127.0.0.1:6157/healthcheckDanach war die Prüfsumme der opengist.db identisch, Admin-Login und Admin-Panel funktionierten, die Registrierung blieb gesperrt (403), der SSH-Clone zeigte alle drei Commits ohne Host-Key-Warnung.
tar tzf /backup/opengist-*.tgz listet ./opengist.db, ./repos/ und ./ssh/, nach dem Restore liefert der Healthcheck ok.
Schritt 8: Updates und Rollback-Grenzen
Update mit fester Version in der .env:
# Vorher Backup wie oben anlegen
# Neue Version in der .env eintragen, zum Beispiel OPENGIST_VERSION=1.15.3
docker compose pull
docker compose up -d
# Neue Version im Startbanner prüfen
docker compose logs --no-log-prefix | grep -m1 'Opengist v'Im Test lief die Instanz mit den Daten von 1.15.2 auch unter 1.15.1 und zurück, aber nur für diesen Patch-Schritt. Migrationen laufen beim Start automatisch, ein älteres Image kommt mit migrierter Datenbank nicht zuverlässig klar. Rollback heißt: altes Image und Backup vor dem Update zusammen zurückspielen. Lesen Sie vor Minor-Updates das Changelog.
Das Startbanner zeigt die neue Version, und der Healthcheck liefert ok.
Typische Fehler mit Diagnose und Lösung
| Symptom | Ursache und Lösung |
|---|---|
Connection closed by ... port 2222 beim SSH-Clone | OG_SSH_PORT geändert, aber Mapping zeigt noch auf 2222. Im Test mit Mapping 18613:2222 und OG_SSH_PORT=18613 reproduziert. Container-Port im Mapping an OG_SSH_PORT anpassen |
Permission denied (publickey). | Schlüssel nicht hinterlegt oder falscher Schlüssel im Agent. Unter Settings, SSH prüfen, testweise mit ssh -i und -o IdentitiesOnly=yes |
fatal: repository '...' not found trotz existierendem Gist | Falsches Passwort oder privater Gist ohne Anmeldung. Zugangsdaten und Sichtbarkeit prüfen |
fatal: could not read Username | Gist ist privat, Git fragt nach Zugangsdaten, im nicht interaktiven Skript fehlt der Credential-Helper |
HTTP 403, Log invalid csrf token | Kein gültiger CSRF-Cookie, typisch bei curl-Automatisierung oder falscher OG_EXTERNAL_URL hinter dem Proxy |
HTTP 404 auf /register oder /login | Die Pfade lauten /-/register und /-/login |
Bind for 127.0.0.1:6157 failed: port is already allocated | Port belegt. Mit ss -ltnp den Belegenden finden oder Host-Port ändern |
manifest unknown beim Pull von v1.15.2 | GHCR-Tags haben kein v. Tag 1.15.2 verwenden |
| Alle Nutzer plötzlich abgemeldet | OG_SECRET_KEY fehlt oder wurde geändert. Festen Schlüssel in der .env setzen und sichern |
Saubere Deinstallation
Warnung: Die folgenden Schritte löschen alle Gists, Benutzer und SSH-Schlüssel unwiderruflich. Legen Sie vorher ein Backup an, falls Sie die Daten noch brauchen.
cd /opt/opengist
# Container und Netzwerk entfernen
docker compose down -v --remove-orphans
# Datenverzeichnis und Konfiguration löschen
sudo rm -rf /opt/opengist
# Image gezielt entfernen, keine globalen Prune-Befehle
docker rmi ghcr.io/thomiceli/opengist:1.15.2Vergessen Sie nicht, Firewall-Freigaben für den SSH-Port und die Route im Reverse Proxy zu entfernen.
Häufige Fragen
Warum bricht der SSH-Clone mit Connection closed ab?
OG_SSH_PORT ändert auch den Port des SSH-Servers im Container, nicht nur die Clone-URL. Passen Sie den Container-Port im Port-Mapping an OG_SSH_PORT an.
Warum findet Docker den Tag v1.15.2 nicht?
GHCR-Tags tragen kein v. Der Pull von v1.15.2 endet mit manifest unknown, verwenden Sie 1.15.2.
Wer wird Administrator, und wie verhindern Sie fremde Konten?
Das erste Konto wird Administrator. Danach bleibt die Registrierung offen, bis Sie im Admin-Panel unter Configuration den Schalter Disable signup aktivieren. Einladungslinks im Admin-Panel umgehen die Sperre gezielt.
Was gehört ins Backup?
Das Verzeichnis opengist-data mit Datenbank, Repositories und dem SSH-Host-Key in ssh/, dazu die .env mit OG_SECRET_KEY. Geht der Host-Key verloren, melden alle Clients eine geänderte Host-Identität.
Läuft Git über SSH durch den Reverse Proxy?
Nein. Git über HTTP läuft durch den Proxy, Git über SSH nicht. Der SSH-Port ist am Host auf allen Schnittstellen veröffentlicht, beschränken Sie ihn per Firewall oder binden Sie ihn intern.
Fazit
Opengist liefert einen schlanken Gist-Server mit Git über HTTP und SSH, der mit SQLite im Datenverzeichnis auskommt. Schließen Sie nach dem ersten Konto sofort die Registrierung, halten Sie OG_SSH_PORT und Port-Mapping gleich und sichern Sie opengist-data kalt samt .env. Pull Requests, Issues oder Organisationen bietet Opengist nicht, dafür ist ein Git-Server wie Gitea gedacht.
Weiterführende Anleitungen und Quellen
- Gitea mit Docker: eigener Git-Server für ganze Projekte
- Traefik als Reverse Proxy mit HTTPS für Docker einrichten
- Docker Compose absichern: Secrets, Healthchecks und Non-Root
- Docker und Docker Compose auf Linux installieren (Ubuntu/Debian): die Self-Hosting-Grundlage
- Linux-Server absichern: UFW-Firewall und Fail2ban


