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:
wpclihing nur ankisp_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 Dienstwpcli
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
wordpressstandextra_hostsbereits./etc/hostsim Container enthält zwei Einträge für den Namen:172.17.0.1undfd00: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/hostsklären.
Zeig mir, wie es geht: WP-Cron abschalten, Timer einrichten
- Konstante in der
wp-config.phpsetzen.WORDPRESS_CONFIG_EXTRAgreift 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/, Quelleninfra/kisp-wp-cron.serviceundinfra/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-Ereignisaction_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,failedwar 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
401scheitern würde. Die Annahme ist plausibel, getestet wurde sie nicht.