Zum Hauptinhalt springen

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

MethodeBeschreibung
->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');

Wochentagsbeschränkungen

MethodeBeschreibung
->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

1

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.
2

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