OliveTin mit Docker Compose sicher einrichten: Login, ACL, Socket-Proxy
OliveTin stellt vordefinierte Shell-Befehle als Buttons in einer Weboberfläche bereit. Diese Anleitung richtet die Version 3000.20.0 mit Docker Compose ein: Login mit Argon2id, Gruppen mit Zugriffslisten, eingeschränkte Argumente, Socket-Proxy statt Rohsocket, nginx davor sowie Backup und Restore.
Geprüft am 11.10.2026 · für OliveTin 3000.20.0
Mit KI erstellt – redaktionelle Prüfung ausstehend
WerbelinksMit * 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

OliveTin macht aus vordefinierten Shell-Befehlen Schaltflächen in einer schlanken Weboberfläche. Ein Teammitglied klickt auf „Container neu starten“, statt sich per SSH anzumelden und den Befehl selbst zu tippen. Das spart Zeit, bringt aber ein Risiko mit: Ein Dienst, der Befehle auf Ihrem Server ausführt, gehört zu den sensibelsten Anwendungen im Netz. Diese Anleitung richtet OliveTin deshalb von Anfang an abgesichert ein.
Sie bauen einen Compose-Stack mit festgelegter Version, Login, zwei Benutzergruppen, validierten Argumenten, Docker-Socket-Proxy und Reverse Proxy, dazu Backup und Wiederherstellung. Alles wurde auf einem Docker-Testhost mit OliveTin 3000.20.0 durchgespielt.
Voraussetzungen
Im Leerlauf belegte OliveTin auf dem Testhost etwa 4 MiB Arbeitsspeicher, der nginx knapp 5 MiB und der Socket-Proxy rund 24 MiB. Das Image ist entpackt rund 626 MB groß, weil es auf Fedora basiert und die Docker-CLI mitbringt.
- Linux-Server mit Docker Engine und Compose v2 (getestet mit Docker 29.8 auf x86-64; das Image gibt es laut Docker Hub auch für ARM64)
- mindestens 1 CPU-Kern, 512 MB RAM und 1 GB freien Speicher; empfohlen sind 2 Kerne und 4 GB RAM, wenn der Server weitere Dienste trägt
- für den Zugriff aus dem Team ein Hostname und ein TLS-Zertifikat auf einem vorgeschalteten Reverse Proxy
| Eckdatum | Wert |
|---|---|
| Image | ghcr.io/olivetin/olivetin:3000.20.0 (Docker Hub: jamesread/olivetin) |
| Port | 1337 im Container, nur im Compose-Netz erreichbar; veröffentlicht wird 8080 des nginx |
| Volume | ./config nach /config (config.yaml, sessions.yaml) |
| Wichtige Variablen | TZ, DOCKER_HOST (zeigt auf den Socket-Proxy) |
| Container-Benutzer | olivetin, UID 1000, GID 999 |
| Lizenz | AGPL-3.0 |
Stand 11.10.2026: Das Repository OliveTin/OliveTin hat 3.832 Sterne, ist nicht archiviert und erhielt den letzten Push am 05.10.2026. Die Version 3000.20.0 erschien am 10.09.2026. Die AGPL-3.0 betrifft vor allem Sie, wenn Sie den Code ändern und als Netzdienst anbieten. Lizenzschlüssel oder Bezahlstufen sind nicht aufgetaucht.
Schritt 1: Projektordner, Compose-Datei und Start
Der Container-Benutzer hat die UID 1000 und muss in config lesen und schreiben können, denn OliveTin legt dort die Datei sessions.yaml an.
mkdir -p ~/olivetin/config ~/olivetin/proxy && cd ~/olivetin
sudo chown -R 1000:1000 config
Die .env enthält nur Versionen und Ports, keine Geheimnisse.
OLIVETIN_VERSION=3000.20.0
SOCKET_PROXY_VERSION=3.4.6
NGINX_VERSION=1.29-alpine
TZ=Europe/Berlin
PROXY_BIND=127.0.0.1
PROXY_PORT=8080
Die compose.yaml startet OliveTin, den Socket-Proxy, nginx und einen Demo-Container, den Sie später per Klick neu starten.
name: olivetin
services:
olivetin:
image: ghcr.io/olivetin/olivetin:${OLIVETIN_VERSION}
container_name: olivetin
restart: unless-stopped
environment:
TZ: ${TZ}
DOCKER_HOST: tcp://socket-proxy:2375
volumes:
- ./config:/config
expose:
- "1337"
healthcheck:
test: ["CMD", "curl", "-fsS", "-o", "/dev/null", "http://localhost:1337/"]
interval: 30s
timeout: 5s
retries: 3
start_period: 15s
depends_on:
- socket-proxy
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
socket-proxy:
image: lscr.io/linuxserver/socket-proxy:${SOCKET_PROXY_VERSION}
container_name: olivetin-socket-proxy
restart: unless-stopped
environment:
CONTAINERS: 1
ALLOW_RESTARTS: 1
POST: 0
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
read_only: true
tmpfs:
- /run
security_opt:
- no-new-privileges:true
proxy:
image: nginx:${NGINX_VERSION}
container_name: olivetin-proxy
restart: unless-stopped
ports:
- "${PROXY_BIND}:${PROXY_PORT}:80"
volumes:
- ./proxy/olivetin.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- olivetin
demo-target:
image: alpine:3.22
container_name: demo-target
command: ["sleep", "infinity"]
restart: unless-stopped
Legen Sie proxy/olivetin.conf (Inhalt in Schritt 5) und eine erste config/config.yaml an:
logLevel: "INFO"
actions:
- title: Speicherplatz anzeigen
shell: df -h /config
icon: disk
onclick: execution-dialog
Starten Sie den Stack:
docker compose up -d
docker compose ps
Verifizieren: docker compose ps zeigt alle vier Container als Up, olivetin nach wenigen Sekunden als healthy. curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/ liefert 200. Im Log steht „OliveTin started“ mit configDir="/config".
Schritt 2: Passwort-Hashes erzeugen
Lokale Benutzer speichert OliveTin mit Argon2id-Hash in der config.yaml. Die laufende Instanz erzeugt den Hash über ihre API. Mit read -rs landet das Passwort nicht in der Shell-Historie. Solange kein Login aktiv ist, muss der Dienst lokal bleiben, deshalb der Bind auf 127.0.0.1.
read -rs PW; echo
curl -sS --json "{\"password\": \"$PW\"}" http://127.0.0.1:8080/api/PasswordHash | jq -r .hash
Die Antwort ist ein JSON-Objekt mit dem Feld hash. Die Dokumentation zeigt noch den Satz „Your password hash is“, Version 3000.20.0 liefert jedoch JSON. Wiederholen Sie den Aufruf für jeden Benutzer.
Verifizieren: Jeder Hash beginnt mit $argon2id$v=19$m=65536,t=4,p=4$. Der HTTP-Status des Aufrufs ist 200.
Schritt 3: Aktionen, Argumente und Zugriffsregeln
Drei Bausteine sind wichtig. Erstens setzt authRequireGuestsToLogin: true alle Standardrechte auf false, Gäste sehen nur die Anmeldeseite. Zweitens regeln Zugriffslisten (ACLs), wer welche Aktion ausführt: „admins“ hängt mit addToEveryAction an jeder Aktion, „operators“ nur dort, wo es eingetragen ist. Drittens begrenzen Argumenttypen die Eingabe.
Ersetzen Sie die Platzhalter durch Ihre Hashes. Der Typ dnsname lässt nur gültige Hostnamen zu, die Auswahlliste nur vorab definierte Werte.
logLevel: "INFO"
pageTitle: "Betrieb: Server-Aktionen"
authRequireGuestsToLogin: true
authLocalUsers:
enabled: true
users:
- username: admin
usergroup: admins
password: HASH_ADMIN_HIER
- username: operator
usergroup: operators
password: HASH_OPERATOR_HIER
accessControlLists:
- name: admins
matchUsergroups:
- admins
permissions:
view: true
exec: true
logs: true
addToEveryAction: true
- name: operators
matchUsergroups:
- operators
permissions:
view: true
exec: true
logs: true
defaultPolicy:
showDiagnostics: false
showLogList: true
actions:
- title: Speicherplatz anzeigen
shell: df -h /config
icon: disk
onclick: execution-dialog
acls:
- operators
- title: Host auflösen
shell: getent hosts {{ host }}
icon: ping
onclick: execution-dialog
acls:
- operators
arguments:
- name: host
title: Hostname
type: dnsname
- title: Container neu starten
shell: docker restart {{ container }}
timeout: 30
icon: restart
onclick: execution-dialog
arguments:
- name: container
title: Container
choices:
- title: Demo-Ziel
value: demo-target
- title: Langer Lauf (Timeout 10 s)
shell: sleep 6 && echo fertig
timeout: 10
onclick: execution-dialog
Übernehmen Sie die Änderung mit docker compose restart olivetin. Die Datei wird nur beim Start gelesen.
Verifizieren: Ein anonymer Aufruf von /api/GetDashboard endet mit „guests are not allowed to access the dashboard“. Nach der Anmeldung sieht der Benutzer „admin“ alle vier Aktionen, „operator“ nur Speicherplatz und Host auflösen.


Gefährlich bleiben Argumente. Roh-Typen wie very_dangerous_raw_string, password, url und eigene regex-Typen lehnt OliveTin bei shell: ab, dort ist exec: nötig. Bevorzugen Sie Auswahllisten und den strengsten Typ. Der Hostname „x; id“ wird mit „invalid dnsname label“ abgewiesen, der Befehl läuft gar nicht erst.

Ruft operator per API die Admin-Aktion auf, antwortet OliveTin mit „permission denied“, im Log steht „ACL check failed. Blocked from executing.“ Eine Anmeldung mit falschem Passwort liefert HTTP 200 mit {"success":false}, Skripte müssen also den Inhalt prüfen.
Schritt 4: Docker-Zugriff über den Socket-Proxy
Der Zugriff auf /var/run/docker.sock ist faktisch Root-Zugriff auf den Host. Im Test genügte ein Container mit gemountetem Socket und der Docker-Gruppe, um mit einem zweiten Container das Wurzelverzeichnis des Hosts einzubinden und dessen /etc/os-release zu lesen. Ohne group_add scheiterte der Versuch an „permission denied“.
Sicherer ist der Socket-Proxy aus dem Stack: Nur er sieht den Socket, schreibgeschützt. CONTAINERS=1 und ALLOW_RESTARTS=1 erlauben Abfragen und Neustarts, POST=0 sperrt alles Übrige. OliveTin nutzt ihn über DOCKER_HOST. Veröffentlichen Sie den Port 2375 niemals.
docker compose exec olivetin docker ps --format '{{.Names}}'
docker compose exec olivetin docker restart demo-target
docker compose exec olivetin docker rm -f demo-target
docker compose exec olivetin docker run --rm alpine id
Verifizieren: Die ersten beiden Befehle funktionieren. Die letzten beiden enden mit „403 Forbidden, Request forbidden by administrative rules“, ebenso docker exec in andere Container. In der Weboberfläche startet die Aktion „Container neu starten“ den Demo-Container neu.

Beachten Sie die Dauer: Docker beendet einen Container, der SIGTERM ignoriert, erst nach 10 Sekunden. OliveTin bricht Aktionen standardmäßig nach 3 Sekunden ab, weshalb hier timeout: 30 steht. Ohne diese Zeile lief der Neustart zwar zu Ende, die Anzeige meldete aber „timed out after 3 seconds“.
Schritt 5: Reverse Proxy und Freigabe
Der Port 1337 bleibt im Compose-Netz, von außen erreichbar ist nur der nginx. Auf dem Testhost scheiterte curl gegen Port 1337 mit Fehler 7, gegen Port 8080 kam Status 200. Den Websocket-Block aus der Dokumentation brauchte Version 3000.20.0 nicht: Der Pfad /websocket lieferte nur die Startseite.
server {
listen 80;
server_name _;
# Nur Weboberfläche und API von OliveTin weiterreichen
location / {
proxy_pass http://olivetin:1337/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
}
Für das Team schalten Sie einen TLS-terminierenden Proxy auf dem Host davor, der auf 127.0.0.1:8080 weiterleitet, oder setzen PROXY_BIND auf die interne Adresse, jeweils erst nach aktivem Login. Das Anmeldecookie ist HttpOnly mit SameSite=Lax. Ins offene Internet gehört der Dienst nicht, die Dokumentation rät ausdrücklich davon ab. Besser sind VPN oder eine Zugangsschicht davor.
Verifizieren: curl -sI http://127.0.0.1:8080/ liefert 200 und die Header X-Frame-Options: DENY sowie eine Content-Security-Policy. Der Aufruf von http://127.0.0.1:1337/ schlägt fehl.
Schritt 6: Healthcheck, Backup und Wiederherstellung
Das Image bringt keinen Healthcheck mit, der Stack ergänzt einen mit curl. Persistent ist nur der Ordner config: Konfiguration und sessions.yaml mit den angemeldeten Sitzungen. Die Sitzungen liegen dort im Klartext, die Datei hat den Modus 600. Behandeln Sie das Backup deshalb wie ein Geheimnis.
docker compose stop olivetin
tar czf olivetin-config-$(date +%F).tgz config
docker compose start olivetin
Geprüft wurde die Wiederherstellung mit einer Marker-Aktion: ergänzen, sichern, config leeren, zurückspielen, Besitzer setzen, starten. Danach war der Marker wieder da, und die alte admin-Sitzung galt weiter.
docker compose stop olivetin
rm -rf config && mkdir config
tar xzf olivetin-config-JJJJ-MM-TT.tgz
sudo chown -R 1000:1000 config
docker compose start olivetin
Verifizieren: Der Dashboard-Aufruf mit der alten Sitzung listet die Aktion „Marker Restore-Test“. Der Status im docker compose ps ist nach wenigen Sekunden healthy.
Schritt 7: Update und Deinstallation
Ändern Sie für ein Update OLIVETIN_VERSION in der .env und sichern Sie vorher config. Eine Datenbank gibt es nicht, ein Rollback besteht im Eintragen des alten Tags. Für den Sprung zwischen den Reihen 2k und 3000 hat das Projekt eine eigene Migrationsanleitung, lesen Sie die Release-Notes vorher.
docker compose pull olivetin
docker compose up -d olivetin
docker compose logs olivetin | grep -i 'initializing'
docker compose down entfernt Container und Netz, der Ordner config bleibt liegen. Löschen Sie ihn erst mit Backup: Danach sind config.yaml und alle Sitzungen unwiederbringlich verloren.
Verifizieren: Die Log-Zeile zeigt die neue Version. Nach docker compose down listet docker ps -a keine Container des Stacks mehr.
Troubleshooting
| Symptom | Ursache und Lösung |
|---|---|
| Container startet ständig neu, Log: „mapping key \"timeout\" already defined at line 59“ | Doppelter Schlüssel in der config.yaml. Die Datei ist striktes YAML, entfernen Sie den doppelten Eintrag. |
| Restart-Schleife, Log: „open /config/config.yaml: permission denied“ | Der Container-Benutzer (UID 1000) darf die Datei nicht lesen. Besitzer oder Modus anpassen, zum Beispiel chown 1000:1000 und chmod 644. |
| docker pull mit Tag v3000.20.0 scheitert mit „not found“ | Der Tag heißt 3000.20.0 ohne v. Die Release-Bezeichnung auf GitHub weicht vom Registry-Tag ab. |
| Aktion endet mit „timed out after 3 seconds“ | Standardzeitlimit. Setzen Sie bei langen Aktionen timeout pro Aktion. |
| Docker-Befehle melden „403 Forbidden“ | Der Socket-Proxy sperrt die Operation. Öffnen Sie nur das, was die Aktion wirklich braucht, etwa ALLOW_RESTARTS. |
| Mit gemountetem Rohsocket „permission denied while trying to connect to the docker API“ | Der Benutzer olivetin gehört nicht zur Gruppe des Sockets. Mit group_add und der GID aus getent group docker lösbar, sicherer ist der Proxy. |
Häufige Fragen
Darf ich OliveTin im Internet erreichbar machen?
Das Projekt rät ausdrücklich davon ab. Wählen Sie ein VPN oder eine Zugangsschicht wie Warpgate.
Warum nicht einfach den Docker-Socket mounten?
Weil ein Fehler dann Root-Rechte auf dem Host bedeutet. Der Proxy begrenzt den Schaden auf die erlaubten Operationen.
Gibt es Alternativen?
Für geplante Jobs statt Klick-Aktionen eignet sich Cronicle. OliveTin passt, wenn Menschen gezielt einzelne Befehle auslösen.
Fazit
OliveTin ist ein schlankes Werkzeug mit kleiner Angriffsfläche, solange Sie Login, Zugriffslisten und strenge Argumente konsequent nutzen. Der Stack ließ die erwarteten Aktionen zu und blockierte Gäste, den Operator bei Admin-Aktionen, ungültige Eingaben und gefährliche Docker-Befehle. Für KMU ist das eine sichere Brücke zwischen Admins und weniger erfahrenen Kollegen.
Weiterführende Anleitungen und Quellen
- Arcane per Docker Compose installieren und absichern, eine weitere Docker-Verwaltungsoberfläche mit Socket-Zugriff
- Nginx UI mit Docker Compose betreiben und absichern, für den vorgeschalteten Reverse Proxy
- Warpgate als Bastion Host, als Zugangsschicht vor internen Weboberflächen
- OliveTin Dokumentation
- OliveTin: Docker Compose
- OliveTin: Access Control Lists
- OliveTin: Argumenttypen
- GitHub: OliveTin/OliveTin
- Release 3000.20.0
- linuxserver/docker-socket-proxy


