← Alle Beiträge

Shopware Monitoring: Was Sie in Shopware 6 überwachen sollten

Shopware Monitoring: Was Sie in Shopware 6 überwachen sollten

Ein Shopware-Shop kann stundenlang kaputt sein, ohne dass ein Uptime-Monitor etwas davon merkt. Die Startseite liefert 200, der Warenkorb funktioniert, nur die Bestellbestätigungen gehen nicht raus. Oder der Sitemap-Export ist seit drei Wochen nicht gelaufen. Oder die Suche zeigt Artikel, die längst ausverkauft sind.

Der Grund ist fast immer derselbe. Shopware 6 erledigt einen großen Teil seiner Arbeit nicht im Request, sondern im Hintergrund: Mails, Indexierung, Thumbnails, Sitemaps, Cache-Invalidierung, dazu alles, was Plugins für ERP- oder Marktplatz-Anbindungen in die Queue legen. Bleibt dieser Teil stehen, sieht der Kunde erst einmal nichts. Monitoring muss deshalb dort ansetzen und nicht bei der Startseite.

Die Reihenfolge unten folgt einer einfachen Frage: Was bleibt am längsten unbemerkt, wenn es ausfällt?

Message Queue und Worker

Ohne weitere Konfiguration arbeitet Shopware die Queue über den Admin Worker ab. Solange jemand im Admin eingeloggt ist, pollt der Browser und stößt die Verarbeitung an. Ist niemand eingeloggt, passiert nichts. Für Produktion empfiehlt Shopware, den Admin Worker abzuschalten und die Queue per CLI zu konsumieren:

# config/packages/shopware.yaml
shopware:
    admin_worker:
        enable_admin_worker: false
bin/console messenger:consume --time-limit=60 --memory-limit=512M async low_priority

Mit --time-limit beendet sich der Worker regelmäßig selbst, systemd oder Supervisor startet ihn neu. Ein laufender Prozess ist trotzdem kein Beweis, dass gearbeitet wird. Ein Worker, der nur async konsumiert, lässt low_priority einfach liegen, und das fällt keinem Prozess-Check auf.

Überwachen sollten Sie deshalb drei Dinge:

  • Die laufenden Worker. Null Prozesse ist ein sofortiger Alarm.
  • Die Größe der Queue, vor allem ihren Verlauf. 5.000 Nachrichten nach einem Produktimport sind normal. Eine Queue, die seit einer Stunde nur wächst, ist es nicht.
  • Den Failure-Transport. Schlägt eine Nachricht wiederholt fehl, landet sie im Transport aus MESSENGER_TRANSPORT_FAILURE_DSN. Jede Nachricht dort ist eine Aufgabe, die nie erledigt wurde. Reinschauen können Sie mit bin/console messenger:failed:show.

Seit Shopware 6.7.1.0 gibt es dafür einen eigenen Endpoint, GET /api/_info/message-stats.json, der Statistiken zur Verarbeitung der Nachrichten liefert. Er braucht die ACL-Berechtigung message_queue_stats:read, die Statistik ist per Default aktiv (shopware.messenger.stats.enabled, Zeitfenster über time_span, Standard 300 Sekunden). In älteren Versionen liefert /api/_info/queue.json die Queue-Größen. Der Endpoint ist als deprecated markiert und fällt mit 6.8 weg, also planen Sie den Umstieg gleich mit ein.

Scheduled Tasks

Scheduled Tasks sind der zweite stille Kandidat. Sitemap, Aufräumjobs, Produkt-Exporte, Zahlungsabgleiche mancher Plugins: Alles hängt daran, dass jemand scheduled-task:run startet (in Produktion typischerweise per Cron mit --no-wait) und die Worker die eingereihten Tasks dann auch abarbeiten.

bin/console scheduled-task:list

zeigt den aktuellen Stand. Jeder Task hat einen Status: scheduled, queued, running, failed, skipped oder inactive. Für ein Monitoring reicht eine Abfrage auf die Tabelle scheduled_task, die alle aktiven Tasks findet, deren nächste Ausführung deutlich in der Vergangenheit liegt:

SELECT name, status, last_execution_time, next_execution_time
FROM scheduled_task
WHERE status <> 'inactive'
  AND next_execution_time < UTC_TIMESTAMP() - INTERVAL 1 HOUR;

Liefert das Zeilen, läuft entweder der Scheduler nicht oder die Worker kommen nicht hinterher. Tasks im Status failed gehören ebenfalls in den Alarm.

Ein Detail, das man kennen sollte: Hängengebliebene Tasks reiht Shopware erst nach shopware.messenger.scheduled_task.requeue_timeout neu ein, und der Default dafür steht auf 12 Stunden. Ein einziger hängender Task kann also einen halben Tag lang blockieren, ohne dass sich von selbst etwas bewegt.

Was Shopware selbst mitbringt

Seit 6.6.6.0 hat Shopware ein eigenes Framework für Systemchecks. Auf der Kommandozeile:

bin/console system:check --format=json

Die JSON-Ausgabe gibt es seit 6.6.7.0. Wichtiger für Monitoring ist der Exit-Code: Ist ein Check nicht gesund, endet der Befehl mit einem Fehlercode, und das kann jedes Monitoring-Tool auswerten. Über --context wählen Sie aus, welche Checks laufen (cli, web, pre_rollout, recurrent).

Dasselbe gibt es per HTTP unter /api/_info/system-health-check. Der Endpoint verlangt einen gültigen Bearer-Token oder, seit 6.7.4.0, einen statischen Token, den Sie über shopware.api.static_token.health_check setzen (am besten aus einer Umgebungsvariable):

curl -s -H "Authorization: Static $HEALTH_CHECK_TOKEN" \
  https://ihr-shop.example/api/_info/system-health-check

Daneben gibt es noch /api/_info/health-check. Der liefert nur eine leere 200-Antwort und taugt als Liveness-Check, etwa für einen Docker-HEALTHCHECK, mehr aber nicht.

Erwarten Sie vom Core keine Vollüberwachung. Die mitgelieferten Checks sind vor allem Readiness-Checks: Sind die Sales Channels erreichbar, rendern Listing und Produktdetailseite, sind die Locales in Ordnung, ist der Admin gebaut. Zu Queue, Redis oder OPcache sagt system:check von Haus aus nichts. Das Framework ist erweiterbar, eigene Checks lassen sich als Klasse auf Basis von BaseCheck ergänzen. Wer nicht selbst bauen will, findet in FroshTools einen Queue- und Scheduled-Task-Manager sowie den Befehl frosh:monitor, der bei Problemen mit Queue und Tasks per Mail benachrichtigt.

Redis

In der Shopware-Doku stehen drei Klassen von Daten mit unterschiedlichen Anforderungen: Caches (dürfen verloren gehen), Sessions (wichtig, verlieren aber mit der Zeit an Relevanz) und Warenkörbe oder Nummernkreise (dürfen nicht verloren gehen, Persistenz ist Pflicht). Die Empfehlung lautet, dafür getrennte Redis-Instanzen mit eigener Eviction-Policy zu betreiben.

Daraus ergibt sich, was Sie messen sollten:

redis-cli -p 6379 INFO memory | grep -E 'used_memory_human|maxmemory_human|maxmemory_policy'
redis-cli -p 6379 INFO stats | grep evicted_keys
redis-cli -p 6379 INFO persistence | grep rdb_last_bgsave_status

Auf der Cache-Instanz sind Evictions normal. Auf der Instanz mit Warenkörben ist jeder Anstieg von evicted_keys ein Problem, denn dort verschwinden gerade Warenkörbe von Kunden. Bei persistenten Instanzen gehört zusätzlich der Status des letzten Snapshots in den Check. Und wer alles in eine Instanz gekippt hat, sollte genau das zuerst ändern.

OPcache

OPcache fällt nicht aus, er wird langsam voll. Dann kompiliert PHP Dateien bei jedem Request neu, und der Shop wird spürbar zäher, ohne dass irgendwo ein Fehler steht. Die Werte kommen aus opcache_get_status(), und zwar aus dem PHP-FPM-Prozess, nicht aus der CLI, die einen eigenen OPcache hat. Am einfachsten geht das mit cachetool:

cachetool opcache:status --fcgi=/run/php/php-fpm.sock

Interessant sind cache_full, der freie Speicher unter memory_usage und interned_strings_usage sowie oom_restarts. Steigt num_cached_keys bis an max_cached_keys, ist opcache.max_accelerated_files zu klein. Shopware empfiehlt in den Performance Tweaks unter anderem opcache.interned_strings_buffer=20 und opcache.validate_timestamps=0. Letzteres heißt: Nach jedem Deployment muss der OPcache zurückgesetzt werden, sonst läuft alter Code weiter. Auch das ist ein Fall für einen Check nach dem Deploy.

Elasticsearch und OpenSearch

bin/console es:status

zeigt Cluster-Status, Anzahl der Nodes und ob gerade indexiert wird. Für ein dauerhaftes Monitoring fragen Sie den Cluster direkt über _cluster/health ab: yellow ist ein Warnsignal, red ein Alarm. Achten Sie außerdem auf den Plattenplatz der Nodes. Erreicht er die Flood-Stage-Schwelle, setzt Elasticsearch die Indizes auf schreibgeschützt. Die Suche funktioniert dann weiter, nur neue Preise und Bestände kommen nicht mehr an. Genau so ein Fehler bleibt wochenlang unbemerkt.

Logs, Fehlerquote, Plattenplatz

In der Produktionsumgebung schreibt Shopware ab Level error in rotierende Dateien unter var/log/, also prod-2026-10-02.log und so weiter. Zählen Sie die Einträge mit ERROR und CRITICAL pro Stunde und alarmieren Sie bei Ausreißern statt bei festen Schwellen, denn jeder Shop hat sein eigenes Grundrauschen. Dazu gehört die Quote der 5xx-Antworten aus dem Webserver-Log.

Und der Plattenplatz. Logs, var/cache, Medien und Thumbnails, Binlogs der Datenbank: Läuft die Platte voll, fällt alles gleichzeitig um, und das meistens nachts.

Das Signal, das alles andere abfängt

Alle Checks oben messen Technik. Der eine Check, der auch die Fehler findet, an die niemand gedacht hat, misst Umsatz: Wie viele Bestellungen sind in den letzten Stunden eingegangen, verglichen mit demselben Wochentag der Vorwochen?

SELECT COUNT(*)
FROM `order`
WHERE version_id = UNHEX('0fa91ce3e96a4bc2be4bd9ce752c3425')
  AND created_at > UTC_TIMESTAMP() - INTERVAL 2 HOUR;

Der Filter auf die Live-Version ist wichtig, weil die Tabelle order auch Versionsstände enthält, etwa aus Bearbeitungen im Admin. Fällt die Zahl zu einer Zeit, in der sonst regelmäßig bestellt wird, auf null, ist irgendetwas im Checkout kaputt. Vielleicht hat der Zahlungsanbieter seine API geändert, vielleicht hat ein Theme-Build das JavaScript im Checkout zerlegt. Welche Ursache es ist, sagt Ihnen dieser Check nicht. Dass es eine gibt, schon, und meistens früher als der erste Kunde.

Queue, Scheduled Tasks, Redis, OPcache und Elasticsearch per Hand im Blick zu behalten, heißt: jeden Morgen fünf Befehle, und am Wochenende keinen. ShopSignal übernimmt genau diese Checks für Shopware-6-Shops und meldet sich, wenn etwas stehenbleibt, bevor es ein Kunde tut.