Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Synology / NAS 11.09.2026 · 13 min Lesezeit

Semaphore UI auf dem Synology NAS installieren: Web-Oberfläche für Ansible

Semaphore UI bringt Ansible in den Browser: Playbooks per Klick starten, SSH-Keys sicher in der Datenbank speichern, Inventory verwalten – alles ohne Kommandozeile. Diese Anleitung zeigt, wie du Semaphore UI als Compose-Projekt mit PostgreSQL auf dem Synology NAS einrichtest.

Semaphore auf dem NAS: Ansible, Playbooks, SSH-Keys. Illustration mit Laptop und Administrationsoberfläche. KI-generiert

Wer regelmäßig mehrere Linux-Server verwaltet, kennt das Szenario: Ansible-Playbooks liegen irgendwo in einem Git-Repository, die Ausführung erfolgt per Kommandozeile auf dem eigenen Laptop – und sobald ein Kollege einen Update-Lauf starten soll, beginnt die Erklärung von vorn. Semaphore UI löst genau dieses Problem. Die browserbasierte Web-Oberfläche macht Ansible bedienbar, ohne dass jeder Mitarbeiter die CLI kennen muss: Playbooks starten, Ausführungshistorie einsehen, SSH-Keys sicher hinterlegen und Zugriffsrechte per Benutzer-/Team-Modell vergeben – alles über den Browser. Das Synology NAS bietet sich als dauerläufiger Host an: Es steht im Heimnetz oder Büro, zieht wenig Strom und Docker läuft dank Container Manager out of the box. So wird das NAS zur stillen Automatisierungszentrale, die Server-Updates, Compliance-Checks und Konfigurationsmanagement ohne zusätzlichen Cloud-Dienst übernimmt. Semaphore UI unterstützt neben Ansible auch Terraform, OpenTofu und PowerShell – der Einstieg lohnt sich also weit über Ansible hinaus.

Voraussetzungen

  • Synology NAS mit DSM 7.2 oder neuer, x86_64-Architektur (z. B. DS920+, DS923+, DS1522+) oder ARM64 (z. B. DS423+)
  • Container Manager aus dem Package Center installiert
  • Mindestens 2 GB freier RAM (empfohlen: 4 GB, damit NAS-Betrieb, PostgreSQL und Semaphore parallel laufen)
  • Mindestens 5 GB freier Speicher auf /volume1 für Container-Images und Datenbankdaten
  • SSH-Zugang zum NAS aktiviert (Schritt für Schritt: SSH auf dem Synology NAS aktivieren und verbinden)
  • Einen Linux-Zielhost mit aktiviertem SSH-Zugang für den ersten Ansible-Test
  • Einen SSH-Keypair (ed25519 empfohlen) für Ansible-Verbindungen zum Zielhost

Schritt 1: Ordnerstruktur anlegen und Verschlüsselungsschlüssel generieren

Semaphore UI benötigt vier Host-Verzeichnisse, die vor dem ersten Container-Start existieren müssen. Fehlen sie, kann PostgreSQL sein Datenverzeichnis nicht initialisieren und der Start schlägt still fehl. Öffne eine SSH-Verbindung zum NAS (Details: SSH auf dem Synology NAS aktivieren und verbinden) und lege die Ordner an:

mkdir -p /volume1/docker/semaphore/db
mkdir -p /volume1/docker/semaphore/data
mkdir -p /volume1/docker/semaphore/config
mkdir -p /volume1/docker/semaphore/temp
chmod 755 /volume1/docker/semaphore/db
chmod 755 /volume1/docker/semaphore/data
chmod 755 /volume1/docker/semaphore/config
chmod 755 /volume1/docker/semaphore/temp

Generiere jetzt den Verschlüsselungsschlüssel für SSH-Keys und Passwörter in der Datenbank. Dieser Wert darf nach dem ersten Start nie mehr geändert werden, da sonst alle gespeicherten Credentials unlesbar werden:

head -c32 /dev/urandom | base64

Der Befehl gibt einen 44-stelligen Base64-String aus, zum Beispiel:

k7Xv2pN1mQwRzA8uLsTjHcYdFbGnEoKi4e5f6g7h8=

Kopiere diesen Wert in die Zwischenablage – du benötigst ihn im nächsten Schritt. Speichere ihn zusätzlich an einem sicheren Ort (z. B. Passwortmanager), da er für die Wiederherstellung aus einem Backup unbedingt benötigt wird.

Lege außerdem die requirements.txt an, damit Ansible im Container verfügbar ist. Das offizielle :latest-Image enthält Ansible nicht vorinstalliert – ohne diese Datei schlägt der erste Playbook-Run mit ansible: command not found fehl:

echo "ansible" > /volume1/docker/semaphore/config/requirements.txt

Verifizieren: Prüfe, ob alle vier Ordner angelegt wurden und die Datei existiert:

ls -la /volume1/docker/semaphore/
cat /volume1/docker/semaphore/config/requirements.txt

Erwartete Ausgabe: Vier Unterordner (db, data, config, temp) mit Berechtigung drwxr-xr-x sowie ansible als Inhalt der requirements.txt.

Schritt 2: compose.yaml erstellen

Erstelle die Compose-Datei unter /volume1/docker/semaphore/compose.yaml. Ersetze die Platzhalter-Passwörter durch eigene sichere Werte und füge den generierten Schlüssel aus Schritt 1 ein. Passwörter mit Sonderzeichen wie ! oder @ müssen in Anführungszeichen stehen, sonst entsteht ein YAML-Parse-Fehler.

services:
  semaphore-db:
    image: postgres:14
    container_name: Semaphore-DB
    hostname: semaphore-db
    restart: unless-stopped
    volumes:
      - /volume1/docker/semaphore/db:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: semaphore
      POSTGRES_PASSWORD: "SicheresDBPasswort123!"
      POSTGRES_DB: semaphore
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U semaphore"]
      interval: 10s
      timeout: 5s
      retries: 5

  semaphore:
    image: semaphoreui/semaphore:latest
    container_name: Semaphore-UI
    restart: unless-stopped
    ports:
      - "3000:3000"
    volumes:
      - /volume1/docker/semaphore/data:/var/lib/semaphore
      - /volume1/docker/semaphore/config:/etc/semaphore
      - /volume1/docker/semaphore/temp:/tmp/semaphore
    environment:
      SEMAPHORE_DB_USER: semaphore
      SEMAPHORE_DB_PASS: "SicheresDBPasswort123!"
      SEMAPHORE_DB_HOST: semaphore-db
      SEMAPHORE_DB_PORT: "5432"
      SEMAPHORE_DB_DIALECT: postgres
      SEMAPHORE_DB: semaphore
      SEMAPHORE_PLAYBOOK_PATH: /tmp/semaphore/
      SEMAPHORE_ADMIN: admin
      SEMAPHORE_ADMIN_PASSWORD: "SicheresAdminPasswort456!"
      SEMAPHORE_ADMIN_NAME: Admin
      SEMAPHORE_ADMIN_EMAIL: admin@localhost
      SEMAPHORE_ACCESS_KEY_ENCRYPTION: "HIER_DEN_BASE64_SCHLUESSEL_EINTRAGEN"
      SEMAPHORE_LDAP_ACTIVATED: "no"
      TZ: Europe/Berlin
    depends_on:
      semaphore-db:
        condition: service_healthy

Drei Designentscheidungen dieser Konfiguration verdienen eine kurze Erklärung: Erstens verwendet die Anleitung postgres:14 statt neuerer Tags – das ist das von der offiziellen Semaphore-Dokumentation referenzierte Image und damit die am besten getestete Kombination. Zweitens bewirkt depends_on: condition: service_healthy zusammen mit dem healthcheck auf dem Datenbank-Container, dass Semaphore erst startet, wenn PostgreSQL wirklich bereit ist – Race-Condition-Fehler beim ersten Hochfahren werden so zuverlässig verhindert. Drittens stellt restart: unless-stopped sicher, dass beide Container nach einem NAS-Neustart automatisch wieder starten.

Eine Übersicht aller relevanten Eckdaten:

ParameterWertHinweis
Imagesemaphoreui/semaphore:latestStabile Alternative: v2.18.12 pinnen
DB-Imagepostgres:14Offiziell empfohlen
Host-Port3000Alternativ 4448 bei Kollision
Container-Port3000Intern fest verdrahtet
Image-Größeca. 463 MBamd64 und arm64 unterstützt
DatenbankPostgreSQL (Pflicht für Produktion)BoltDB/SQLite ab v2.16 für Tests
Volume (Host-Pfad)Container-PfadInhalt
/volume1/docker/semaphore/db/var/lib/postgresql/dataPostgreSQL-Datenbankdateien
/volume1/docker/semaphore/data/var/lib/semaphoreSemaphore-Laufzeitdaten
/volume1/docker/semaphore/config/etc/semaphoreKonfiguration und requirements.txt
/volume1/docker/semaphore/temp/tmp/semaphoreTemporäre Playbook-Arbeitsdateien

Verifizieren: Überprüfe die YAML-Syntax, bevor du den Stack startest:

cat /volume1/docker/semaphore/compose.yaml

Die Datei sollte vollständig und ohne Tabulatoren als Einrückung ausgegeben werden. Besonders wichtig: Der Wert von SEMAPHORE_ACCESS_KEY_ENCRYPTION darf nicht mehr der Platzhaltertext sein.

Schritt 3: Stack im Container Manager starten

Öffne den Container Manager in DSM. So legst du das Compose-Projekt an (ausführliche Grundlagen: Container Manager: Docker-Compose-Projekt anlegen, starten und aktualisieren):

  1. Wähle im linken Menü Projekt und klicke auf Erstellen.
  2. Vergib den Projektnamen semaphore (Kleinschreibung).
  3. Wähle als Pfad /volume1/docker/semaphore – Container Manager erkennt die compose.yaml automatisch.
  4. Klicke auf Weiter und dann auf Ausführen. DSM lädt die Images und startet beide Container.

Der erste Start dauert länger, da postgres:14 (ca. 90 MB) und semaphoreui/semaphore:latest (ca. 463 MB) heruntergeladen werden müssen. Beim allerersten Hochfahren installiert Semaphore außerdem Ansible aus der requirements.txt – das kann weitere ein bis zwei Minuten in Anspruch nehmen.

Wichtig: Die Bootstrap-Variablen SEMAPHORE_ADMIN* werden nur beim ersten Start ausgewertet. Nach der Erstinitialisierung lässt sich das Admin-Passwort ausschließlich über die Web-UI ändern.

Verifizieren: Prüfe den Status beider Container im Container Manager – beide müssen den Status Aktiv (grüner Punkt) zeigen. Zusätzlich kannst du per SSH die Logs einsehen:

docker logs Semaphore-UI --tail 30

Erwartete Ausgabe: Zeilen wie Loading config, Postgres connected und Server is running. Fehlermeldungen wie connection refused oder encryption key is not set deuten auf Konfigurationsprobleme hin (siehe Troubleshooting).

Schritt 4: Erster Login und Passwort ändern

Öffne deinen Browser und rufe http://NAS-IP:3000 auf. Melde dich mit dem Bootstrap-Admin an:

  • Benutzername: admin (Wert aus SEMAPHORE_ADMIN)
  • Passwort: der Wert aus SEMAPHORE_ADMIN_PASSWORD

Nach dem ersten Login solltest du das Admin-Passwort sofort in der Web-UI ändern: Klicke oben rechts auf den Benutzernamen → Edit ProfileChange Password. Setze außerdem deine echte E-Mail-Adresse ein, damit Benachrichtigungen korrekt zugestellt werden können.

Für den dauerhaften Zugang empfiehlt sich ein Reverse Proxy über das DSM-Anwendungsportal. Das macht HTTPS möglich und erlaubt einen sauberen Hostnamen statt einer IP. Aktiviere dabei unbedingt die WebSocket-Unterstützung (Upgrade- und Connection-Header), da das Live-Log-Streaming im Browser WebSocket-Verbindungen verwendet. Eine detaillierte Anleitung findest du unter Nginx Proxy Manager auf der Synology mit Container Manager einrichten.

Verifizieren: Du siehst nach dem Login das Semaphore-Dashboard mit den Menüpunkten Projects, Key Store, Team Members und Activity. Die Versionsnummer wird unten links angezeigt und sollte v2.18.x lauten.

Schritt 5: SSH-Key hinterlegen

Ansible benötigt einen SSH-Schlüssel, um sich bei Zielhosts anzumelden. Semaphore speichert private Keys verschlüsselt in der PostgreSQL-Datenbank – kein Plaintext auf dem Dateisystem. Navigiere im Web-UI zu Key StoreNew Key:

  1. Key Name: z. B. ansible-homelab
  2. Type: SSH Key
  3. Füge deinen privaten SSH-Schlüssel (ed25519 empfohlen) in das Textfeld ein – der komplette Inhalt deiner ~/.ssh/id_ed25519-Datei inklusive der -----BEGIN OPENSSH PRIVATE KEY------Zeilen.
  4. Optional: Passphrase, falls der Key damit gesichert ist.
  5. Klicke auf Save.

Du hast noch kein SSH-Keypair? Erstelle eines auf dem NAS per SSH:

ssh-keygen -t ed25519 -C "semaphore-ansible" -f ~/.ssh/id_ed25519_semaphore
cat ~/.ssh/id_ed25519_semaphore.pub

Kopiere den Public Key auf den Zielhost:

ssh-copy-id -i ~/.ssh/id_ed25519_semaphore.pub benutzer@ZIELHOST-IP

Weitere Details zu SSH-Keys und sicherer Konfiguration findest du in SSH-Hardening Deep-Dive: sshd_config, Match-Blöcke und ssh-audit für KMU-Server.

Verifizieren: Im Key Store erscheint der neue Eintrag mit dem Typ SSH Key und einem grünen Häkchen. Der private Schlüssel ist nicht mehr im Klartext lesbar – das zeigt, dass die Verschlüsselung per SEMAPHORE_ACCESS_KEY_ENCRYPTION funktioniert.

Schritt 6: Inventory, Repository und erstes Playbook einrichten

Bevor du einen Task starten kannst, müssen drei weitere Objekte in Semaphore angelegt werden: ein Projekt, ein Inventory und ein Repository. Navigiere zunächst zu ProjectsNew Project und vergib einen Namen, z. B. Homelab.

Innerhalb des Projekts:

  1. Inventory anlegen: Wähle InventoryNew Inventory. Wähle den Typ Static und trage die IP-Adresse deines Zielhosts ein:
[linux]
192.168.1.100 ansible_user=ubuntu
  1. Repository anlegen: Wähle RepositoriesNew Repository. Trage eine Git-URL ein oder nutze für einen schnellen Test den Pfad /tmp/semaphore als Local Path. Für ein lokales Test-Playbook legst du es direkt unter /volume1/docker/semaphore/temp/ ab – dieses Verzeichnis ist als Volume in /tmp/semaphore gemountet.
  2. Task Template erstellen: Wähle Task TemplatesNew Template. Wähle Inventory, Repository und SSH-Key aus den Dropdowns. Trage den Dateinamen des Playbooks ein, z. B. ping.yml.

Ein einfaches Test-Playbook zum Ablegen unter /volume1/docker/semaphore/temp/ping.yml:

---
- name: Ping-Test
  hosts: linux
  gather_facts: false
  tasks:
    - name: Ping
      ansible.builtin.ping:

Starte das Template über Run und beobachte das Live-Log. Das Ergebnis zeigt dir in Echtzeit, ob Ansible den Zielhost erreicht und der SSH-Key akzeptiert wurde.

Semaphore kann auch als Automatisierungszentrale für regelmäßige Server-Updates dienen – kombiniert mit einem einfachen Update-Playbook und dem eingebauten Scheduler ersetzt das aufwändige manuelle SSH-Runden. Für die Grundlagen der Skript-Automatisierung empfiehlt sich ein Blick auf Bash-Skripting für Admins – die Grundlagen. Wer Playbooks direkt auf dem NAS entwickeln und testen möchte, findet in code-server auf dem Synology NAS eine komfortable VS-Code-Oberfläche im Browser.

Verifizieren: Im Live-Log erscheinen grüne ok-Zeilen für den Ping-Task und am Ende PLAY RECAP mit failed=0. Ein roter UNREACHABLE-Eintrag deutet auf ein SSH- oder Netzwerkproblem hin – prüfe dann den SSH-Key und die Inventory-IP.

Schritt 7: DSM-Firewall freigeben (optional, aber empfohlen)

Wenn die DSM-Firewall aktiv ist, muss Port 3000 TCP für das lokale Netz freigegeben werden. Die ausführliche Anleitung dazu: DSM-Firewall auf dem Synology NAS: Ports für Docker-Container freigeben. Kurz zusammengefasst: Systemsteuerung → Sicherheit → Firewall → Regel hinzufügen → Port 3000, Protokoll TCP, Quelle: dein lokales Subnetz (z. B. 192.168.1.0/24).

Nutzt du statt Port 3000 den alternativen Port 4448 (sinnvoll, falls Port 3000 durch ein anderes DSM-Paket belegt ist), passe das Port-Mapping in der compose.yaml entsprechend an: "4448:3000".

Verifizieren: Öffne Semaphore von einem anderen Gerät im gleichen Netz aus dem Browser. Ist der Login erreichbar, ist die Firewall-Regel korrekt gesetzt.

Troubleshooting / Typische Fehler

  • „encryption key is not set" / SSH-Keys lassen sich nicht speichern: SEMAPHORE_ACCESS_KEY_ENCRYPTION ist leer oder enthält noch den Platzhaltertext. Generiere den Schlüssel mit head -c32 /dev/urandom | base64 und trage ihn in der compose.yaml ein. Achtung: Wird der Schlüssel nach dem ersten Start geändert, sind alle bereits gespeicherten Keys unlesbar.
  • „connection refused" beim Start von Semaphore-UI: PostgreSQL war noch nicht bereit. Überprüfe, ob der healthcheck-Block im semaphore-db-Service korrekt eingetragen ist und ob depends_on: condition: service_healthy beim semaphore-Service gesetzt ist.
  • „ansible: command not found" beim ersten Playbook-Run: Das offizielle :latest-Image enthält kein Ansible. Stelle sicher, dass /volume1/docker/semaphore/config/requirements.txt die Zeile ansible enthält und das Verzeichnis korrekt als Volume gemountet ist. Starte den Container danach neu.
  • PostgreSQL-Container startet nicht: Das Host-Verzeichnis /volume1/docker/semaphore/db existiert nicht oder hat falsche Rechte. Lege es mit mkdir -p und chmod 755 neu an und starte den Stack erneut.
  • „no such host" beim DB-Verbindungsversuch: SEMAPHORE_DB_HOST muss exakt mit dem hostname-Wert des Datenbank-Containers übereinstimmen. In dieser Anleitung ist das semaphore-db – ein Tippfehler hier führt zu DNS-Auflösungsfehlern innerhalb des Docker-Netzwerks.
  • Live-Log-Streaming bricht nach wenigen Sekunden ab: Der Reverse Proxy überträgt die WebSocket-Verbindung nicht weiter. Im DSM-Anwendungsportal müssen die Header Upgrade: $http_upgrade und Connection: upgrade gesetzt sein – dafür gibt es einen expliziten WebSocket-Schalter.
  • YAML-Parse-Fehler beim Stack-Start: Passwörter mit Sonderzeichen wie !, @ oder # müssen in der compose.yaml in doppelte Anführungszeichen eingeschlossen sein, sonst interpretiert der YAML-Parser sie als Sonderzeichen.
  • Port 3000 bereits belegt: Manche DSM-Versionen nutzen Port 3000 für Node.js-Pakete. Ändere das Port-Mapping auf "4448:3000" und passe die Firewall-Regel entsprechend an.

Häufige Fragen

Kann ich Semaphore ohne PostgreSQL betreiben?

Ja – für lokale Tests reicht BoltDB (eingebettete Datenbank, kein separater Container nötig). Setze dazu SEMAPHORE_DB_DIALECT: bolt und entferne den semaphore-db-Service aus der Compose-Datei. Ab Semaphore v2.16 ist außerdem SQLite als dritte Option verfügbar. Für dauerhaften Produktivbetrieb auf dem NAS ist PostgreSQL jedoch die richtige Wahl: Die Daten lassen sich mit pg_dump sichern, parallele Schreibzugriffe werden korrekt behandelt, und das Datenbankverzeichnis lässt sich mit Hyper Backup einschließen.

Wie sichere ich die Semaphore-Daten?

Drei Bereiche müssen gesichert werden: Erstens das Verzeichnis /volume1/docker/semaphore/db/ für die PostgreSQL-Daten (am besten per Hyper Backup auf eine externe Quelle). Zweitens die Verzeichnisse /volume1/docker/semaphore/config/ und /volume1/docker/semaphore/data/ für die App-Konfiguration. Drittens – und das ist kritisch – den Wert von SEMAPHORE_ACCESS_KEY_ENCRYPTION separat und sicher aufbewahren, zum Beispiel in einem Passwortmanager. Ohne diesen Schlüssel sind Datenbank-Backups zwar vorhanden, aber alle gespeicherten SSH-Keys und Passwörter sind nicht wiederherstellbar.

Wie richte ich HTTPS mit eigenem Hostnamen ein?

Öffne in DSM die Systemsteuerung → Anwendungsportal → Reverse-Proxy und lege eine neue Regel an: Quelle HTTPS auf Port 443 (oder deiner Synology-DDNS-Adresse), Ziel HTTP localhost:3000. Aktiviere den WebSocket-Schalter. Setze anschließend in der compose.yaml die Variable SEMAPHORE_WEB_ROOT: "https://dein-hostname.de", damit Links in E-Mail-Benachrichtigungen korrekt sind.

Welche Ansible-Version wird installiert?

Die requirements.txt mit dem Inhalt ansible installiert die jeweils aktuelle Version aus PyPI. Willst du eine bestimmte Version pinnen, schreibe z. B. ansible==10.7.0. Ansible-Collections und weitere Python-Pakete kannst du zusätzlich eintragen, z. B. netaddr für IP-Berechnungen in Playbooks.

Wie aktualisiere ich Semaphore auf eine neue Version?

Ändere in der compose.yaml den Image-Tag auf die gewünschte Version (oder behalte :latest) und führe im Container Manager einen Pull durch: Projekt auswählen → AktionNeuerstellen. Semaphore führt beim Neustart automatisch Datenbankmigrationen durch. Erstelle vor dem Update ein Backup des db-Verzeichnisses.

Kann Semaphore auch Terraform-Pläne ausführen?

Ja – Semaphore unterstützt neben Ansible auch Terraform, OpenTofu, Terragrunt und PowerShell als Task-Runner. Dazu muss das jeweilige Binary im Container vorhanden sein, entweder über ein angepasstes Image oder über eine erweiterte requirements.txt. In der Task-Template-Konfiguration wählst du dann den entsprechenden Runner statt Ansible aus.

Fazit

Semaphore UI macht das Synology NAS zur ernstgemeinten Automatisierungszentrale für kleine IT-Abteilungen und Heimlab-Admins. Das Compose-Projekt mit PostgreSQL ist in unter 45 Minuten aufgesetzt, der Admin-Bootstrap über Umgebungsvariablen spart manuelle Erstkonfiguration, und der healthcheck auf dem Datenbank-Container verhindert den häufigsten Anfängerfehler. Der einzige echte Stolperstein ist die fehlende Ansible-Installation im offiziellen Image – wer die requirements.txt anlegt, hat dieses Problem aber schnell gelöst. Wer darüber hinausgehende Infrastruktur-Automatisierung plant, findet in Semaphore ein solides Fundament: SSH-Keys verschlüsselt in der DB, Git-basierte Playbook-Verwaltung, Benutzer- und Team-Rechte, Benachrichtigungen und ein Scheduler für regelmäßige Jobs – alles ohne externe CI/CD-Plattform.

Weiterführende Anleitungen und Quellen

Offizielle Quellen: Semaphore UI Docker-Installationsdokumentation · Semaphore UI GitHub-Repository · semaphoreui/semaphore auf Docker Hub