Opengist mit Docker Compose: eigener Gist-Server mit Git über HTTP und SSH
Opengist ist ein schlanker, selbst gehosteter Snippet- und Gist-Server, bei dem jeder Gist ein eigenes Git-Repository ist. Die Anleitung zeigt Installation mit Docker Compose, Erstkonfiguration mit gesperrter Registrierung, Clone und Push über HTTP und SSH, Reverse Proxy, Backup mit geprüftem Restore, Updates und typische Fehler.

Kurze Skripte, Konfigurationsschnipsel und Befehlsfolgen landen in vielen Admin-Teams in Chatverläufen, privaten GitHub-Gists oder Textdateien auf dem Desktop. Opengist ist ein selbst gehosteter Gist-Server: Jeder Snippet ist ein eigenes Git-Repository, das sich im Browser bearbeiten und zusätzlich per git clone und git push über HTTP oder SSH pflegen lässt. Die Daten bleiben auf dem eigenen Server.
Diese Anleitung zeigt den Betrieb mit Docker Compose vollständig: compose.yaml und .env, Erstkonfiguration mit Admin-Konto, geschlossene Registrierung, Git über HTTP und SSH, Reverse Proxy, Backup mit echtem Restore, Updates, typische Fehler und Deinstallation. Der wichtigste Praxisfund aus dem Test vorweg: Die Variable OG_SSH_PORT ändert nicht nur die angezeigte Clone-URL, sondern auch den Port, auf dem der SSH-Server im Container lauscht. Wer das Port-Mapping nicht anpasst, bekommt nur Connection closed.
Nutzen und Grenzen von Opengist
Opengist ist in Go geschrieben, steht unter der AGPL-3.0 und orientiert sich funktional an GitHub Gist. Laut GitHub-API (Abruf am 28.09.2026) hat das Repository thomiceli/opengist 3.362 Sterne, der letzte Push und das aktuelle Release v1.15.2 stammen vom 30.08.2026. Das Projekt wird im Kern von einem Hauptentwickler getragen, was Sie bei der Einschätzung der langfristigen Pflege berücksichtigen sollten.
Was Opengist gut kann:
- Snippets mit mehreren Dateien, Syntaxhervorhebung, Revisionen und Sichtbarkeit öffentlich, ungelistet oder privat
- Git-Zugriff über HTTP und über einen eingebauten SSH-Server, inklusive 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
Die Grenzen: Opengist ist kein Ersatz für eine Code-Plattform. Es gibt keine Pull Requests, keine Issues und keine Organisationen mit fein abgestuften Rechten. Wer ganze Projekte versionieren will, ist mit einem vollwertigen Git-Server besser bedient. Außerdem ist die Standardkonfiguration 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 mit Docker Engine und Docker Compose v2
- Ein freier HTTP-Port (intern 6157) und optional ein freier SSH-Port für Git
- Ein DNS-Name und ein Reverse Proxy mit TLS, wenn der Dienst außerhalb von localhost erreichbar sein soll
- Ein unprivilegierter Host-Benutzer, dessen UID und GID das Datenverzeichnis besitzen sollen
Opengist ist sparsam. Im Test belegte das Image ghcr.io/thomiceli/opengist:1.15.2 142 MB auf der Platte, der Container brauchte direkt nach dem Start rund 25 MiB RAM und nach Git-Operationen und Indexaufbau rund 103 MiB. Das GHCR-Manifest von 1.15.2 enthält Images für amd64 und arm64, damit läuft Opengist auch auf ARM-Servern und Einplatinenrechnern. Getestet wurde auf amd64.
Als Datenbank nutzt Opengist standardmäßig SQLite im Datenverzeichnis. PostgreSQL und MySQL/MariaDB werden laut Doku über OG_DB_URI unterstützt, für kleine Teams reicht SQLite aber völlig aus und vereinfacht das Backup erheblich.
Getestete Version und Image-Tags
Getestet wurde Opengist v1.15.2, das Startbanner im Log lautet Opengist v1.15.2. Die Tags in der GitHub Container Registry tragen kein v: 1.15.2, 1.15, 1 und latest existieren, v1.15.2 dagegen nicht. Die offizielle Doku verwendet den Tag 1, der innerhalb der Hauptversion automatisch mitwandert. Für einen planbaren Betrieb empfiehlt sich der exakte Tag 1.15.2, den Sie in der .env pflegen.
Vollständige compose.yaml und .env
Die folgende Datei basiert auf dem offiziellen Beispiel aus der Opengist-Doku und wurde für den Test um Versionsvariable, Healthcheck, Bindung an 127.0.0.1 und einen abweichenden SSH-Port ergänzt. Legen Sie ein Verzeichnis an, etwa /opt/opengist, und speichern Sie dort:
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
- "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: 20s
Dazu 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 .env
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 für die Verschlüsselung von MFA-Daten in der Datenbank. Ohne Angabe erzeugt Opengist einen Zufallswert |
OG_SSH_PORT | Port des eingebauten SSH-Servers im Container und gleichzeitig Port in der angezeigten Clone-URL, Standard 2222 |
OG_SSH_EXTERNAL_DOMAIN | Hostname in der SSH-Clone-URL, falls er vom HTTP-Namen abweicht |
OG_HTTP_GIT_ENABLED, OG_SSH_GIT_ENABLED | Git über HTTP ein oder aus, SSH als builtin, host oder disabled |
Das Image bringt eine /config.yml mit allen Standardwerten mit. Umgebungsvariablen mit Präfix OG_ überschreiben diese Werte, das Log listet beim Start genau auf, welche Variablen übernommen wurden. Eine vollständige Liste steht im Configuration Cheat Sheet der Doku.
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 -12
Im Test meldete das Log nach wenigen Sekunden die angewendeten Migrationen, Bleve indexer initialized, Starting HTTP server on http://0.0.0.0:6157 und Starting SSH server on ssh://0.0.0.0:.... Das Datenverzeichnis gehörte anschließend dem Host-Benutzer mit UID 1000.
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 -1
Die Antwort lautete im Test {"database":"ok","opengist":"ok","time":"..."} mit HTTP 200, der HEAD-Aufruf lieferte HTTP/1.1 200 OK. Der Healthcheck in der Compose-Datei nutzt wget, das im Image vorhanden ist, und setzte den Container zuverlässig auf healthy.
Erstkonfiguration: Admin-Konto und Registrierung schließen
Die Registrierung liegt unter /-/register, der Login unter /-/login. Das erste angelegte Konto wird automatisch Administrator. Im Test bestätigte die Datenbank das: Der erste Nutzer hatte is_admin = 1, der zweite 0. Das Admin-Panel unter /-/admin-panel antwortete für den Admin mit 200 und für den zweiten Nutzer mit 404.
Wichtig: Nach dem ersten Konto bleibt die Registrierung offen. Im Test ließ sich direkt ein zweites Konto ohne jede Einladung anlegen. Schließen Sie die Registrierung deshalb sofort nach dem Anlegen des Admins:
- 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, wenn auch öffentliche Gists nur für angemeldete Nutzer sichtbar sein sollen
Diese Einstellungen liegen in der Datenbank, nicht in der Konfigurationsdatei. Das Cheat Sheet kennt keine Umgebungsvariable für das Schließen der Registrierung. Im Test stand nach dem Umschalten disable-signup = 1 in der Tabelle admin_settings, ein weiterer Registrierungsversuch endete mit HTTP 403 und der Meldung Signing up is disabled. Die Einstellung überstand sowohl ein Neuerstellen des Containers als auch den Restore aus dem Backup. Neue Kollegen laden Sie danach über Einladungslinks im Admin-Panel ein, diese setzen die Sperre gezielt außer Kraft.
Für automatisierte Installationen gibt es CLI-Befehle, die im Container als Benutzer opengist laufen müssen:
# 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 meldete der erste Befehl Password for user bob has been reset., danach funktionierte nur noch das neue Passwort. Das Anlegen per CLI quittierte Opengist mit User ci has been created.. Für Skripte empfiehlt die Doku stattdessen --password-stdin, damit das Passwort nicht in der Prozessliste auftaucht.
Gists per Git über HTTP und SSH
Neue Gists lassen sich direkt aus einem lokalen Repository anlegen. Der Pfad besteht aus Ihrem Benutzernamen und einem frei wählbaren Namen:
# 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=private
Im Test entstand der Gist beim ersten Push, und Opengist empfahl im Remote-Text, die URL ohne eingebettete Zugangsdaten zu setzen. Solange der Gist öffentlich war, funktionierte git clone ohne Anmeldung. Nach -o visibility=private verlangte Git Zugangsdaten, die Webseite lieferte anonym 404. Ein falsches Passwort führt zur irreführenden Meldung fatal: repository '...' not found statt zu einem Authentifizierungsfehler.
Für SSH hinterlegen Sie Ihren öffentlichen Schlüssel unter Settings, SSH. Danach klappt der Zugriff über den eingebauten SSH-Server:
# 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 push
Im Test gelangen Clone und Push über SSH. Ein nicht hinterlegter Schlüssel wurde mit Permission denied (publickey). abgewiesen. Der eingebaute Server läuft unabhängig vom SSH-Dienst des Hosts, Port 22 bleibt also frei für die Administration. Alternativ kann Opengist laut Doku mit OG_SSH_GIT_ENABLED=host an das OpenSSH des Hosts delegieren.
Persistente Daten und Rechte
Alles, was zählt, liegt im gemounteten Verzeichnis opengist-data. Weil der Entrypoint bei jedem Start chown -R auf UID und GID ausführt, gehören die Dateien immer dem konfigurierten Host-Benutzer. Das erleichtert Backups ohne root. Wer den Container von Anfang an ohne root betreiben will, setzt laut Doku stattdessen user: "1000:1000" und muss das Datenverzeichnis vorher selbst mit passenden Rechten anlegen. Diese Variante wurde nicht getestet.
Den Ordner ssh/ sollten Sie nicht unterschätzen: Er enthält den Host-Key des Git-SSH-Servers. Geht er verloren, erzeugt Opengist einen neuen Schlüssel, und alle Clients melden beim nächsten Zugriff eine geänderte Host-Identität.
Netzwerkfreigabe, Reverse Proxy und TLS
Die Weboberfläche sollte nie unverschlüsselt ins Netz. Binden Sie Port 6157 wie oben an 127.0.0.1 und stellen Sie einen Reverse Proxy mit TLS davor. Die Doku liefert Beispiele für Nginx und Traefik. Für Traefik mit Docker-Labels sieht der Kern so aus:
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=6157
Git über HTTP läuft durch denselben Proxy, Git über SSH dagegen nicht: Der SSH-Port muss direkt am Host veröffentlicht und in der Firewall freigegeben werden. Setzen Sie OG_EXTERNAL_URL auf die HTTPS-Adresse, damit Links und Clone-URLs stimmen. Laut Doku bietet Opengist außerdem eine Anbindung an fail2ban gegen Brute-Force-Versuche auf den Login.
Backup und Restore
Für eine konsistente Sicherung mit SQLite stoppen Sie den Container kurz, sichern das komplette Datenverzeichnis und starten wieder. Bei wenigen Megabyte dauert das Sekunden.
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 start
Sichern Sie zusätzlich die .env mit dem OG_SECRET_KEY an einem geschützten Ort. Im Test führte ein neuer Schlüssel dazu, dass bestehende Sessions ungültig wurden und alle Nutzer sich neu anmelden mussten. Laut Doku verschlüsselt der Schlüssel auch MFA-Daten. Nach einem Schlüsselverlust wären hinterlegte TOTP-Zweitfaktoren daher vermutlich nicht mehr nutzbar, das wurde nicht geprüft.
Der Restore wurde im Test vollständig durchgespielt: Container gestoppt, Datenverzeichnis gelöscht, aus dem Archiv zurückgespielt, gestartet.
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/healthcheck
Nach dem Restore war die Prüfsumme der opengist.db identisch, der Admin konnte sich anmelden, das Admin-Panel lieferte 200, die gesperrte Registrierung blieb bei 403, und der SSH-Clone zeigte alle drei Commits ohne Host-Key-Warnung, weil der alte Schlüssel mit zurückkam.
Updates und Rollback-Grenzen
Die offizielle Update-Anleitung ist kurz: Datenverzeichnis sichern, neues Image ziehen, Container neu starten. Mit fester Version in der .env sieht das so aus:
# 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 ließ sich die Instanz mit den Daten von 1.15.2 auch mit 1.15.1 starten und nutzen, danach wieder mit 1.15.2. Das gilt aber nur für diesen Patch-Schritt. Beim Start wendet Opengist Datenbankmigrationen automatisch an, und ein älteres Image kann mit einer bereits migrierten Datenbank nicht zuverlässig umgehen. Ein Rollback über Versionen mit Migrationen bedeutet deshalb: altes Image und das vor dem Update angelegte Backup zusammen zurückspielen. Lesen Sie vor jedem Minor-Update das Changelog auf der Release-Seite.
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 im Opengist-Profil 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 | Formular ohne gültigen CSRF-Cookie abgeschickt, typisch bei Automatisierung per curl oder bei falsch gesetzter 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, im Test mit einem Hilfscontainer provoziert. 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.2
Vergessen Sie nicht, Firewall-Freigaben für den SSH-Port und die Route im Reverse Proxy zu entfernen.
Testumfang
Am 28.09.2026 selbst getestet wurden Start mit v1.15.2, Healthcheck, erster Nutzer als Admin, Schließen der Registrierung mit 403 als Negativprobe, Anlegen, Clone und Push über HTTP und SSH, Sichtbarkeit privat, CLI-Passwort-Reset, Backup mit vollständigem Restore, Wechsel auf 1.15.1 und zurück, Schlüsselwechsel sowie die gezeigten Fehlermeldungen. Reverse Proxy mit TLS, OAuth, OIDC, LDAP, externe Datenbanken, Meilisearch, Rootless-Betrieb, SSH im Host-Modus und arm64 stammen nur aus der offiziellen Dokumentation.
Passende Anleitungen auf S-EDV
- 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


