Koel mit Docker installieren: Schlanker persönlicher Musik-Streamingserver
Koel ist mit 17.000+ Stars der eleganteste selbst gehostete Musik-Streamingserver: Laravel-Backend, Vue.js-Frontend, Smart Playlists und Last.fm-Scrobbling – kostenlos. Diese Anleitung zeigt, wie du Koel per Docker Compose in 20 Minuten startest.

Spotify und Apple Music sind bequem – aber sie kennen jeden Song, den du hörst, und du zahlst monatlich für Musik, die dir nicht gehört. Koel dreht dieses Modell um: Du betreibst den Server selbst, deine MP3- und FLAC-Dateien bleiben auf deiner eigenen Hardware, und das Interface sieht dabei besser aus als die meisten kommerziellen Alternativen. Das Laravel-PHP-Backend kombiniert sich mit einem Vue.js/TypeScript-Frontend zu einer Streaming-App, die Smart Playlists, Last.fm-Scrobbling, Podcast-Import und sogar einen KI-Assistenten für Sprachbefehle mitbringt – alles in der kostenlosen Community-Edition. Mit 17.200 GitHub-Stars und über 160 Releases ist Koel aktiv gepflegt und produktionsreif. Diese Anleitung richtet sich an alle, die eine datenschutzfreundliche Spotify-Alternative für die eigene Musiksammlung auf einem Linux-Host, einer VM oder einem NAS mit Docker betreiben möchten.
Voraussetzungen
- Docker Engine 24+ und das Docker Compose Plugin v2 (
docker composeohne Bindestrich) sind auf dem Host installiert – wie das geht, erklärt die Grundanleitung Docker und Docker Compose auf Linux installieren. - Linux-Host, VM oder NAS mit Docker-Unterstützung (Ubuntu, Debian, Raspberry Pi OS, etc.) – auch
linux/arm64undlinux/arm/v7werden vom Multi-Arch-Image abgedeckt. - Mindestens 1 GB RAM (2 GB empfohlen bei größeren Bibliotheken); mindestens 10 GB freier Speicher für Images, Datenbank und Suchindex.
- Eine Musiksammlung als MP3-, FLAC-, AAC-, OGG- oder WAV-Dateien auf dem Host.
- Optional: Domain oder feste IP für externen Zugriff; für HTTPS einen vorgeschalteten Reverse Proxy (Caddy, Traefik oder Nginx) – Koel selbst liefert kein TLS.
Schritt 1: Projektordner anlegen und Eckdaten überblicken
Leg zunächst ein sauberes Verzeichnis für den Koel-Stack an. Alle Compose-Dateien und Secrets landen hier, und du behältst den Überblick beim späteren Update.
sudo mkdir -p /opt/koel
cd /opt/koelHier die wichtigsten technischen Eckdaten auf einen Blick:
| Eigenschaft | Wert |
|---|---|
| Image | phanan/koel:latest (aktuell v9.8.0) |
| Registrierung | Docker Hub (hub.docker.com/r/phanan/koel) |
| Architekturen | linux/amd64, linux/arm64, linux/arm/v7 |
| Image-Größe | ~409 MB (amd64), ~386 MB (arm64) |
| Webinterface-Port | 80 (HTTP, kein TLS im Container) |
| Empfohlene Datenbank | MariaDB 10.11 |
| Lizenz | MIT (Community Edition kostenlos) |
| Volume | Zweck |
|---|---|
./music:/music | Deine Musikdateien (Host-Pfad einbinden) |
koel_images | Album-Cover und Avatare (persistent) |
koel_search | Suchindex (sonst Neuaufbau nach Neustart) |
koel_db | MariaDB-Datenbankdaten |
Verifizieren: Das Verzeichnis existiert und ist leer.
ls /opt/koel
# Erwartet: leeres Verzeichnis (keine Fehlermeldung)Schritt 2: .env-Datei mit Secrets anlegen
Secrets gehören nie direkt in die compose.yaml. Leg eine .env-Datei an, die Docker Compose automatisch einliest. Ersetze die Platzhalterwerte durch sichere, zufällige Passwörter – mindestens 20 Zeichen, gemischt.
# /opt/koel/.env
# Datenbank-Passwörter (bitte durch sichere Werte ersetzen!)
DB_PASSWORD=SICHERES_PASSWORT_HIER
DB_ROOT_PASSWORD=ROOT_PASSWORT_HIER
# Öffentliche URL der Koel-Instanz
APP_URL=http://192.168.1.100
# APP_KEY: Leer lassen – wird beim ersten Start automatisch generiert.
# Nach dem ersten Start auslesen und hier dauerhaft eintragen!
APP_KEY=
# HTTPS hinter Reverse Proxy?
FORCE_HTTPS=false
# Optional: Last.fm-Scrobbling
# LASTFM_API_KEY=dein_api_key
# LASTFM_API_SECRET=dein_api_secret
# Optional: Spotify Cover-Bilder
# SPOTIFY_CLIENT_ID=deine_client_id
# SPOTIFY_CLIENT_SECRET=dein_client_secretSchränke die Dateiberechtigung direkt ein, damit andere Benutzer die Passwörter nicht lesen können:
chmod 600 /opt/koel/.envVerifizieren: Die Datei ist angelegt und nur für den eigenen Benutzer lesbar.
ls -la /opt/koel/.env
# Erwartet: -rw------- 1 root root ... .envSchritt 3: compose.yaml erstellen
Die folgende compose.yaml definiert zwei Services: den Koel-Applikationscontainer und MariaDB 10.11 als Datenbank. Ein Healthcheck stellt sicher, dass Koel erst startet, nachdem MariaDB vollständig bereit ist – ohne diesen Mechanismus schlägt der erste Start regelmäßig fehl.
# /opt/koel/compose.yaml
services:
koel:
image: phanan/koel:latest
container_name: koel
restart: unless-stopped
depends_on:
database:
condition: service_healthy
ports:
- "80:80"
environment:
- DB_CONNECTION=mysql
- DB_HOST=database
- DB_PORT=3306
- DB_USERNAME=koel
- DB_PASSWORD=${DB_PASSWORD}
- DB_DATABASE=koel
- APP_URL=${APP_URL:-http://localhost}
- APP_KEY=${APP_KEY}
- FORCE_HTTPS=${FORCE_HTTPS:-false}
- MEMORY_LIMIT=512
- SCAN_JOBS=4
# Optional: Last.fm Scrobbling
# - LASTFM_API_KEY=${LASTFM_API_KEY}
# - LASTFM_API_SECRET=${LASTFM_API_SECRET}
# Optional: Spotify Cover-Bilder
# - SPOTIFY_CLIENT_ID=${SPOTIFY_CLIENT_ID}
# - SPOTIFY_CLIENT_SECRET=${SPOTIFY_CLIENT_SECRET}
volumes:
- ./music:/music
- koel_images:/var/www/html/storage/app/public/images
- koel_search:/var/www/html/storage/search-indexes
database:
image: mariadb:10.11
container_name: koel_db
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
- MYSQL_DATABASE=koel
- MYSQL_USER=koel
- MYSQL_PASSWORD=${DB_PASSWORD}
volumes:
- koel_db:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--su-mysql", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
volumes:
koel_db:
driver: local
koel_images:
driver: local
koel_search:
driver: localEin Hinweis zum Music-Volume: Das Verzeichnis ./music wird relativ zum Compose-Verzeichnis eingebunden. Du kannst stattdessen einen absoluten Pfad zu deiner bestehenden Musiksammlung angeben, z. B. /mnt/nas/musik:/music:ro (mit :ro für Read-Only, wenn Koel nichts schreiben soll).
Verifizieren: Die Syntax der Compose-Datei ist fehlerfrei.
docker compose config
# Erwartet: Fehlerfreie Ausgabe der gemergten Konfiguration ohne WarnungenSchritt 4: Stack starten und APP_KEY dauerhaft sichern
Jetzt startest du den Stack. Beim ersten Start lädt Docker die Images (~409 MB), initialisiert MariaDB und führt die Laravel-Datenbankmigrationen aus. Das dauert je nach Verbindung 1–3 Minuten.
docker compose up -dBeobachte die Logs, bis Koel vollständig gestartet ist:
docker compose logs -f koelWarte, bis du in den Logs eine Zeile siehst wie Apache ... configured -- resuming normal operations oder den Hinweis, dass die Datenbankmigrationen abgeschlossen sind. Dann kannst du den Log-Stream mit Strg+C beenden.
Jetzt kommt der wichtigste Schritt: den generierten APP_KEY dauerhaft sichern. Wird er nicht gespeichert, generiert Koel bei jedem Neustart einen neuen Schlüssel – alle bestehenden Sessions werden ungültig und verschlüsselte Daten sind verloren.
docker exec koel php artisan key:generate --show
# Ausgabe: base64:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=Kopiere den ausgegebenen Schlüssel (einschließlich base64:) und trage ihn in der .env-Datei ein:
APP_KEY=base64:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=Damit der neue Wert aktiv wird, starte den Koel-Container neu:
docker compose up -d koelVerifizieren: Beide Container laufen, MariaDB ist gesund, und der HTTP-Endpunkt antwortet.
docker compose ps
# Erwartet:
# NAME IMAGE STATUS PORTS
# koel phanan/koel:latest Up X minutes 0.0.0.0:80->80/tcp
# koel_db mariadb:10.11 Up X minutes (healthy)
curl -I http://localhost
# Erwartet: HTTP/1.1 200 OK oder 302 FoundSchritt 5: Ersteinrichtung im Browser und Passwort ändern
Öffne http://<server-ip> im Browser. Du siehst den Koel-Login. Die Standard-Zugangsdaten lauten:
- E-Mail:
admin@koel.dev - Passwort:
KoelIsCool
Diese Kombination ist öffentlich bekannt und muss sofort geändert werden. Ändere das Admin-Passwort über die Kommandozeile:
docker exec -it koel php artisan koel:admin:change-passwordDer Befehl fragt dich interaktiv nach einer neuen E-Mail-Adresse und einem neuen Passwort. Wähle ein starkes Passwort mit mindestens 16 Zeichen.
Verifizieren: Der Login mit dem neuen Passwort funktioniert. Das Interface zeigt ein leeres Dashboard – das ist korrekt, da die Bibliothek noch nicht gescannt wurde.
Schritt 6: Musikbibliothek einbinden und scannen
Koel erwartet die Musikdateien unter /music im Container. Du hast den Pfad bereits in der compose.yaml als Host-Mount definiert. Falls du ein separates Verzeichnis verwenden möchtest, passe den linken Teil des Volume-Eintrags an und starte den Stack neu.
Koel scannt die Bibliothek nicht automatisch. Der erste manuelle Scan ist Pflicht, und bei neuen Dateien muss er wiederholt werden:
docker exec --user www-data koel php artisan koel:syncWichtig: Der Befehl muss mit --user www-data ausgeführt werden, sonst entstehen Berechtigungsprobleme im Storage-Verzeichnis. Bei großen Bibliotheken (über 10.000 Titel) kann der erste Scan mehrere Minuten dauern und spürbar CPU sowie RAM beanspruchen. Du kannst den Scan mit MEMORY_LIMIT=1024 und SCAN_JOBS=8 beschleunigen, falls genug RAM vorhanden ist.
Für automatisches Scanning bei neuen Dateien richtest du einen Cron-Job auf dem Host ein:
# crontab -e (als root)
*/30 * * * * docker exec --user www-data koel php artisan koel:sync >> /var/log/koel-sync.log 2>&1Verifizieren: Nach dem Scan erscheinen deine Songs, Alben und Künstler in der Koel-Oberfläche. Prüfe die Scan-Ausgabe auf Fehler:
docker compose logs koel | tail -20
# Erwartet: Keine PHP-Fehlermeldungen; Sync-Ergebnis zeigt Anzahl neu gefundener TracksSchritt 7: HTTPS über Reverse Proxy einrichten (empfohlen)
Koel selbst liefert kein TLS. Für den Zugriff über das Internet oder aus Sicherheitsgründen im Heimnetz ist ein vorgeschalteter Reverse Proxy Pflicht. Eine vollständige Anleitung findest du unter Caddy als Reverse Proxy mit automatischem HTTPS einrichten.
In der Koel-.env trägst du dann ein:
APP_URL=https://musik.deine-domain.de
FORCE_HTTPS=truePasse außerdem das Port-Mapping in der compose.yaml an, wenn der Reverse Proxy auf demselben Host läuft – dann ist Koel nur intern erreichbar:
ports:
- "127.0.0.1:8080:80"Starte danach den Stack neu:
docker compose up -dVerifizieren: Der Browser zeigt das Schloss-Symbol, und Koel lädt alle Assets über HTTPS ohne Mixed-Content-Warnungen in der Browser-Konsole.
Schritt 8: Updates und Backup
Koel und MariaDB werden mit zwei Befehlen aktualisiert:
cd /opt/koel
docker compose pull
docker compose up -dNach einem Update führt Koel beim Start automatisch ausstehende Datenbankmigrationen aus – kein manueller Eingriff nötig, solange SKIP_INIT=false (Standard) gesetzt ist.
Für ein vollständiges Backup sicherst du drei Dinge:
- Die
.env-Datei (enthältAPP_KEYund Passwörter – verschlüsselt aufbewahren!) - Den MariaDB-Dump:
docker exec koel_db mariadb-dump -u koel -p<DB_PASSWORD> koel > koel_backup.sql - Das Volume
koel_images(Album-Cover):docker run --rm -v koel_images:/data -v $(pwd):/backup alpine tar czf /backup/koel_images.tar.gz /data
Die Musikdateien selbst sicherst du separat als Teil deiner normalen Backup-Strategie – eine Anleitung bietet 3-2-1-Backup-Strategie umsetzen.
Verifizieren: Nach dem Update laufen beide Container, und die Weboberfläche ist erreichbar.
docker compose ps
# Erwartet: beide Container mit Status "Up" und koel_db "(healthy)"Troubleshooting / Typische Fehler
SQLSTATE[HY000] [2002] Connection refused: Koel startete, bevor MariaDB bereit war. Ursache ist ein fehlendes oder zu kurzes Healthcheck-start_period. Stelle sicher, dassdepends_onmitcondition: service_healthygesetzt ist. Mitdocker compose restart koelerzwingst du einen erneuten Startversuch.- Port 80 bereits belegt (
bind: address already in use): Auf dem Host läuft bereits ein Webserver. Ändere das Port-Mapping in dercompose.yamlauf einen freien Port, z. B."8080:80", danndocker compose up -d. - Musik nach dem Scan nicht sichtbar: Der Scan wurde nicht mit
--user www-dataausgeführt oder das/music-Verzeichnis ist für den Container nicht lesbar (UID 33 = www-data). Fix:chmod o+rx /pfad/zur/musikauf dem Host, dann Scan wiederholen. - Mixed-Content-Fehler im Browser: Koel läuft hinter HTTPS-Proxy, aber
FORCE_HTTPS=trueist nicht gesetzt. Laravel generiert interne HTTP-URLs. Fix:FORCE_HTTPS=truein die.enveintragen unddocker compose up -dausführen. - Sessions nach Neustart ungültig:
APP_KEYwurde nicht dauerhaft in der.envgespeichert. Koel generierte einen neuen Schlüssel. Fix:docker exec koel php artisan key:generate --show, Ausgabe in.enveintragen, Container neu starten. - Standard-Passwort nicht geändert:
admin@koel.dev / KoelIsCoolist öffentlich bekannt. Sofort ausführen:docker exec -it koel php artisan koel:admin:change-password. - Hohe CPU-Last beim ersten Scan: Normales Verhalten bei großen Bibliotheken. Reduziere
SCAN_JOBS=2in der Umgebung, um die Last zu begrenzen, oder starte den Scan zu einem Zeitpunkt geringer Auslastung. - MariaDB 11.x statt 10.11: Neuere MariaDB-Versionen können SQL-Kompatibilitätsprobleme verursachen. Bleibe bei
mariadb:10.11, bis eine offizielle Freigabe für höhere Versionen vorliegt.
Häufige Fragen
Welche Audioformate unterstützt Koel?
Koel unterstützt alle gängigen Formate: MP3, FLAC, AAC, OGG, WAV und WMA. FLAC wird standardmäßig per ffmpeg zu MP3 mit 128 kbps transcodiert (TRANSCODE_FLAC=true). ffmpeg ist bereits im Container enthalten. Die Bitrate lässt sich mit TRANSCODE_BIT_RATE=320 erhöhen.
Funktioniert Koel auf einem Raspberry Pi?
Ja. Das offizielle Image phanan/koel ist Multi-Arch und unterstützt linux/arm64 und linux/arm/v7. Ein Raspberry Pi 4 oder 5 mit 4 GB RAM betreibt Koel problemlos. Auf einem Pi 3 mit 1 GB RAM ist der Betrieb knapp möglich – MEMORY_LIMIT=256 und SCAN_JOBS=1 empfehlen sich dann.
Was ist der Unterschied zwischen Community Edition und Koel Plus?
Die kostenlose Community Edition (MIT-Lizenz) bietet den vollen Funktionsumfang für einen Benutzer: Smart Playlists, Last.fm-Scrobbling, Podcasts, KI-Assistent, Spotify- und MusicBrainz-Metadaten sowie YouTube-Integration. Koel Plus (Abonnement) schaltet Multi-User-Bibliotheken, Single Sign-On (Google, OIDC), Cloud-Storage-Treiber (S3, Dropbox, SFTP, WebDAV), eigene Themes und White-Labeling frei. Für den persönlichen Heimgebrauch reicht die kostenlose Edition vollständig aus.
Wie richte ich Last.fm-Scrobbling ein?
Du benötigst eine Last.fm-Developer-App (unter last.fm/api/account/create). Trage API Key und Shared Secret in der .env ein, kommentiere die entsprechenden Zeilen in der compose.yaml aus und starte den Container neu. Die Verknüpfung mit dem eigenen Last.fm-Account erfolgt dann in den Koel-Benutzereinstellungen im Browser.
Wie aktualisiere ich die Bibliothek automatisch bei neuen Dateien?
Richte einen Cron-Job auf dem Host ein, der docker exec --user www-data koel php artisan koel:sync in regelmäßigen Abständen aufruft. Alternativ kann ein inotifywait-basierter Watcher Änderungen im Musikverzeichnis überwachen und den Scan nur bei tatsächlichen Dateiänderungen auslösen – das spart Ressourcen bei sehr großen Bibliotheken.
Kann ich statt MariaDB PostgreSQL verwenden?
Ja. Tausche den database-Service gegen postgres:16 aus, setze DB_CONNECTION=pgsql und DB_PORT=5432 und verwende die PostgreSQL-Umgebungsvariablen (POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD). Das offizielle Docker-Repository unter github.com/koel/docker bietet ein fertiges docker-compose.postgres.yml-Beispiel.
Fazit
Koel ist eine der elegantesten Self-Hosting-Lösungen überhaupt: Das Interface übertrifft viele kommerzielle Dienste, der Funktionsumfang der kostenlosen Edition ist beeindruckend, und der Docker-Betrieb ist dank des offiziellen Multi-Arch-Images unkompliziert. Der einzige Stolperstein, der in der Praxis immer wieder auftaucht, ist der APP_KEY: Ihn direkt nach dem ersten Start dauerhaft in der .env zu sichern ist der wichtigste Schritt. Wer Koel produktiv betreiben möchte, kombiniert ihn mit einem Reverse Proxy für HTTPS, einem Cron-Job für den automatischen Bibliotheksscan und einem regelmäßigen Datenbank-Dump – dann läuft die persönliche Spotify-Alternative stabil und wartungsarm. Für ein sauber abgesichertes Docker-Setup lohnt sich außerdem ein Blick auf Docker Compose absichern: Secrets, Healthchecks und Non-Root.
Weiterführende Anleitungen und Quellen
- Caddy als Reverse Proxy mit automatischem HTTPS einrichten
- Docker Compose absichern: Secrets, Healthchecks und Non-Root
- 3-2-1-Backup-Strategie umsetzen: Anleitung mit Restic, USB-Disk und S3-Cloud
- Jellyfin mit Docker: eigener Media-Server ohne Abo
Offizielle Quellen: Koel Docker Repository (koel/docker) – die maßgebliche Referenz für alle Docker-Deployments, mit fertigen Compose-Beispielen für MySQL und PostgreSQL. Die allgemeine Koel-Dokumentation unter docs.koel.dev verweist für Docker-Details explizit auf dieses separate Repository.