Was ist Task-Scheduling
Traditionell mussten Sie für jede regelmäßig ausgeführte Aufgabe einen eigenen Cron-Eintrag anlegen.
Dabei liegen die Zeitpläne außerhalb des Quellcodes, sind nicht versioniert, und für Änderungen ist stets ein SSH-Zugang nötig.
Mit dem Laravel-Scheduler definieren Sie Zeitpläne fließend in der Anwendung.
Auf dem Server genügt ein einziger Cron-Eintrag; die Definitionen versionieren Sie zusammen mit dem Code.
Zeitpläne werden üblicherweise in routes/console.php definiert.
<?php
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schedule;
Schedule::call(function () {
DB::table('recent_users')->delete();
})->daily();
Mit dem Artisan-Befehl schedule:list sehen Sie definierte Tasks und deren nächste Ausführung.php artisan schedule:list
Ausführungsablauf des Schedulers
Zeitpläne definieren
Zeitpläne stehen in routes/console.php.
Alternativ nutzen Sie die Methode withSchedule in bootstrap/app.php.
// bootstrap/app.php
use Illuminate\Console\Scheduling\Schedule;
->withSchedule(function (Schedule $schedule) {
$schedule->call(new DeleteRecentUsers)->daily();
})
Was Sie planen können
Artisan-Kommandos
Mit command planen Sie Artisan-Kommandos – als Kommandoname oder Klassenname.
use App\Console\Commands\SendEmailsCommand;
use Illuminate\Support\Facades\Schedule;
// Nach Kommandoname
Schedule::command('emails:send Taylor --force')->daily();
// Nach Klassenname (Argumente als Array)
Schedule::command(SendEmailsCommand::class, ['Taylor', '--force'])->daily();
Auch Closure-Kommandos lassen sich direkt nach der Definition planen.
Artisan::command('delete:recent-users', function () {
DB::table('recent_users')->delete();
})->purpose('Delete recent users')->daily();
Queue-Jobs
Mit job planen Sie Queue-Jobs direkt, ohne Closure.
use App\Jobs\Heartbeat;
use Illuminate\Support\Facades\Schedule;
Schedule::job(new Heartbeat)->everyFiveMinutes();
Sie können Queue-Name und Verbindung angeben.
// Dispatch auf Queue "heartbeats" der Verbindung "sqs"
Schedule::job(new Heartbeat, 'heartbeats', 'sqs')->everyFiveMinutes();
Shell-Kommandos
Mit exec führen Sie OS-Kommandos aus.
use Illuminate\Support\Facades\Schedule;
Schedule::exec('node /home/forge/script.js')->daily();
Closures
Mit call planen Sie beliebige PHP-Closures.
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schedule;
Schedule::call(function () {
DB::table('recent_users')->delete();
})->daily();
Auch Invokable-Objekte mit __invoke sind möglich.
Schedule::call(new DeleteRecentUsers)->daily();
Ausführungsfrequenz
Wichtige Methoden
| Methode | Beschreibung |
|---|
->everySecond() | Sekündlich |
->everyMinute() | Minütlich |
->everyFiveMinutes() | Alle 5 Minuten |
->everyFifteenMinutes() | Alle 15 Minuten |
->everyThirtyMinutes() | Alle 30 Minuten |
->hourly() | Stündlich |
->hourlyAt(17) | Zur Minute 17 jede Stunde |
->daily() | Täglich um 0 Uhr |
->dailyAt('13:00') | Täglich um 13 Uhr |
->twiceDaily(1, 13) | Täglich um 1 und 13 Uhr |
->weekly() | Sonntag 0 Uhr |
->weeklyOn(1, '8:00') | Montag 8 Uhr |
->monthly() | Am 1. um 0 Uhr |
->monthlyOn(4, '15:00') | Am 4. um 15 Uhr |
->quarterly() | 1. Tag jedes Quartals, 0 Uhr |
->yearly() | 1. Januar, 0 Uhr |
->timezone('Europe/Berlin') | Zeitzone setzen |
Direkte Cron-Ausdrücke
Mit cron verwenden Sie beliebige Cron-Ausdrücke.
use Illuminate\Support\Facades\Schedule;
Schedule::command('emails:send')->cron('0 9 * * *'); // Täglich um 9
Kombinierte Bedingungen
Frequenz- und Wochentagsmethoden lassen sich kombinieren.
use Illuminate\Support\Facades\Schedule;
// Montag 13 Uhr
Schedule::call(function () {
// ...
})->weekly()->mondays()->at('13:00');
// Werktags stündlich zwischen 8 und 17 Uhr
Schedule::command('foo')
->weekdays()
->hourly()
->timezone('Europe/Berlin')
->between('8:00', '17:00');
Zeitzone
Mit timezone legen Sie die Zeitzone pro Task fest.
use Illuminate\Support\Facades\Schedule;
Schedule::command('report:generate')
->timezone('Europe/Berlin')
->at('9:00');
Eine gemeinsame Zeitzone für alle Tasks konfigurieren Sie in config/app.php unter schedule_timezone.
// config/app.php
'schedule_timezone' => 'Europe/Berlin',
In Zeitzonen mit Sommerzeit können Tasks bei der Umstellung doppelt oder gar nicht ausgeführt werden.
Nach Möglichkeit UTC verwenden.
Bedingungen
when / skip
when führt aus, wenn die Closure true liefert; skip überspringt bei true.
use Illuminate\Support\Facades\Schedule;
// Nur ausführen, wenn true
Schedule::command('emails:send')->daily()->when(function () {
return true;
});
// Überspringen, wenn true
Schedule::command('emails:send')->daily()->skip(function () {
return true;
});
environments
Mit environments grenzen Sie die Ausführung auf bestimmte Umgebungen ein.
Schedule::command('emails:send')
->daily()
->environments(['staging', 'production']);
Zeitfenster
between / unlessBetween beschränken das Zeitfenster.
// Nur zwischen 7:00 und 22:00
Schedule::command('emails:send')
->hourly()
->between('7:00', '22:00');
// Zwischen 23:00 und 4:00 nicht
Schedule::command('emails:send')
->hourly()
->unlessBetween('23:00', '4:00');
| Methode | Beschreibung |
|---|
->weekdays() | Nur Werktage |
->weekends() | Nur Wochenenden |
->mondays() bis ->sundays() | Bestimmter Wochentag |
->days([0, 3]) | Mehrere Tage (z. B. Sonntag=0, Mittwoch=3) |
use Illuminate\Support\Facades;
use Illuminate\Console\Scheduling\Schedule;
Facades\Schedule::command('emails:send')
->hourly()
->days([Schedule::SUNDAY, Schedule::WEDNESDAY]);
Überlappungen verhindern
Standardmäßig startet die nächste Ausführung, auch wenn die vorherige noch läuft.
Mit withoutOverlapping warten Sie, bis der vorherige Lauf beendet ist.
use Illuminate\Support\Facades\Schedule;
Schedule::command('emails:send')->withoutOverlapping();
Die Lock-Dauer (in Minuten) ist konfigurierbar (Standard: 24 Stunden).
// Lock läuft nach 10 Minuten ab
Schedule::command('emails:send')->withoutOverlapping(10);
withoutOverlapping verwaltet den Lock über den Application-Cache.
Bei stecken gebliebenen Tasks können Sie den Lock mit schedule:clear-cache freigeben.php artisan schedule:clear-cache
Ausführung auf mehreren Servern
Läuft der Scheduler auf mehreren Servern, sorgt onOneServer dafür, dass ein Task nur auf einem Server ausgeführt wird.
use Illuminate\Support\Facades\Schedule;
Schedule::command('report:generate')
->fridays()
->at('17:00')
->onOneServer();
Voraussetzung: Der Standard-Cache-Treiber ist database, memcached, dynamodb oder redis, und alle Server verwenden denselben Cache-Server.
Ablauf mit onOneServer()
Tasks gruppieren
Mit group wenden Sie dieselben Einstellungen auf mehrere Tasks an.
use Illuminate\Support\Facades\Schedule;
Schedule::daily()
->onOneServer()
->timezone('Europe/Berlin')
->group(function () {
Schedule::command('emails:send --force');
Schedule::command('emails:prune');
});
Hintergrundausführung
Standardmäßig werden Tasks mit derselben Startzeit sequenziell ausgeführt. Ein langlaufender Task verzögert die nachfolgenden.
Mit runInBackground starten Sie sie parallel.
use Illuminate\Support\Facades\Schedule;
Schedule::command('analytics:report')
->daily()
->runInBackground();
runInBackground funktioniert nur mit command und exec.
Wartungsmodus
Im Wartungsmodus werden Tasks nicht ausgeführt. Mit evenInMaintenanceMode erzwingen Sie die Ausführung trotzdem.
Schedule::command('emails:send')->evenInMaintenanceMode();
Ablauf im Wartungsmodus
Scheduler pausieren
Sie können den Scheduler ohne Codeänderung anhalten.
# Scheduler stoppen
php artisan schedule:pause
# Scheduler fortsetzen
php artisan schedule:continue
Sollen bestimmte Tasks auch bei Pause weiterlaufen (z. B. Health Checks oder System-Monitoring), verwenden Sie evenWhenPaused.
Schedule::command('emails:send')->evenWhenPaused();
Ausgaben verarbeiten
In eine Datei schreiben
Mit sendOutputTo schreiben Sie die Ausgabe in eine Datei.
use Illuminate\Support\Facades\Schedule;
Schedule::command('emails:send')
->daily()
->sendOutputTo(storage_path('logs/emails-send.log'));
Mit appendOutputTo wird die Ausgabe angehängt.
Schedule::command('emails:send')
->daily()
->appendOutputTo(storage_path('logs/emails-send.log'));
Per E-Mail versenden
Mit emailOutputTo verschicken Sie die Ausgabe per Mail.
Setzt eine Mail-Konfiguration voraus.
Schedule::command('report:generate')
->daily()
->sendOutputTo($filePath)
->emailOutputTo('[email protected]');
Nur bei Fehlern versenden: emailOutputOnFailure.
Schedule::command('report:generate')
->daily()
->emailOutputOnFailure('[email protected]');
Task-Hooks
Mit before und after fügen Sie Code vor bzw. nach dem Task ein.
use Illuminate\Support\Facades\Schedule;
Schedule::command('emails:send')
->daily()
->before(function () {
// vor der Ausführung
})
->after(function () {
// nach der Ausführung
});
Erfolgs- und Fehler-Hooks: onSuccess und onFailure.
use Illuminate\Support\Stringable;
Schedule::command('emails:send')
->daily()
->onSuccess(function (Stringable $output) {
// bei Erfolg
})
->onFailure(function (Stringable $output) {
// bei Fehler
});
Deployment auf dem Server
Cron-Eintrag hinzufügen
Fügen Sie in der crontab eine einzige Zeile hinzu, um den Scheduler jede Minute laufen zu lassen.* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
Mit crontab -e bearbeiten Sie die crontab. Scheduler-Betrieb prüfen
Listen Sie die Tasks und die nächsten Ausführungen auf.php artisan schedule:list
Mit Laravel Cloud verwalten Sie Zeitpläne ganz ohne Cron-Konfiguration.
Lokale Entwicklung
Statt Cron nutzen Sie lokal schedule:work, um den Scheduler dauerhaft laufen zu lassen.
php artisan schedule:work
Der Befehl läuft im Vordergrund und ruft den Scheduler jede Minute auf. Beenden mit Strg + C.
Sub-Minuten-Zeitpläne (kürzer als eine Minute)
Cron ist auf eine Minute begrenzt; Laravel bietet aber sekündliche Zeitpläne.
use Illuminate\Support\Facades\Schedule;
Schedule::call(function () {
DB::table('recent_users')->delete();
})->everySecond();
Sind Sub-Minuten-Tasks definiert, läuft schedule:run bis zum Ende der Minute weiter und führt alle Sub-Minuten-Tasks aus.
Delegieren Sie Sub-Minuten-Tasks an Queue-Jobs oder Hintergrund-Kommandos.
Ein langlaufender Task verzögert sonst die nachfolgenden Sub-Minuten-Läufe.
Um einen laufenden schedule:run während eines Deployments zu unterbrechen, fügen Sie dem Deploy-Skript Folgendes hinzu:
php artisan schedule:interrupt
Wichtige Befehle
# Task-Liste anzeigen
php artisan schedule:list
# Scheduler manuell auslösen (das Cron-Kommando)
php artisan schedule:run
# Für die lokale Entwicklung
php artisan schedule:work
# Scheduler pausieren
php artisan schedule:pause
# Scheduler fortsetzen
php artisan schedule:continue
# Lock für Überlappungsschutz löschen
php artisan schedule:clear-cache
# Laufende Sub-Minuten-Tasks unterbrechen (bei Deploys)
php artisan schedule:interrupt
Zuletzt geändert am 13. Juli 2026