Docker Compose Ressourcenlimits, Healthchecks und Neustartrichtlinien

11 Min. Lesezeit·Matthieu·vpsproductioncontainersdocker-composedocker|

Ihre Compose-Datei funktioniert in der Entwicklung, ist aber nicht produktionsreif. Lernen Sie, wie Sie Speicher-/CPU-Limits, Healthchecks, Neustartrichtlinien und Startabhängigkeiten konfigurieren, um Ihren VPS vor OOM-Kills und Kaskadenausfällen zu schützen.

Eine Compose-Datei, die docker compose up fehlerfrei ausführt, ist nicht produktionsreif. Ohne Ressourcenlimits kann ein einzelner Container den gesamten Hostspeicher verbrauchen und den Linux OOM-Killer auslösen, der dann jeden Dienst auf dem VPS mit in die Tiefe reißt. Ohne Healthchecks hat Docker keine Möglichkeit, einen hängenden Prozess zu erkennen. Ohne Neustartrichtlinien bleibt ein abgestürzter Container tot, bis Sie es bemerken.

Diese drei Systeme greifen ineinander: Ressourcenlimits verhindern unkontrollierten Verbrauch, Healthchecks erkennen Ausfälle, und Neustartrichtlinien sorgen für die Wiederherstellung. Dieser Leitfaden behandelt alle drei als integrierte Härtungsschicht für Docker Compose v2.

Voraussetzungen: Eine funktionierende Docker-Compose-Installation auf einem VPS. Vertrautheit mit der grundlegenden Compose-Dateistruktur. Für eine Auffrischung, siehe [-> docker-compose-multi-service-vps].

Wie setzt man Speicher- und CPU-Limits in Docker Compose?

Verwenden Sie den Schlüssel deploy.resources.limits in Ihrer Dienstdefinition. Setzen Sie memory auf einen Wert wie 512M oder 1G, um eine feste Obergrenze zu definieren. Setzen Sie cpus auf eine Dezimalzeichenkette wie '0.5' für einen halben Kern. Der Containerprozess wird per OOM-Kill beendet, wenn er das Speicherlimit überschreitet. CPU-Limits drosseln den Prozess, anstatt ihn zu beenden.

services:
  api:
    image: node:22-slim
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M

Was das bewirkt: Der api-Container kann maximal 1 CPU-Kern und 512 MB RAM verwenden. Docker garantiert mindestens 0,25 Kerne und 256 MB, auch wenn andere Container um Ressourcen konkurrieren.

Überprüfen Sie, ob die Limits angewendet werden:

docker compose up -d
docker stats --no-stream --format "table {{.Name}}\t{{.MemUsage}}\t{{.CPUPerc}}"

Die Spalte MEM USAGE / LIMIT zeigt sowohl die aktuelle Nutzung als auch die konfigurierte Obergrenze. Sie sollten 512MiB als Limit sehen, nicht den gesamten Host-RAM. Falls Sie den Gesamtspeicher des Hosts sehen, sind die Limits nicht aktiv.

Was ist der Unterschied zwischen Limits und Reservations?

Limits sind harte Obergrenzen. Der Container kann sie nicht überschreiten. Reservations sind weiche Garantien. Docker nutzt sie für Planungsentscheidungen und die Handhabung von Speicherengpässen.

Einstellung Funktion Wann relevant
limits.memory Harte Obergrenze. OOM-Kill bei Überschreitung. Immer. Verhindert außer Kontrolle geratene Container.
limits.cpus Drosselung. Prozess wird langsamer. CPU-lastige Workloads (Builds, Inferenz).
reservations.memory Garantiertes Minimum. Host unter Speicherdruck.
reservations.cpus Garantierter CPU-Anteil. Mehrere CPU-intensive Container.

Ohne Reservations vergibt Docker Ressourcen nach dem Windhundprinzip. Unter Last kann jeder Container verhungern. Setzen Sie Reservations auf das Minimum, das Ihr Dienst zum Funktionieren braucht.

Was passiert, wenn ein Docker-Container sein Speicherlimit überschreitet?

Der Linux-Kernel-OOM-Killer beendet den Hauptprozess des Containers mit SIGKILL. Docker verzeichnet den Exit-Code 137 (128 + 9, wobei 9 für SIGKILL steht). Wenn die Neustartrichtlinie on-failure oder always ist, startet Docker den Container automatisch neu.

Prüfen Sie, ob ein Container per OOM-Kill beendet wurde:

docker inspect api-1 --format '{{.State.OOMKilled}}'

Ausgabe: true bestätigt einen OOM-Kill.

Für weitere Details:

docker inspect api-1 --format '{{json .State}}' | python3 -m json.tool

Suchen Sie nach "OOMKilled": true, "ExitCode": 137 und "RestartCount", um zu sehen, wie oft der Container neu gestartet wurde.

Ohne Limits allokiert der Container Speicher, bis der Host keinen mehr hat. Der Kernel beendet dann systemweit Prozesse, um RAM freizugeben. Das kann Ihre Datenbank, Ihren Reverse Proxy oder den SSH-Daemon mit herunterreißen. Limits begrenzen den Schadensradius auf den problematischen Container.

Wie plant man das Ressourcenbudget für Container auf einem VPS?

Auf einem VPS mit festen Ressourcen müssen Sie den Speicher über alle Container verteilen. Lassen Sie Puffer für das Host-Betriebssystem und Docker selbst.

Beispielbudget für einen VPS mit 8 GB:

Komponente Speicher
Host-OS + Docker-Engine 1 GB
PostgreSQL 2 GB
Redis 512 MB
Node.js-API 1 GB
Nginx 128 MB
Hintergrundworker 1 GB
Puffer (nicht zugeteilt) 2,35 GB

Halten Sie 20-30 % als Puffer frei. Wenn die Summe Ihrer Containerlimits den Gesamt-RAM des Hosts übersteigt, riskieren Sie, dass der Host-OOM-Killer eingreift, der Dockers Containergrenzen ignoriert.

Überprüfen Sie die Gesamtzuteilung gegen den Hostspeicher:

free -h
docker stats --no-stream --format "{{.Name}}: {{.MemUsage}}"

Wie konfiguriert man einen Healthcheck in Docker Compose?

Fügen Sie einen healthcheck-Block zu Ihrer Dienstdefinition hinzu. Docker führt den Testbefehl im angegebenen Intervall aus und markiert den Container als unhealthy nach der konfigurierten Anzahl aufeinanderfolgender Fehlschläge. Andere Dienste können von diesem Gesundheitsstatus für die Startordnung abhängen.

services:
  api:
    image: node:22-slim
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s
      start_interval: 2s

Healthcheck-Parameter erklärt:

Parameter Standard Empfohlen Funktion
interval 30s 15-60s Zeit zwischen Prüfungen nach dem Start
timeout 30s 5-10s Maximale Laufzeit einer Prüfung
retries 3 3-5 Fehlschläge vor unhealthy
start_period 0s 10-60s Karenzzeit für langsam startende Dienste
start_interval 5s 2-5s Intervall während des Starts (Compose v2.20+)

Der Parameter start_interval (ab Compose v2.20) ermöglicht häufigere Prüfungen während des Starts. Der Container wechselt von starting zu healthy, sobald die erste Prüfung während des start_period erfolgreich ist. Danach laufen Prüfungen im normalen interval.

Was ist der Unterschied zwischen CMD und CMD-SHELL bei Healthchecks?

CMD führt den Befehl direkt ohne Shell aus. CMD-SHELL leitet ihn durch /bin/sh -c. Bevorzugen Sie CMD, wenn möglich. Es vermeidet Shell-Overhead und beseitigt PID-1-Probleme, bei denen die Shell und nicht Ihr Prüfbefehl Signale empfängt.

# CMD-Format - keine Shell, führt die Binary direkt aus
healthcheck:
  test: ["CMD", "pg_isready", "-U", "postgres"]

# CMD-SHELL-Format - läuft über /bin/sh -c
healthcheck:
  test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]

# String-Kurzform - entspricht CMD-SHELL
healthcheck:
  test: curl -f http://localhost:3000/health || exit 1

Verwenden Sie CMD-SHELL, wenn Sie Shell-Funktionen wie ||, Pipes oder Variablenexpansion benötigen. Verwenden Sie CMD für einfache Binary-Ausführung.

Welchen Healthcheck-Befehl sollten Sie für PostgreSQL, Redis und Nginx verwenden?

Jeder Dienst braucht einen Healthcheck, der die Fähigkeit des Dienstes testet, Anfragen zu verarbeiten, nicht nur ob der Prozess läuft.

Dienst Healthcheck-Befehl Was er testet
PostgreSQL ["CMD", "pg_isready", "-U", "postgres"] Akzeptiert Verbindungen
Redis ["CMD", "redis-cli", "ping"] Reagiert auf Befehle
Nginx `["CMD-SHELL", "curl -f http://localhost/
Node.js-App `["CMD-SHELL", "curl -f http://localhost:3000/health
MySQL/MariaDB ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] Engine bereit, nicht nur Socket offen

Wichtig: Für curl-basierte Healthchecks muss das Image curl enthalten. Viele schlanke Images haben es nicht. Installieren Sie es in Ihrem Dockerfile, verwenden Sie stattdessen wget, oder schreiben Sie einen minimalen Health-Endpoint, den Ihr Anwendungsframework nativ bereitstellt.

Überprüfen Sie den Healthcheck-Status:

docker compose ps

Suchen Sie nach (healthy) oder (unhealthy) in der STATUS-Spalte. Für die detaillierte Healthcheck-Historie:

docker inspect api-1 --format '{{json .State.Health}}' | python3 -m json.tool

Dies zeigt die letzten Prüfergebnisse, einschließlich stdout/stderr-Ausgaben fehlgeschlagener Prüfungen. Achten Sie genau darauf: Wenn FailingStreak weiter steigt, ist Ihr Prüfbefehl falsch oder der Dienst hat ein echtes Problem.

Wie wirken Neustartrichtlinien mit Healthchecks zusammen?

Neustartrichtlinien steuern, was Docker tut, wenn ein Container beendet wird. Healthchecks steuern, wie Docker Probleme in laufenden Containern erkennt. Zusammen bilden sie eine automatische Wiederherstellungsschleife: Der Healthcheck erkennt den Ausfall, der Container wird gestoppt, und die Neustartrichtlinie bringt ihn zurück.

services:
  api:
    restart: on-failure:5
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

Neustartrichtlinien im Vergleich

Richtlinie Bei Absturz Bei Neustart Bei docker stop Geeignet für
no Bleibt tot Bleibt tot Bleibt tot Einmalige Aufgaben, Debugging
always Neustart Neustart Neustart Kerninfrastruktur (Datenbanken, Proxys)
unless-stopped Neustart Neustart Bleibt tot Die meisten Produktionsdienste
on-failure:N Neustart (bis zu N Mal) Bleibt tot Bleibt tot Dienste, die nicht endlos neustarten sollen

on-failure:5 bedeutet, dass Docker bis zu 5 Mal neu startet. Danach bleibt der Container tot. Das verhindert Neustartschleifen, die CPU auf einen grundlegend defekten Dienst verschwenden.

Die OOM-Neustart-Interaktion: Wenn ein Container sein Speicherlimit erreicht und per OOM-Kill beendet wird (Exit-Code 137), behandelt Docker das als Fehlschlag. Mit on-failure:5 startet er bis zu 5 Mal neu. Wenn der Dienst Speicher verliert, wird er wiederholt beendet und neu gestartet, bis das Wiederholungslimit erreicht ist. Prüfen Sie den Neustartzähler:

docker inspect api-1 --format '{{.RestartCount}}'

Für die meisten Produktionsdienste verwenden Sie unless-stopped. Es startet bei Abstürzen und Host-Neustarts neu, respektiert aber manuelle docker compose stop-Befehle. Verwenden Sie on-failure:N für Dienste, bei denen eine Absturzschleife einen Alarm auslösen soll, anstatt endlos still weiterzumachen.

Wie lässt man einen Dienst auf einen anderen gesunden Dienst warten?

Verwenden Sie depends_on mit condition: service_healthy. Damit wartet Docker Compose, bis der Healthcheck der Abhängigkeit erfolgreich ist, bevor der abhängige Dienst gestartet wird.

services:
  db:
    image: postgres:17
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 10s
    restart: unless-stopped

  api:
    image: myapp:latest
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 3
    restart: unless-stopped

Ohne condition: service_healthy wartet depends_on nur darauf, dass der Container startet, nicht darauf, dass der darin laufende Dienst bereit ist. PostgreSQL braucht mehrere Sekunden zum Initialisieren. Ihre Anwendung würde abstürzen, weil sie versucht, sich mit einer Datenbank zu verbinden, die noch keine Verbindungen akzeptiert.

Die Option restart: true innerhalb von depends_on (Compose v2.21+) weist Docker an, den abhängigen Dienst neu zu starten, wenn die Abhängigkeit neu startet:

depends_on:
  db:
    condition: service_healthy
    restart: true

Das ist nützlich, wenn Ihre Anwendung die Datenbankverbindung zwischenspeichert und sich nicht von einem Datenbank-Neustart erholen kann, ohne selbst komplett neu zu starten.

Welche Ulimits sollten Sie für Produktionscontainer setzen?

Setzen Sie nofile (offene Dateideskriptoren) und nproc (maximale Prozessanzahl) für Dienste, die viele gleichzeitige Verbindungen verarbeiten. Jede TCP-Verbindung, geöffnete Datei und Pipe verbraucht einen Dateideskriptor. Das Standardlimit (1024 in vielen Images) ist zu niedrig für Datenbanken und Dienste mit hohem Datenverkehr.

services:
  db:
    image: postgres:17
    ulimits:
      nofile:
        soft: 65536
        hard: 65536
      nproc:
        soft: 4096
        hard: 4096

Überprüfung innerhalb des Containers:

docker compose exec db cat /proc/1/limits

Suchen Sie nach Max open files und Max processes. Die Werte sollten mit Ihren Einstellungen übereinstimmen.

Fork-Bomb-Schutz mit PID-Limit

Setzen Sie deploy.resources.limits.pids, um die Anzahl der Prozesse zu begrenzen, die ein Container erstellen kann. Das verhindert, dass Fork-Bombs und unkontrolliertes Prozess-Spawning alle PIDs auf dem Host verbrauchen.

services:
  api:
    image: myapp:latest
    deploy:
      resources:
        limits:
          pids: 200

Falls Sie deploy.resources nicht für CPU-/Speicherlimits nutzen, funktioniert auch der Top-Level-Schlüssel pids_limit. Aber wenn deploy.resources.limits vorhanden ist, müssen Sie das PID-Limit ebenfalls dort platzieren. Eine Vermischung beider Varianten verursacht einen Validierungsfehler in Compose v5+.

200 PIDs sind großzügig für eine typische Webanwendung. Eine Node.js-App nutzt etwa 10-30. PostgreSQL verwendet ungefähr einen Prozess pro Verbindung plus Hintergrundworker. Dimensionieren Sie auf das 2-3-fache Ihres erwarteten Spitzenwerts.

Wie begrenzt man die Log-Größe von Containern?

Ohne Log-Limits wachsen Container-Logs unbegrenzt. Ein geschwätziger Dienst kann Ihre Festplatte in Stunden füllen. Setzen Sie max-size und max-file beim json-file-Logging-Treiber, um Logs automatisch zu rotieren.

services:
  api:
    image: myapp:latest
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Das behält maximal 3 Dateien mit je 10 MB, was den Log-Speicher auf 30 MB pro Dienst begrenzt. Passen Sie es an Ihren Debugging-Bedarf an. Verwenden Sie einen YAML-Anker, um dieselbe Logging-Konfiguration auf alle Dienste anzuwenden:

x-logging: &default-logging
  driver: "json-file"
  options:
    max-size: "10m"
    max-file: "3"

services:
  api:
    image: myapp:latest
    logging: *default-logging
  worker:
    image: myapp:latest
    logging: *default-logging

stop_grace_period für sauberes Herunterfahren setzen

Wenn Docker einen Container stoppt, sendet es SIGTERM und wartet, bis der Prozess sich ordentlich beendet. Wenn der Prozess sich nicht innerhalb der Gnadenfrist beendet, sendet Docker SIGKILL. Der Standardwert beträgt 10 Sekunden.

services:
  db:
    image: postgres:17
    stop_grace_period: 30s

  api:
    image: myapp:latest
    stop_grace_period: 5s

Datenbanken brauchen längere Gnadenfristen, um Schreibvorgänge abzuschließen und Verbindungen sauber zu schließen. Webserver und API-Prozesse beenden sich in der Regel innerhalb weniger Sekunden. Setzen Sie die Gnadenfrist entsprechend der tatsächlichen Herunterfahrzeit Ihres Dienstes, mit einem kleinen Puffer.

Vollständige produktionsreife Compose-Datei

Dieses Beispiel kombiniert alle Einstellungen für einen typischen Webanwendungs-Stack: Nginx-Reverse-Proxy, Node.js-API, PostgreSQL-Datenbank und Redis-Cache.

x-logging: &default-logging
  driver: "json-file"
  options:
    max-size: "10m"
    max-file: "3"

services:
  nginx:
    image: nginx:1.27-alpine
    ports:
      - "80:80"
      - "443:443"
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 128M
        reservations:
          memory: 64M
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3
    restart: unless-stopped
    stop_grace_period: 5s
    logging: *default-logging
    depends_on:
      api:
        condition: service_healthy

  api:
    image: myapp:latest
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
          pids: 200
        reservations:
          cpus: '0.25'
          memory: 256M
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 15s
      start_interval: 2s
    restart: unless-stopped
    stop_grace_period: 5s
    logging: *default-logging
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy

  db:
    image: postgres:17
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 2G
        reservations:
          cpus: '0.5'
          memory: 1G
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
      interval: 10s
      timeout: 3s
      retries: 5
      start_period: 15s
    restart: unless-stopped
    stop_grace_period: 30s
    ulimits:
      nofile:
        soft: 65536
        hard: 65536
    logging: *default-logging
    volumes:
      - pgdata:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          memory: 128M
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3
    restart: unless-stopped
    stop_grace_period: 5s
    logging: *default-logging

volumes:
  pgdata:

secrets:
  db_password:
    file: ./secrets/db_password.txt

Hinweis: Das Datenbankpasswort verwendet Docker Secrets mit POSTGRES_PASSWORD_FILE anstelle einer Klartext-Umgebungsvariable POSTGRES_PASSWORD. Erstellen Sie die Secrets-Datei mit eingeschränkten Berechtigungen:

mkdir -p secrets
openssl rand -base64 32 > secrets/db_password.txt
chmod 600 secrets/db_password.txt

Prüfliste

Nachdem Sie alle Einstellungen angewendet haben, gehen Sie diese Prüfungen durch, um zu bestätigen, dass alles aktiv ist.

1. Prüfen, ob Ressourcenlimits angewendet werden:

docker stats --no-stream --format "table {{.Name}}\t{{.MemUsage}}\t{{.CPUPerc}}"

Die Spalte MEM USAGE / LIMIT zeigt sowohl die aktuelle Nutzung als auch die konfigurierte Obergrenze. Jeder Container sollte sein konfiguriertes Speicherlimit anzeigen, nicht den Gesamt-RAM des Hosts.

2. Healthcheck-Status prüfen:

docker compose ps

Alle Dienste sollten (healthy) in der STATUS-Spalte zeigen. Falls welche (health: starting) anzeigen, warten Sie, bis die start_period abgelaufen ist.

3. Neustartrichtlinie prüfen:

docker inspect --format '{{.HostConfig.RestartPolicy.Name}}:{{.HostConfig.RestartPolicy.MaximumRetryCount}}' $(docker compose ps -q)

4. Ulimits innerhalb eines Containers prüfen:

docker compose exec db cat /proc/1/limits | grep -E "open files|processes"

5. Log-Konfiguration prüfen:

docker inspect --format '{{.HostConfig.LogConfig.Type}} max-size={{index .HostConfig.LogConfig.Config "max-size"}} max-file={{index .HostConfig.LogConfig.Config "max-file"}}' $(docker compose ps -q api)

6. Die gesamte Wiederherstellungskette testen:

Stoppen Sie einen Container und beobachten Sie die Wiederherstellung:

docker compose stop api
docker compose ps  # api sollte Exited zeigen
docker compose start api
docker compose ps  # api sollte nach der start_period (healthy) zeigen
docker inspect $(docker compose ps -q api) --format 'RestartCount: {{.RestartCount}}'

Um den automatischen Neustart bei einem echten Absturz zu testen, senken Sie das Speicherlimit unter das Minimum der Anwendung. Die Neustartrichtlinie greift, wenn der Prozess sich selbst beendet. Beachten Sie, dass docker kill in aktuellen Docker-Versionen keine Neustartrichtlinien auslöst.

Kurzreferenz für die Ressourcendimensionierung

Startpunkte für gängige Dienste auf einem VPS. Passen Sie sie an Ihre tatsächliche Last an.

Dienst Speicherlimit CPU-Limit Healthcheck
PostgreSQL 1-4 GB 1.0-2.0 pg_isready -U postgres
Redis 256M-1G 0.25-0.5 redis-cli ping
Node.js-API 256M-1G 0.5-1.0 curl -f http://localhost:PORT/health
Nginx 64M-256M 0.25-0.5 curl -f http://localhost/
Ollama (LLM) 4-8 GB 2.0-4.0 curl -f http://localhost:11434/
Hintergrundworker 256M-1G 0.5-1.0 Anwendungsspezifische Prüfung

Etwas funktioniert nicht?

Container startet in einer Schleife neu:

docker compose logs api --tail 50
docker inspect api-1 --format '{{.State.ExitCode}} OOM:{{.State.OOMKilled}} Restarts:{{.RestartCount}}'

Wenn OOMKilled: true, erhöhen Sie das Speicherlimit. Wenn der Exit-Code nicht 137 ist, prüfen Sie die Anwendungslogs auf den tatsächlichen Fehler.

Healthcheck schlägt immer fehl:

docker inspect api-1 --format '{{json .State.Health.Log}}' | python3 -m json.tool

Dies zeigt die Ausgabe jeder Prüfung. Häufige Ursachen: Der Health-Endpoint existiert nicht, curl ist im Image nicht installiert, oder der Dienst lauscht auf einem anderen Port als dem, den die Prüfung anspricht.

depends_on wartet nicht:

Stellen Sie sicher, dass die Abhängigkeit einen healthcheck definiert hat. Ohne diesen hat condition: service_healthy nichts, worauf es warten kann, und Compose meldet einen Fehler beim Start.

Limits werden in docker stats nicht angezeigt:

Überprüfen Sie, ob Sie Docker Compose v2 verwenden (das docker compose-Plugin, nicht das alte docker-compose-Binary). Prüfen Sie Ihre Version:

docker compose version

Der Schlüssel deploy.resources erfordert Compose v2. Falls Sie eine ältere Version nutzen, siehe [-> docker-commands] für Installationsanweisungen.

Logs lesen, wenn etwas fehlschlägt:

journalctl -u docker -f
docker compose logs -f --tail 100

Das Docker-Daemon-Log zeigt OOM-Ereignisse und Änderungen im Container-Lebenszyklus. Die Compose-Logs zeigen die Anwendungsausgabe.