Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Synology / NAS 11.09.2026 · 10 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 verschlüsselt speichern, Inventories verwalten. Die Anleitung zeigt, wie Sie Semaphore UI als Compose-Projekt mit PostgreSQL auf dem Synology NAS einrichten.

Geprüft am 30.09.2026 · für semaphore 2.19.14, postgres 14

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

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

Ansible-Playbooks per Kommandozeile vom eigenen Laptop zu starten, funktioniert, bis ein Kollege einen Update-Lauf übernehmen soll. Semaphore UI macht Ansible im Browser bedienbar: Playbooks starten, Ausführungshistorie einsehen, SSH-Keys verschlüsselt hinterlegen und Rechte pro Team vergeben. Neben Ansible unterstützt Semaphore auch Terraform, OpenTofu und PowerShell.

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
  • Mindestens 5 GB freier Speicher auf /volume1
  • 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
  • Ein SSH-Schlüsselpaar (ed25519 empfohlen) für Ansible-Verbindungen zum Zielhost

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

Semaphore UI benötigt fünf Host-Verzeichnisse, die vor dem ersten Start existieren müssen. Öffnen Sie eine SSH-Verbindung zum NAS (Details: SSH auf dem Synology NAS aktivieren und verbinden) und legen Sie 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
mkdir -p /volume1/docker/semaphore/playbooks
sudo chown -R 1001:0 /volume1/docker/semaphore/data /volume1/docker/semaphore/config /volume1/docker/semaphore/temp

Der chown-Befehl ist nötig, weil der Semaphore-Container nicht als root, sondern als Benutzer mit der ID 1001 läuft und sonst nicht in seine Ordner schreiben kann. Den Ordner db richtet PostgreSQL beim ersten Start selbst ein; in playbooks legen Sie später Ihre Test-Playbooks ab.

Erzeugen Sie 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 eine 44-stellige Base64-Zeichenkette aus. Speichern Sie sie im Passwortmanager; Sie brauchen sie im nächsten Schritt und für jede Wiederherstellung aus einem Backup.

Ansible (Paket 13.5.0) ist im offiziellen Image bereits in einer eigenen Python-Umgebung installiert. Eine requirements.txt im Ordner config brauchen Sie nur für zusätzliche Python-Pakete wie netaddr; Ansible selbst tragen Sie dort nicht ein.

Verifizieren: Alle fünf Ordner sind vorhanden, drei davon gehören der ID 1001:

ls -ln /volume1/docker/semaphore/

Erwartete Ausgabe: config, data und temp mit Besitzer 1001 0, dazu db und playbooks.

Schritt 2: compose.yaml erstellen

Erstellen Sie die Compose-Datei unter /volume1/docker/semaphore/compose.yaml. Ersetzen Sie die Platzhalter durch eigene Passwörter und den Schlüssel aus Schritt 1. Setzen Sie Werte mit Sonderzeichen in doppelte Anführungszeichen, sonst kann YAML sie falsch lesen.

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: "STARKES_DB_PASSWORT"
      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
      - /volume1/docker/semaphore/playbooks:/playbooks:ro
    environment:
      SEMAPHORE_DB_USER: semaphore
      SEMAPHORE_DB_PASS: "STARKES_DB_PASSWORT"
      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: "STARKES_ADMIN_PASSWORT"
      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

postgres:14 entspricht dem Compose-Beispiel der offiziellen Dokumentation. Über depends_on mit service_healthy startet Semaphore erst, wenn PostgreSQL bereit ist.

Eine Übersicht aller relevanten Eckdaten:

ParameterWertHinweis
Imagesemaphoreui/semaphore:latestAlternativ Version pinnen, z. B. v2.19.14
DB-Imagepostgres:14Wie im offiziellen Compose-Beispiel
Host-Port3000Alternativ 4448 bei Kollision
Container-Port3000Standard, per SEMAPHORE_PORT änderbar
Image-Größeca. 355 MBamd64 und arm64 unterstützt
DatenbankPostgreSQL (für Produktion)Alternativ MySQL oder SQLite
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/semaphoreArbeitsverzeichnis der Tasks (geklonte Repositories)
/volume1/docker/semaphore/playbooks/playbooksLokale Playbooks (nur lesend)

Verifizieren: Prüfen Sie die Datei vor dem Start:

cat /volume1/docker/semaphore/compose.yaml

Die Einrückung besteht nur aus Leerzeichen, und SEMAPHORE_ACCESS_KEY_ENCRYPTION enthält nicht mehr den Platzhalter.

Schritt 3: Stack im Container Manager starten

Öffnen Sie den Container Manager. So legen Sie das Compose-Projekt an (ausführliche Grundlagen: Container Manager: Docker-Compose-Projekt anlegen, starten und aktualisieren):

  1. Wählen Sie im linken Menü Projekt und klicken Sie auf Erstellen.
  2. Vergeben Sie den Projektnamen semaphore (Kleinschreibung).
  3. Wählen Sie als Pfad /volume1/docker/semaphore; der Container Manager erkennt die vorhandene compose.yaml.
  4. Klicken Sie auf Weiter und dann auf Fertig. DSM lädt die Images und startet beide Container.

Der erste Start dauert wegen des Image-Downloads etwas länger.

Wichtig: Die Bootstrap-Variablen SEMAPHORE_ADMIN* werden nur beim ersten Start ausgewertet. Spätere Änderungen an diesen Variablen wirken nicht mehr; das Passwort ändern Sie dann in der Weboberfläche.

Verifizieren: Beide Container zeigen im Container Manager den Status „Wird ausgeführt“. Per SSH sehen Sie die Logs:

docker logs Semaphore-UI --tail 30

Erwartete Ausgabe: Zeilen wie Pinging database on semaphore-db port 5432, Port :3000 und Server is running. Meldungen wie permission denied oder connection refused deuten auf Konfigurationsprobleme hin (siehe Troubleshooting).

Schritt 4: Erster Login und Passwort ändern

Rufen Sie http://NAS-IP:3000 auf und melden Sie sich mit dem beim Start angelegten Admin an:

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

Da das Startpasswort im Klartext in der compose.yaml steht, ändern Sie es nach der ersten Anmeldung in der Weboberfläche über Ihr Benutzerprofil. Hinterlegen Sie dort auch Ihre E-Mail-Adresse.

Für HTTPS mit eigenem Hostnamen richten Sie einen Reverse Proxy ein (siehe FAQ, alternativ Nginx Proxy Manager).

Verifizieren: Nach der Anmeldung erscheint die Semaphore-Oberfläche; die Versionsnummer im Benutzermenü lautet v2.19.x oder neuer.

Schritt 5: SSH-Key hinterlegen

Ansible meldet sich per SSH-Schlüssel an den Zielhosts an; Semaphore speichert ihn verschlüsselt in der Datenbank. Öffnen Sie im Projekt Key Store → New Key:

  1. Key Name: z. B. ansible-homelab
  2. Type: SSH Key
  3. Fügen Sie den kompletten Inhalt der privaten Schlüsseldatei id_ed25519_semaphore ein, inklusive der BEGIN- und END-Zeilen.
  4. Optional: Passphrase, falls der Key damit gesichert ist.
  5. Klicken Sie auf Save.

Noch kein Schlüsselpaar vorhanden? Erstellen Sie eines auf dem NAS per SSH:

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

Kopieren Sie den öffentlichen Schlüssel auf den Zielhost:

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

Mehr zur sicheren SSH-Konfiguration: 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; der private Schlüssel wird beim erneuten Öffnen nicht mehr angezeigt.

Schritt 6: Inventory, Repository und erstes Playbook einrichten

Vor dem ersten Task legen Sie ein Projekt, ein Inventory und ein Repository an. Legen Sie zunächst über New Project ein Projekt an, z. B. Homelab. Den Key Store aus Schritt 5 finden Sie innerhalb dieses Projekts.

Innerhalb des Projekts:

  1. Inventory anlegen: Inventory → New Inventory, Typ Static, IP-Adresse des Zielhosts eintragen:
[linux]
192.168.1.100 ansible_user=ubuntu
  1. Repository anlegen: Repositories → New Repository. Tragen Sie eine Git-URL ein oder für einen schnellen Test den lokalen Pfad /playbooks; dieser zeigt auf /volume1/docker/semaphore/playbooks auf dem NAS.
  2. Task Template erstellen: Task Templates → New Template, Inventory, Repository und SSH-Key auswählen, Dateiname des Playbooks eintragen, z. B. ping.yml.

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

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

Starten Sie das Template über Run. Das Live-Log zeigt, ob Ansible den Zielhost erreicht und der SSH-Key akzeptiert wird. Hinweis: Das Image deaktiviert die Prüfung unbekannter Host-Schlüssel (StrictHostKeyChecking no); betreiben Sie Semaphore deshalb nur in vertrauenswürdigen Netzen.

Update-Playbooks lassen sich per Zeitplaner regelmäßig ausführen und mit code-server auf dem Synology NAS bearbeiten.

Verifizieren: Im Live-Log erscheint für den Ping-Task ok und am Ende PLAY RECAP mit failed=0. UNREACHABLE deutet auf ein SSH- oder Netzwerkproblem hin; prüfen Sie dann SSH-Key und Inventory-IP.

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

Ist die DSM-Firewall aktiv, geben Sie Port 3000 TCP für das lokale Netz frei (DSM-Firewall: Ports für Docker-Container freigeben): Systemsteuerung → Sicherheit → Firewall → Regel hinzufügen → Port 3000, Protokoll TCP, Quelle: Ihr lokales Subnetz (z. B. 192.168.1.0/24).

Ist Port 3000 bereits durch einen anderen Container belegt, ändern Sie das Port-Mapping in der compose.yaml, z. B. auf "4448:3000".

Verifizieren: Die Anmeldeseite ist von einem anderen Gerät im selben Netz erreichbar.

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. Erzeugen Sie den Schlüssel mit head -c32 /dev/urandom | base64 und tragen Sie 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. Prüfen Sie healthcheck und depends_on in der Compose-Datei.
  • permission denied im Semaphore-Log: Die Ordner data, config oder temp gehören nicht der ID 1001. Führen Sie den chown-Befehl aus Schritt 1 aus und starten Sie das Projekt neu.
  • PostgreSQL-Container startet nicht: Das Host-Verzeichnis /volume1/docker/semaphore/db fehlt. Legen Sie es mit mkdir -p an und starten Sie das Projekt erneut.
  • „no such host“ beim DB-Verbindungsversuch: SEMAPHORE_DB_HOST muss dem Hostnamen des Datenbank-Containers entsprechen, hier semaphore-db.
  • Live-Log-Streaming bricht nach wenigen Sekunden ab: Der Reverse Proxy leitet WebSocket nicht weiter. In der Regel unter „Benutzerdefinierte Kopfzeile“ „Erstellen → WebSocket“ wählen.
  • Port 3000 bereits belegt: Port-Mapping z. B. auf "4448:3000" ändern, Firewall-Regel anpassen.

Häufige Fragen

Kann ich Semaphore ohne PostgreSQL betreiben?

Ja. Für Tests und kleine Installationen reicht SQLite: Setzen Sie SEMAPHORE_DB_DIALECT: sqlite, entfernen Sie die übrigen SEMAPHORE_DB_*-Variablen sowie den semaphore-db-Service. Die Datenbankdatei liegt dann in /var/lib/semaphore. Das frühere BoltDB unterstützt das aktuelle Image nicht mehr als Datenbank, nur noch als Migrationsquelle. Für den Dauerbetrieb mit mehreren Nutzern bleibt PostgreSQL die robustere Wahl.

Wie sichere ich die Semaphore-Daten?

Drei Bereiche gehören ins Backup: Erstens die Datenbank, am besten als Dump (docker exec Semaphore-DB pg_dump -U semaphore semaphore > /volume1/backup/semaphore_$(date +%F).sql), da eine Dateikopie der laufenden Datenbank nicht zuverlässig konsistent ist. Zweitens die Verzeichnisse config, data und playbooks unter /volume1/docker/semaphore/. Drittens den Wert von SEMAPHORE_ACCESS_KEY_ENCRYPTION im Passwortmanager; ohne ihn sind gespeicherte SSH-Keys und Passwörter nicht wiederherstellbar.

Wie richte ich HTTPS mit eigenem Hostnamen ein?

Öffnen Sie Systemsteuerung → Anmeldeportal → Erweitert → Reverse Proxy und legen Sie eine Regel an: Quelle HTTPS, Port 443, Ihr Hostname (z. B. Synology-DDNS), Ziel HTTP localhost:3000. Aktivieren Sie unter „Benutzerdefinierte Kopfzeile“ die WebSocket-Vorlage. Setzen Sie anschließend in der compose.yaml SEMAPHORE_WEB_ROOT: "https://semaphore.example.de", damit Links in Benachrichtigungen stimmen.

Welche Ansible-Version wird installiert?

Das Image bringt eine feste Ansible-Version mit (aktuell das Paket 13.5.0); sie ändert sich mit Image-Updates. Zusätzliche Python-Pakete wie netaddr tragen Sie in /volume1/docker/semaphore/config/requirements.txt ein; sie werden bei jedem Containerstart installiert. Ansible-Collections binden Sie über eine collections/requirements.yml im Playbook-Repository ein.

Wie aktualisiere ich Semaphore auf eine neue Version?

Erstellen Sie zuerst einen Datenbank-Dump. Stoppen Sie dann das Projekt, laden Sie unter „Image“ die neue Version von semaphoreui/semaphore herunter (oder ändern Sie den gepinnten Tag in der compose.yaml) und erstellen Sie das Projekt neu. Datenbankmigrationen laufen beim Start automatisch.

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

Ja. Terraform, OpenTofu und Terragrunt sind im offiziellen Image bereits enthalten. Für PowerShell gibt es eigene Image-Tags mit dem Zusatz -powershell. Im Task Template wählen Sie den passenden Typ statt Ansible.

Fazit

Semaphore UI macht das Synology NAS zur Automatisierungszentrale für kleine IT-Abteilungen. Das Compose-Projekt ist in unter einer Stunde eingerichtet; der wichtigste Stolperstein sind die Ordnerrechte für die Benutzer-ID 1001. Danach stehen verschlüsselte SSH-Keys, Git-basierte Playbooks, Team-Rechte, Benachrichtigungen und ein Zeitplaner bereit, 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