Zum Inhalt

Docker Compose mit include übersichtlich in mehrere Dateien aufteilen

Veröffentlicht am 8. September 2026 · Geschätzte Lesezeit: 10 Minuten

Mit include bindest du vollständige Compose-Dateien als eigenständige Bausteine in ein Hauptprojekt ein. Compose übernimmt deren Dienste, Netzwerke und Volumes in das gemeinsame Anwendungsmodell; relative Pfade bleiben dabei auf den jeweiligen Baustein bezogen.[1][2] So kannst du einen gewachsenen Stack ordnen, ohne bei jedem Befehl mehrere -f-Optionen anzugeben.[3] Plane etwa 30 bis 45 Minuten für Sicherung, Aufteilung und Funktionsprüfung ein.

Voraussetzungen

  • Ein Linux-Server oder NAS mit Docker Engine und dem Docker-Compose-Plugin ab Version 2.20.3
  • Ein bereits funktionierendes Compose-Projekt mit einer compose.yaml
  • Terminalzugriff auf den Projektordner
  • Ein aktuelles Backup der Compose-Datei und aller wichtigen Anwendungsdaten

1. Projekt und Compose-Version prüfen

Wechsle in den Ordner deines vorhandenen Compose-Projekts. Ersetze <projektordner> durch dessen vollständigen Pfad:

cd <projektordner>

Zeige die installierte Compose-Version an:

docker compose version

Die aktuelle Docker-Anleitung nennt Docker Compose 2.20.3 oder neuer als Voraussetzung für include.[2] Ist die installierte Version älter oder meldet Docker, dass compose kein gültiger Befehl ist, aktualisiere zuerst das offizielle Docker-Compose-Plugin passend zu deinem Betriebssystem.

Prüfe danach die noch unveränderte Konfiguration:

docker compose config --quiet

docker compose config löst die Compose-Dateien und Variablen zum tatsächlichen Anwendungsmodell auf; --quiet prüft nur und unterdrückt die normale Ausgabe. Behebe vorhandene Fehler, bevor du Dateien verschiebst.

Zeige außerdem die definierten Dienste und den aktuellen Containerzustand:

docker compose config --services
docker compose ps --all

Notiere Dienstnamen, veröffentlichte Ports und den normalen Nutzerzugang. Diese Angaben brauchst du für den Vergleich nach der Umstellung.

2. Konfiguration und Daten sichern

Erstelle eine datierte Sicherung des gesamten Projektordners. Wechsle dafür zuerst in dessen übergeordneten Ordner:

cd ..

Ersetze <projektname> durch den tatsächlichen Namen des Compose-Projektordners:

cp -a <projektname> "<projektname>.vor-include-$(date +%F-%H%M)"

Wechsle anschließend wieder in das Projekt:

cd <projektname>

Schreibe zusätzlich das bisher aufgelöste Modell in eine Vergleichsdatei:

docker compose config > compose-resolved-before.yaml

<projektname> ist in allen drei Befehlen derselbe vorhandene Ordnername. cp -a erhält Unterordner und Dateiattribute. Die Kopie ist nur dann ein ausreichendes Datenbackup, wenn alle Bind-Mounts innerhalb dieses Projektordners liegen. Sichere Datenbanken, externe Pfade und benannte Volumes zusätzlich mit dem dokumentierten Verfahren der jeweiligen Anwendung.

Keine Volumes löschen

Verwende weder beim Umbau noch beim Rückweg docker compose down -v. Die Option -v entfernt benannte Volumes und kann damit dauerhafte Anwendungsdaten löschen.[9]

3. Geeignete Grenze für den Baustein wählen

Lagere eine zusammengehörige, bereits funktionierende Komponente aus, beispielsweise einen Webdienst samt eigenem Konfigurationsordner oder eine Datenbank samt Verwaltungswerkzeug. Eine eingebundene Datei ist keine lose Textvorlage, sondern ein eigenes Compose-Anwendungsmodell. Nach dem Laden kopiert Compose dessen Ressourcen in das Hauptmodell.[1][4]

Für diese Anleitung dient ein kleiner Nginx-Webdienst als ungefährliches Muster. Das Hauptprojekt enthält später einen echten Funktionstest, der den eingebundenen Dienst über seinen Compose-Dienstnamen web aufruft.

Lege die benötigten Unterordner an:

mkdir -p web/html

Die geplante Struktur sieht so aus:

<projektordner>/
├── compose.yaml
├── compose-resolved-before.yaml
└── web/
    ├── compose.yaml
    └── html/
        └── index.html

<projektordner> steht nur in der Darstellung für deinen vorhandenen Projektordner und wird nicht als Ordner mit spitzen Klammern angelegt. Der Unterordner web ist der eigenständige Baustein; web/html enthält die Testseite.

4. Testinhalt im Baustein anlegen

Erstelle eine harmlose HTML-Datei:

nano web/html/index.html

Füge folgenden Inhalt ein:

<!doctype html>
<html lang="de">
  <head>
    <meta charset="utf-8">
    <title>Compose-Include-Test</title>
  </head>
  <body>
    <h1>Der eingebundene Webdienst funktioniert.</h1>
  </body>
</html>

Speichere die Datei und schließe den Editor. Sie enthält keine Zugangsdaten und verändert keine Anwendungsdaten. Im späteren Nginx-Dienst wird der Ordner nur lesbar eingebunden.

<html>, <head>, <meta>, <title>, <body> und <h1> sind feste HTML-Elemente des Beispiels, keine zu ersetzenden Platzhalter. Nur der sichtbare Text zwischen den öffnenden und schließenden Elementen kann nach Wunsch geändert werden.

Wenn du ein vorhandenes Projekt aufteilst, kopierst oder verschiebst du stattdessen die bereits geprüften Konfigurationsdateien des gewählten Dienstes in dessen Unterordner. Ändere beim ersten Umbau möglichst nicht gleichzeitig Image-Versionen, Ports und Anwendungsoptionen.

5. Eigenständige Compose-Datei erstellen

Öffne die Compose-Datei des neuen Bausteins:

nano web/compose.yaml

Füge dieses vollständige Beispiel ein:

services:
  web:
    image: nginx:1.29-alpine
    restart: unless-stopped
    ports:
      - "127.0.0.1:${WEB_PORT:-8080}:80"
    volumes:
      - ./html:/usr/share/nginx/html:ro

Die Werte bedeuten:

  • web ist der Compose-Dienstname. Andere Dienste im gemeinsamen Modell erreichen ihn unter diesem Namen.
  • nginx:1.29-alpine ist der Beispiel-Image-Tag. Prüfe vor einem produktiven Einsatz, ob er zu deiner Plattform und deiner Aktualisierungsstrategie passt; ersetze ein vorhandenes geprüftes Image nicht unnötig.
  • WEB_PORT ist ein optionaler Platzhalter für den Host-Port. Ohne gesetzten Wert verwendet Compose 8080.
  • 127.0.0.1 bindet den Port nur an den Server selbst und stellt ihn nicht im gesamten Heimnetz bereit.
  • 80 rechts vom Doppelpunkt ist der Port innerhalb des Containers.
  • ./html ist absichtlich relativ zur eingebundenen Datei. Compose löst diesen Pfad gegen den Ordner web auf, nicht gegen den Ordner der Hauptdatei.[1][2]
  • :ro bindet den Inhalt nur lesbar ein.

Bei einem bestehenden Dienst übernimmst du dessen bisherigen Image-Tag, Umgebungsvariablen, Netzwerke, Volumes und Sicherheitsoptionen unverändert. Achte darauf, alle zugehörigen relativen Dateien gemeinsam in den Baustein zu verschieben oder die Pfade bewusst anzupassen.

6. Baustein mit include einbinden

Sichere die Hauptdatei noch einmal einzeln:

cp compose.yaml "compose.yaml.vor-include-$(date +%F-%H%M)"

Öffne danach die Hauptdatei:

nano compose.yaml

Trage include auf oberster Ebene ein. Für das vollständige Testprojekt kann die Datei so aussehen:

include:
  - web/compose.yaml

services:
  smoke-test:
    image: curlimages/curl:8.16.0
    depends_on:
      - web
    command:
      - --fail
      - --silent
      - --show-error
      - http://web/
    profiles:
      - test

include steht auf derselben Ebene wie services, nicht innerhalb eines Dienstes.[1][4] web/compose.yaml ist der relative Pfad zur einzubindenden Datei.

Der Dienst smoke-test ruft den eingebundenen Webdienst tatsächlich unter http://web/ auf. curlimages/curl:8.16.0 ist ein Beispiel-Image mit festem Tag; prüfe den Tag vor produktiver Übernahme. Das Profil test verhindert, dass der kurzlebige Prüfcontainer beim normalen docker compose up -d dauerhaft mitgestartet wird. depends_on beschreibt hier die echte Compose-Abhängigkeit vom Dienst web.

In deinem bestehenden Projekt behältst du alle nicht ausgelagerten Dienste unter services:. Entferne einen verschobenen Dienst aus der Hauptdatei, damit sein Ressourcenname nicht doppelt vorkommt. Compose warnt bei Konflikten zwischen eingebundenen und lokalen Ressourcen und versucht nicht, sie stillschweigend zusammenzuführen.[1][4]

7. Aufgelöste Konfiguration vor dem Start prüfen

Prüfe zuerst nur die Syntax und das resultierende Modell:

docker compose config --quiet

Der Befehl muss ohne Fehlermeldung mit Exit-Code 0 enden. Zeige anschließend die übernommenen Dienste:

docker compose config --services

Für das Beispiel müssen web und smoke-test erscheinen. Schreibe das neue Modell in eine Datei:

docker compose config > compose-resolved-after.yaml

Prüfe den tatsächlich aufgelösten Mount-Pfad:

docker compose config | grep -A 8 '/usr/share/nginx/html'

Die Ausgabe muss beim Quellpfad auf <projektordner>/web/html zeigen. Ersetze <projektordner> beim Lesen gedanklich durch den absoluten Pfad deines Projekts; der Befehl selbst enthält keinen Platzhalter.

Vergleiche bei einem umgebauten Bestandsprojekt die beiden Modelle:

diff -u compose-resolved-before.yaml compose-resolved-after.yaml

Erwartet werden vor allem Darstellungsunterschiede durch die neue Dateiaufteilung. Unbeabsichtigte Änderungen an Images, Ports, Volumes, Netzwerken oder Umgebungswerten musst du vor dem Start korrigieren.

Aufgelöste Ausgabe vertraulich behandeln

docker compose config kann interpolierte Werte ausgeben. Veröffentliche compose-resolved-before.yaml, compose-resolved-after.yaml oder kopierte Terminalausgaben nicht ungeprüft, weil darin Zugangsdaten oder interne Pfade sichtbar sein können.

8. Fehlende Datei und Ressourcenkonflikt kontrolliert testen

Prüfe zunächst, ob ein falscher Include-Pfad verständlich fehlschlägt. Benenne den Baustein nur kurzzeitig um:

mv web/compose.yaml web/compose.yaml.test

Führe die Prüfung aus:

docker compose config --quiet

Dieser Aufruf muss wegen der fehlenden Datei fehlschlagen. Stelle den korrekten Namen sofort wieder her:

mv web/compose.yaml.test web/compose.yaml

Prüfe erneut:

docker compose config --quiet

Der Aufruf muss jetzt wieder erfolgreich sein. Teste Ressourcenkonflikte nicht auf einem produktiven Stack. Wenn Haupt- und eingebundene Datei denselben Dienst-, Netzwerk- oder Volumenamen definieren, betrachtet Compose das als Konflikt und führt die Definitionen nicht wie bei einer Override-Datei zusammen.[1][2]

include kann zwar weitere include-Abschnitte rekursiv laden, doch mehrere Ebenen erschweren die Fehlersuche. Beginne mit einer flachen Struktur und ergänze weitere Ebenen nur mit klarer Zuständigkeit.[1][2]

9. Dienste starten und Funktion prüfen

Starte den normalen Stack im Hintergrund:

docker compose up -d

Kontrolliere den Zustand:

docker compose ps --all

Zeige die letzten Protokollzeilen des eingebundenen Dienstes:

docker compose logs --tail=50 web

Teste den lokal gebundenen Port direkt auf dem Server:

curl --fail --silent --show-error http://127.0.0.1:8080/

Der Befehl muss die Überschrift aus web/html/index.html ausgeben. 8080 ist der Standardwert aus der Compose-Datei. Falls du WEB_PORT gesetzt hast, ersetze 8080 im Prüfaufruf durch den tatsächlich gewählten Host-Port.

Starte danach den im Hauptprojekt definierten internen Funktionstest:

docker compose run --rm smoke-test

Der Prüfcontainer erreicht web über das gemeinsame Compose-Netzwerk und muss mit Exit-Code 0 enden. --rm entfernt nur diesen kurzlebigen Testcontainer nach dem Lauf. Prüfe bei einer echten Anwendung zusätzlich deren normalen Browser- oder Client-Zugang sowie alle abhängigen Funktionen.

10. Risiken und typische Fehler beheben

Beachte beim dauerhaften Einsatz diese Risiken:

  • Eingebundene Dateien werden Teil deines resultierenden Compose-Modells. Binde nur lokale oder anderweitig kontrollierte Quellen ein.
  • Verwende keine ungeprüften Remote-Git- oder OCI-Quellen für produktive Systeme, auch wenn aktuelle Compose-Versionen solche Quellen unterstützen können.[2]
  • Eine übersichtlichere Datei ist kein Sicherheitsmechanismus. Rechte, Secrets, Portbindungen, Image-Updates und Backups bleiben weiterhin nötig.
  • Relative Build-, Bind-Mount- und env_file-Pfade können nach dem Verschieben auf andere Dateien zeigen. Kontrolliere deshalb immer die aufgelöste Ausgabe.[1]
  • Mehrere Bausteine teilen das resultierende Compose-Modell. Gleich benannte Ressourcen führen zu Konflikten.[1][4]
  • Beim ersten up nach dem Umbau können Container neu erstellt werden. Plane für wichtige Dienste ein Wartungsfenster ein.

Typische Fehler lassen sich so eingrenzen:

  • Additional property include is not allowed: Die Compose-Version ist zu alt. Prüfe docker compose version und aktualisiere das Plugin auf eine unterstützte Version.[2]
  • Datei nicht gefunden: Kontrolliere Schreibweise, Groß-/Kleinschreibung und den Pfad relativ zur Hauptdatei.
  • Falscher Datenordner: Prüfe docker compose config; kurze include-Pfade erhalten für die eingebundene Anwendung ein eigenes Projektverzeichnis.[1]
  • Ressourcenname ist doppelt: Entferne die alte Definition aus der Hauptdatei oder wähle bewusst einen eindeutigen Namen. Erwarte keine automatische Zusammenführung.[1][4]
  • Variable fehlt: Setze sie in der vorgesehenen lokalen Umgebung oder verwende bei verpflichtenden Werten die Compose-Schreibweise ${VARIABLENNAME:?verständliche Fehlermeldung}.[1]
  • Testcontainer bleibt beendet sichtbar: Verwende den gezeigten Lauf mit docker compose run --rm smoke-test und kontrolliere bei einem Fehler dessen Ausgabe.
  • Webseite ist aus dem Heimnetz nicht erreichbar: Die Beispielbindung 127.0.0.1 ist absichtlich nur lokal. Öffne keinen Port ungeschützt ins Internet; nutze für Fernzugriff einen bereits abgesicherten Reverse Proxy oder VPN.

Die Praxisanleitung von Keitaro zeigt ebenfalls eine Aufteilung in eigenständige Anwendungs- und Backend-Dateien und verwendet bei Bedarf die lange include-Syntax mit eigenen Umgebungsdateien.[7]

Das öffentlich auffindbare Video „Simplifying Docker Compose with Modularization Using the Include Feature“ demonstriert das Grundprinzip ergänzend.[6]

Für Syntax, Versionsvoraussetzungen und Konfliktverhalten sind die aktuellen Docker-Quellen maßgeblich.[1][2]

11. Rückweg durchführen

Stoppe zuerst weitere Tests. Zeige die angelegten Sicherungsdateien an:

ls -1 compose.yaml.vor-include-*

Ersetze <sicherungsdatei> durch den vollständigen Dateinamen der gewünschten Sicherung:

cp <sicherungsdatei> compose.yaml

<sicherungsdatei> muss eine zuvor angelegte, vertrauenswürdige Sicherung der Hauptdatei sein. Entferne oder verschiebe den neuen Unterordner noch nicht, solange du nicht geprüft hast, ob dort inzwischen Daten liegen.

Prüfe die wiederhergestellte Konfiguration:

docker compose config --quiet

Übernimm danach das alte Modell:

docker compose up -d

Kontrolliere Container und Protokolle:

docker compose ps --all
docker compose logs --tail=50

docker compose down entfernt standardmäßig die für das Projekt erstellten Container und Netzwerke, aber keine externen Ressourcen; mit -v würden zusätzlich benannte Volumes entfernt.[9] Für diesen Rückweg reicht normalerweise docker compose up -d mit der wiederhergestellten Datei. Bei Datenproblemen verwendest du das anwendungsspezifische Backup aus Schritt 2.

Sources

[1] https://docs.docker.com/reference/compose-file/include — Docker Docs: include [2] https://docs.docker.com/compose/how-tos/multiple-compose-files/include — Docker Docs: Include multiple Compose files [3] https://www.docker.com/blog/improve-docker-compose-modularity-with-include — Docker Blog: Improve Docker Compose Modularity with include [4] https://github.com/compose-spec/compose-spec/blob/main/14-include.md — Compose Specification: Include [6] https://www.youtube.com/watch?v=GhICKo39eW8 — Video: Simplifying Docker Compose with Modularization Using the Include Feature [7] https://www.keitaro.com/insights/2025/01/13/streamline-docker-compose-with-the-include-directive — Keitaro: Streamline Docker Compose with the Include Directive [9] https://docs.docker.com/reference/cli/docker/compose/down — Docker Docs: docker compose down

Fertig

Dein Compose-Projekt besteht jetzt aus einer übersichtlichen Hauptdatei und einem eigenständigen, eingebundenen Baustein. Die Funktionsprüfung ist bestanden, wenn docker compose config --quiet Exit-Code 0 liefert, docker compose config --services die Dienste aus beiden Dateien zeigt, der aufgelöste Mount auf web/html verweist, docker compose up -d den Webdienst startet, der lokale curl-Aufruf die Testseite ausgibt und docker compose run --rm smoke-test erfolgreich endet.[1]