WP-Cron ist kein Cron. Es ist ein Pseudo-Cron, der bei jedem Seitenaufruf prüft, ob etwas fällig ist. Auf einer Seite mit drei Besuchern am Tag heißt das: Backups laufen nicht, geplante Beiträge erscheinen nicht, Updates bleiben liegen.
Getestet mit
- WordPress 6.8
- Debian 12, systemd 252
- PHP 8.3 mit WP-CLI 2.10
Schritt 1: WP-Cron abschalten
In die wp-config.php, oberhalb der Zeile mit /* That's all, stop editing! */:
define( 'DISABLE_WP_CRON', true );
Schritt 2: Service-Unit anlegen
[Unit]
Description=WordPress Cron für example.com
[Service]
Type=oneshot
User=www-data
WorkingDirectory=/var/www/example.com
ExecStart=/usr/local/bin/wp cron event run --due-now
Schritt 3: Timer-Unit anlegen
[Unit]
Description=WordPress Cron alle 5 Minuten
[Timer]
OnBootSec=5min
OnUnitActiveSec=5min
AccuracySec=30s
[Install]
WantedBy=timers.target
systemctl daemon-reload
systemctl enable --now wp-cron@example.service
Warum der Timer lief und trotzdem nichts passierte
Der Timer feuerte im richtigen Takt, der Service meldete status=0/SUCCESS. Trotzdem blieb jede geplante Aufgabe liegen.
Schuld war der PHP-Pfad. WP-CLI läuft unter www-data ohne Login-Shell, und in meinem Setup stand der Pfad zum richtigen PHP-Binary nur in der .bashrc des Deploy-Users. ExecStart griff also auf ein anderes PHP zu, das die WordPress-Konstanten nicht kannte — und beendete sich brav mit Exit-Code 0.
Gesehen habe ich das erst im Journal:
journalctl -u wp-cron@example.service -n 50 --no-pager
Der Fix ist der absolute Pfad in der Unit, ohne Verlass auf die PATH-Variable:
ExecStart=/usr/bin/php8.3 /usr/local/bin/wp cron event run --due-now
Seitdem schreibe ich in systemd-Units grundsätzlich keine Befehle mehr, die auf eine PATH-Variable angewiesen sind.
Kontrolle
systemctl list-timers | grep wp-cron
wp cron event list
Wenn in der Spalte next_run_relative keine negativen Werte mehr stehen, läuft es.
Häufige Fehler
Der Timer läuft, aber nur einmal.
Dann fehlt OnUnitActiveSec. OnBootSec allein feuert genau einmal nach dem Start.
Muss ich pro Seite eine eigene Unit anlegen?
Nein. Mit der Instanz-Schreibweise wp-cron@.service reicht eine Vorlage, der Instanzname wird zum Verzeichnisnamen.
Was passiert mit Plugins, die auf WP-Cron bauen?
Nichts. Sie registrieren ihre Aufgaben wie bisher, nur der Auslöser ist ein anderer.