YAML: Die Auszeichnungssprache in der Docker-Compose-Welt

Aus Tuxipedia
Version vom 24. April 2026, 11:57 Uhr von Admin (Diskussion | Beiträge) (Die Seite wurde neu angelegt: „== docker-compose.yml — Referenz und Konzepte == Dieser Artikel erklärt den Aufbau und die Direktiven einer <code>docker-compose.yml</code> auf Deutsch. Er richtet sich an alle die Docker Compose produktiv einsetzen und eine kompakte Nachschlageseite in ihrer eigenen Sprache brauchen. Die offizielle Referenz ist ausschließlich auf Englisch verfügbar: [https://docs.docker.com/compose/compose-file/ docs.docker.com/compose/compose-file] ---- == YAML…“)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)

docker-compose.yml — Referenz und Konzepte

Dieser Artikel erklärt den Aufbau und die Direktiven einer docker-compose.yml auf Deutsch. Er richtet sich an alle die Docker Compose produktiv einsetzen und eine kompakte Nachschlageseite in ihrer eigenen Sprache brauchen.

Die offizielle Referenz ist ausschließlich auf Englisch verfügbar: docs.docker.com/compose/compose-file


YAML — die Auszeichnungssprache

YAML steht für „YAML Ain't Markup Language" — ein rekursives Akronym, typisch für die Open-Source-Welt. Es ist eine Sprache zur Darstellung strukturierter Daten, die für Menschen lesbar sein soll.

YAML und JSON — Verwandtschaft, kein Dialekt

JSON und YAML sind verwandt, aber nicht dasselbe. Der häufige Vergleich „YAML ist ein JSON-Dialekt" ist ungenau:

  • Jedes gültige JSON ist auch gültiges YAML — YAML ist eine Obermenge von JSON.
  • Umgekehrt gilt das nicht: YAML kann Dinge ausdrücken die JSON nicht kann
 (Kommentare, mehrzeilige Strings, Anker und Referenzen).
  • Die Syntax ist völlig unterschiedlich: JSON verwendet geschweifte Klammern
 und Anführungszeichen, YAML verwendet Einrückung und Doppelpunkte.

Dasselbe Datenkonstrukt in beiden Sprachen:

<syntaxhighlight lang="json"> {

 "services": {
   "nextcloud": {
     "image": "nextcloud:latest",
     "restart": "unless-stopped"
   }
 }

} </syntaxhighlight>

<syntaxhighlight lang="yaml"> services:

 nextcloud:
   image: nextcloud:latest
   restart: unless-stopped

</syntaxhighlight>

YAML ist lesbarer — auf Kosten einer strengen Einrückungsregel: Einrückung mit Leerzeichen, niemals mit Tabs. Ein falsch gesetzter Tab ist der häufigste Fehler in compose-Dateien.

Kommentare

YAML unterstützt Kommentare mit # — JSON nicht. Das macht YAML deutlich besser geeignet für Konfigurationsdateien die Menschen pflegen:

<syntaxhighlight lang="yaml">

  1. Dieser Container darf keinen direkten Außenzugriff haben

ports: [] # keine ports → kein direkter Zugriff von außen </syntaxhighlight>

Datentypen

YAML erkennt Datentypen automatisch:

Wert Typ Anmerkung
true / false Boolean Ohne Anführungszeichen
42 Integer
3.14 Float
hallo String Ohne Anführungszeichen wenn eindeutig
"true" String Mit Anführungszeichen erzwungen
null Null

Stolperfalle: restart: always ist ein String. restart: true wäre ein Boolean — und ungültig. Im Zweifel Anführungszeichen setzen.


Grundaufbau einer docker-compose.yml

Eine docker-compose.yml besteht aus maximal vier Abschnitten auf oberster Ebene:

<syntaxhighlight lang="yaml"> services: # Pflicht — definiert die Container

 ...

networks: # Optional — definiert Netzwerke

 ...

volumes: # Optional — definiert persistente Datenspeicher

 ...

configs: # Optional — definiert Konfigurationsdateien

 ...

</syntaxhighlight>

Der services-Abschnitt ist der einzig verpflichtende. Alles andere ist optional und wird nur angegeben wenn benötigt.

Einrückungsregeln

Die Hierarchie wird ausschließlich durch Einrückung ausgedrückt. Zwei Leerzeichen pro Ebene sind Konvention:

<syntaxhighlight lang="yaml"> services: # Ebene 1

 nextcloud:        # Ebene 2 — Name des Dienstes
   image: ...      # Ebene 3 — Direktive des Dienstes
   networks:       # Ebene 3 — Direktive (Liste folgt)
     - tuxi_net    # Ebene 4 — Listenelement

</syntaxhighlight>


Direktiven im services-Abschnitt

image

Das Docker-Image das für den Container verwendet wird.

<syntaxhighlight lang="yaml"> services:

 nextcloud:
   image: lscr.io/linuxserver/nextcloud:latest

</syntaxhighlight>

Format: registry/image:tag. Ohne Registry wird Docker Hub angenommen. Ohne Tag wird latest angenommen — aber explizit angeben ist besser für Reproduzierbarkeit.

container_name

Fester Name des Containers. Ohne diese Direktive generiert Docker Compose einen Namen aus Projektname + Servicename + Nummer.

<syntaxhighlight lang="yaml"> services:

 nextcloud:
   container_name: nextcloud

</syntaxhighlight>

Wichtig: der Container-Name ist gleichzeitig der Docker-interne DNS-Name mit dem andere Container diesen Dienst ansprechen können.

restart

Neustart-Verhalten des Containers.

<syntaxhighlight lang="yaml"> restart: unless-stopped </syntaxhighlight>

Wert Verhalten
no Nie neu starten (Standard)
always Immer neu starten, auch nach manuellem Stopp
unless-stopped Neu starten außer wenn manuell gestoppt — empfohlen für Produktionsdienste
on-failure Nur neu starten wenn der Container mit Fehlercode endet

image vs. build

Statt ein fertiges Image zu verwenden kann Docker Compose auch selbst eines bauen:

<syntaxhighlight lang="yaml"> services:

 mein-dienst:
   build:
     context: ./mein-verzeichnis   # Pfad zum Dockerfile
     dockerfile: Dockerfile.prod   # optional: anderer Dateiname

</syntaxhighlight>

ports

Portweiterleitung zwischen Host und Container: HOST:CONTAINER.

<syntaxhighlight lang="yaml"> services:

 caddy:
   ports:
     - "80:80"
     - "443:443"
     - "443:443/udp"   # für HTTP/3

</syntaxhighlight>

Wichtig: Wer ports weglässt, ist nur im Docker-Netzwerk erreichbar — nicht von außen. Das ist für interne Dienste wie Datenbanken oder Collabora gewünscht.

volumes

Einbinden von Verzeichnissen oder benannten Volumes in den Container.

<syntaxhighlight lang="yaml"> services:

 nextcloud:
   volumes:
     - ./nextcloud/app:/config          # Bind Mount: Host-Pfad → Container-Pfad
     - /data/nextcloud-data:/data       # absoluter Host-Pfad
     - caddy_data:/data                 # benanntes Volume (unten definiert)

volumes:

 caddy_data:                            # benanntes Volume — Docker verwaltet den Speicherort

</syntaxhighlight>

Typ Syntax Wann verwenden
Bind Mount ./host/pfad:/container/pfad Wenn du direkten Zugriff auf die Dateien brauchst
Benanntes Volume volume_name:/container/pfad Für Datenbankdaten, Caches — Docker verwaltet den Speicherort
tmpfs Siehe tmpfs-Direktive Flüchtige Daten die nur im RAM leben sollen

environment

Umgebungsvariablen die in den Container übergeben werden.

<syntaxhighlight lang="yaml"> services:

 nextcloud:
   environment:
     - PUID=1000
     - PGID=1001
     - TZ=Europe/Berlin
     - MYSQL_PASSWORD=${MYSQL_PASSWORD}   # Wert aus .env-Datei

</syntaxhighlight>

Alternativ als Map-Schreibweise:

<syntaxhighlight lang="yaml">

   environment:
     PUID: "1000"
     TZ: Europe/Berlin

</syntaxhighlight>

env_file

Umgebungsvariablen aus einer externen Datei laden statt sie in der compose-Datei zu hartcodieren — sinnvoll für Passwörter und Secrets:

<syntaxhighlight lang="yaml"> services:

 nc-db:
   env_file:
     - .env

</syntaxhighlight>

Die .env-Datei enthält dann:

<syntaxhighlight lang="bash"> MYSQL_ROOT_PASSWORD=sehrgeheim MYSQL_DATABASE=nextcloud MYSQL_USER=ncuser MYSQL_PASSWORD=auchhgeheim </syntaxhighlight>

Die .env-Datei gehört in .gitignore — niemals in ein öffentliches Repository einchecken.

networks

Netzwerkzugehörigkeit des Containers.

<syntaxhighlight lang="yaml"> services:

 nextcloud:
   networks:
     tuxi_network:
       ipv4_address: 172.18.0.20   # feste IP (optional)
 collabora:
   networks: [tuxi_network]        # Kurzschreibweise ohne feste IP

networks:

 tuxi_network:
   external: true                  # Netzwerk existiert bereits, nicht neu anlegen

</syntaxhighlight>

Ohne networks-Angabe landet der Container im automatisch erstellten Default-Netzwerk des Projekts — Container in verschiedenen Projekten können sich dann nicht erreichen.

extra_hosts

Einträge die direkt in die /etc/hosts des Containers geschrieben werden. Überschreibt DNS — wird vor jeder DNS-Anfrage ausgewertet.

<syntaxhighlight lang="yaml"> services:

 nextcloud:
   extra_hosts:
     - "collabora.beispiel.de:172.18.0.6"

</syntaxhighlight>

Typischer Anwendungsfall: Hairpinning vermeiden — ein Container soll eine Domain intern auflösen statt den Umweg über die externe IP zu nehmen. Siehe Hairpin-NAT – Collabora und Nextcloud hinter Caddy.

depends_on

Startreihenfolge und Abhängigkeiten zwischen Diensten.

<syntaxhighlight lang="yaml"> services:

 nextcloud:
   depends_on:
     db:
       condition: service_healthy   # wartet bis Healthcheck grün ist
     redis:
       condition: service_healthy

</syntaxhighlight>

condition Bedeutung
service_started Container wurde gestartet (Standard — prüft nicht ob er wirklich läuft)
service_healthy Healthcheck des abhängigen Dienstes ist grün
service_completed_successfully Abhängiger Dienst hat sich mit Exit-Code 0 beendet

healthcheck

Prüft ob der Container wirklich funktioniert — nicht nur ob er läuft.

<syntaxhighlight lang="yaml"> services:

 nc-db:
   healthcheck:
     test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -u root -p$$MYSQL_ROOT_PASSWORD || exit 1"]
     interval: 10s      # wie oft prüfen
     timeout: 5s        # wie lange auf Antwort warten
     retries: 30        # wie oft wiederholen bevor unhealthy
     start_period: 30s  # Anlaufzeit bevor Healthcheck zählt

</syntaxhighlight>

Das $$ ist kein Tippfehler — doppeltes Dollar-Zeichen verhindert dass Compose die Variable selbst auflöst; sie wird stattdessen an die Shell im Container übergeben.

tmpfs

Flüchtige Dateisysteme die nur im RAM leben und beim Container-Stopp verschwinden. Für temporäre Daten, Caches und Security-Härtung:

<syntaxhighlight lang="yaml"> services:

 nc-redis:
   tmpfs:
     - /data
     - /tmp
     - /run
 collabora:
   tmpfs:
     - /tmp
     - /var/tmp

</syntaxhighlight>

Redis im RAM-only-Modus: schneller, kein Disk-I/O, Daten gehen beim Neustart verloren — für Session-Caches gewünscht.

dns

DNS-Server die der Container verwenden soll statt des Docker-Defaults.

<syntaxhighlight lang="yaml"> services:

 nextcloud:
   dns:
     - 1.1.1.1    # Cloudflare
     - 9.9.9.9    # Quad9
     - 8.8.8.8    # Google

</syntaxhighlight>

command

Überschreibt den Standard-Startbefehl des Images.

<syntaxhighlight lang="yaml"> services:

 nc-db:
   command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW

</syntaxhighlight>

security_opt und cap_drop / cap_add

Container-Härtung: Linux-Capabilities entziehen oder hinzufügen.

<syntaxhighlight lang="yaml"> services:

 nc-redis:
   security_opt:
     - no-new-privileges:true   # verhindert Privilege Escalation
   cap_drop:
     - ALL                      # alle Capabilities entziehen
   cap_add:
     - NET_BIND_SERVICE         # nur diese eine wieder erlauben (z.B. für Ports <1024)

</syntaxhighlight>

Faustregel aus dem Kommentarteil deiner compose-Datei:

  • no-new-privileges: true — immer möglich, immer gut
  • cap_drop: ALL — wo immer es geht
  • read_only: true — nur bei schlanken Services ohne viele Schreibpfade

read_only

Container-Dateisystem schreibgeschützt — kombiniert mit tmpfs für Verzeichnisse die Schreibzugriff benötigen:

<syntaxhighlight lang="yaml"> services:

 nc-redis:
   read_only: true
   tmpfs:
     - /data
     - /tmp
     - /run

</syntaxhighlight>


Der networks-Abschnitt

Externes Netzwerk verwenden

<syntaxhighlight lang="yaml"> networks:

 tuxi_network:
   external: true   # Netzwerk wurde bereits mit docker network create angelegt

</syntaxhighlight>

Neues Netzwerk anlegen

<syntaxhighlight lang="yaml"> networks:

 mein_netzwerk:
   driver: bridge
   ipam:
     config:
       - subnet: 172.20.0.0/16
         gateway: 172.20.0.1

</syntaxhighlight>


Der volumes-Abschnitt

Benanntes Volume

<syntaxhighlight lang="yaml"> volumes:

 caddy_data:      # Docker verwaltet den Speicherort unter /var/lib/docker/volumes/
 caddy_config:

</syntaxhighlight>

Volume mit tmpfs-Treiber (RAM-Disk)

<syntaxhighlight lang="yaml"> volumes:

 cool-child-roots:
   driver_opts:
     type: tmpfs
     device: tmpfs
     o: "size=4g,mode=1777"

</syntaxhighlight>


Nützliche CLI-Befehle

Befehl Was er tut
docker compose up -d Alle Dienste im Hintergrund starten
docker compose down Alle Dienste stoppen und Container entfernen
docker compose restart Alle Dienste neu starten
docker compose restart caddy Nur einen Dienst neu starten
docker compose up -d --force-recreate nextcloud Einen Container neu erstellen (bei Konfigurationsänderungen)
docker compose logs -f nextcloud Log eines Dienstes live verfolgen
docker compose ps Status aller Dienste anzeigen
docker compose exec nextcloud bash Shell in laufendem Container öffnen
docker compose pull Alle Images aktualisieren

Häufige Fehler

Fehler Ursache Fix
yaml: line X: found character that cannot start any token Tab statt Leerzeichen zur Einrückung Alle Tabs durch Leerzeichen ersetzen
Address already in use Feste IP bereits von anderem Container belegt docker network inspect → Schuldigen finden
Container startet, aber Dienst funktioniert nicht depends_on ohne condition: service_healthy Healthcheck ergänzen und service_healthy verwenden
Umgebungsvariable leer obwohl in .env gesetzt .env liegt nicht im selben Verzeichnis wie die compose-Datei Pfad prüfen oder env_file explizit angeben
$$VARIABLE wird nicht aufgelöst Gewolltes Verhalten — $$ wird zu $ und an die Shell übergeben Für Shell-Variablen im Container: $$ verwenden. Für Compose-Variablen: $

Siehe auch