Wenn ein Werkzeug den Weg nicht findet

Verfasst von

in

WordPress hat ein kleines Uhrwerk. Es erledigt im Hintergrund Hausarbeiten: alte Zwischenstände löschen, nach Updates schauen, geplante Aufgaben anstoßen. Dieses Uhrwerk heißt WP-Cron. Es tickt nur, wenn jemand die Seite besucht. Auf einer Seite hinter einem Passwort, die kaum Besuch bekommt, tickt es entsprechend selten.

TODO: Ausgangssymptom nennen. Was hat den Anstoß gegeben, sich WP-Cron anzusehen?

Beim Nachsehen fiel etwas auf. Mein Werkzeug für Befehle, WP-CLI, fand den Namen meiner Seite nicht. Es lebt in einem eigenen Raum ohne Tür nach draußen. Das war so gewollt, denn es braucht nur Zugang zur Datenbank. Wer jedoch den Namen der Seite nachschlagen möchte, muss durch diese Tür.

Mein Server kennt einen Trick für solche Fälle. Man kann einem Raum einen Eintrag ins Adressbuch schreiben: „Dieser Name gehört zum Haus selbst.“ Dann muss er gar nicht erst draußen fragen. Für WordPress gab es diesen Eintrag schon. Für das Werkzeug habe ich ihn nachgetragen und ihm dazu eine schmale Tür ins Haus gegeben.

Dann ein zweiter Fund. Im Adressbuch stand der Name zweimal: einmal mit einer vertrauten Hausnummer und einmal mit einer langen Adresse der neueren Art (IPv6). Die lange Adresse führte ins Leere, die vertraute ans Ziel. Das Ergebnis des Tests war eindeutig: Der Weg über die vertraute Adresse funktioniert, der andere scheitert sofort und lässt dem Programm Raum für den Rückweg.

TODO: Klären, woher Docker die zweite (IPv6-)Adresse nimmt. In der Docker-Konfiguration steht dazu nichts.

Dann zum Uhrwerk selbst. Ich habe es abgeschaltet und durch einen festen Wecker auf dem Server ersetzt. Der Wecker klingelt alle fünf Minuten und lässt fällige Aufgaben erledigen, unabhängig davon, ob jemand die Seite besucht. Das ist verlässlicher und schont die Seite.

Den Beleg lieferte das Backend gleich mit. Vier Aufgaben von gestern Morgen standen als „überfällig“ da, weil das Uhrwerk ohne Besucher nicht getickt hatte. Nach dem ersten Lauf des Weckers waren sie erledigt, die Meldung verschwand, und die wiederkehrenden Aufgaben hatten neue Termine.

Eine Kleinigkeit hat mich eine Weile beschäftigt: Die Abschaltung stand in der Einrichtung, wirkte aber nicht. Die Konfigurationsdatei von WordPress entsteht nur einmal, beim ersten Start. Spätere Änderungen an den Startwerten erreichen sie nicht. Ich habe die Zeile deshalb direkt in die Datei geschrieben.

Ergebnis: Das Werkzeug findet die Seite, WP-Cron ist abgeschaltet, der Wecker läuft und hat im ersten Durchgang 15 Aufgaben erledigt.

Zeig mir, wie es geht: Namensauflösung im wpcli-Container
  • Befund: wpcli hing nur an kisp_internal (internal: true, ohne Gateway), der Name der Seite ließ sich nicht auflösen:
docker compose -p kisp run --rm wpcli getent ahostsv4 wordpress.thekeepitsimpleproject.org

Ausgabe vorher: leer. Nachher: 172.17.0.1.

  • Lösung in infra/compose.yaml (0.2 und höher): beim Dienst wpcli
extra_hosts:
  - "${KISP_HOST}:host-gateway"
networks:
  - kisp_internal
  - default

Das Standardnetz ist nötig, weil host-gateway im internen Netz nicht erreichbar ist.

  • Im Dienst wordpress stand extra_hosts bereits. /etc/hosts im Container enthält zwei Einträge für den Namen: 172.17.0.1 und fd00:18:10:2011:ffff::1.
  • Test beider Adressfamilien aus dem Container:
docker exec kisp-wp php -r 'foreach ([4,6] as $v) { $c = curl_init("https://thekeepitsimpleproject.org/"); curl_setopt_array($c, [CURLOPT_IPRESOLVE => $v == 4 ? CURL_IPRESOLVE_V4 : CURL_IPRESOLVE_V6, CURLOPT_RETURNTRANSFER => 1, CURLOPT_TIMEOUT => 10]); curl_exec($c); echo "IPv$v: " . curl_getinfo($c, CURLINFO_HTTP_CODE) . " " . curl_error($c) . "\n"; }'

Ergebnis: IPv4 401 (Passwortschutz von Caddy, erwartet), IPv6 sofortiger Verbindungsfehler (Container ohne IPv6-Routing).

  • TODO: Herkunft der IPv6-Adresse im /etc/hosts klären.
Zeig mir, wie es geht: WP-Cron abschalten, Timer einrichten
  • Konstante in der wp-config.php setzen. WORDPRESS_CONFIG_EXTRA greift nur beim Erzeugen der Datei, im laufenden Stack hilft:
docker compose -p kisp run --rm wpcli wp config set DISABLE_WP_CRON true --raw
docker compose -p kisp run --rm wpcli wp cron test

wp cron test meldet danach „WP-Cron spawning is disabled“.

  • systemd-Einheiten unter /etc/systemd/system/, Quellen infra/kisp-wp-cron.service und infra/kisp-wp-cron.timer. Der Dienst führt aus:
docker compose -p kisp run --rm -T wpcli wp cron event run --due-now

Der Timer startet 2 Minuten nach dem Boot und danach alle 5 Minuten.

  • Aktivieren und prüfen:
systemctl daemon-reload
systemctl enable --now kisp-wp-cron.timer
systemctl start kisp-wp-cron.service
journalctl -u kisp-wp-cron.service -n 20 --no-pager
systemctl list-timers kisp-wp-cron.timer --no-pager
  • Action Scheduler: Der Befehl heißt wp action-scheduler action list --status=pending. Er arbeitet über das Cron-Ereignis action_scheduler_run_queue.
  • Beleg für den Timer: Das Backend zeigte „4 past-due actions“ (Termine 2026-10-04, 07:57 bis 09:46 UTC). Nach dem ersten Timer-Lauf standen alle vier auf complete, failed war leer, die Meldung verschwand. Zwei wiederkehrende Aktionen sind neu eingeplant:
docker compose -p kisp run --rm wpcli wp action-scheduler action list --status=pending --fields=id,hook,scheduled_date
docker compose -p kisp run --rm wpcli wp action-scheduler action list --status=complete --fields=id,hook,scheduled_date
docker compose -p kisp run --rm wpcli wp action-scheduler action list --status=failed --fields=id,hook,scheduled_date
  • TODO: Die Aktionen wp_umbrella_* stammen von WP Umbrella. Ob auch die Bibliothek Action Scheduler von diesem Plugin mitgebracht wird, ist offen.
  • TODO: Offen ist, ob bei aktivem WP-Cron der Selbstaufruf an Caddy mit 401 scheitern würde. Die Annahme ist plausibel, getestet wurde sie nicht.