> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Task-Scheduling

> Wie Sie mit dem Laravel-Scheduler wiederkehrende Aufgaben elegant verwalten.

## 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 theme={null}
<?php

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schedule;

Schedule::call(function () {
    DB::table('recent_users')->delete();
})->daily();
```

<Info>
  Mit dem Artisan-Befehl `schedule:list` sehen Sie definierte Tasks und deren nächste Ausführung.

  ```shell theme={null}
  php artisan schedule:list
  ```
</Info>

### Ausführungsablauf des Schedulers

```mermaid theme={null}
flowchart TD
    A["* * * * * artisan schedule:run<br>(Cron ruft jede Minute auf)"] --> B["Console Kernel"]
    B --> C["schedule() aufrufen"]
    C --> D["Registrierte Tasks der Reihe nach prüfen"]
    D --> E{"Passt der Cron-Ausdruck<br>zur aktuellen Zeit?"}
    E -->|"Nein"| F["Überspringen"]
    E -->|"Ja"| G{"Bedingungen prüfen<br>(when / skip / environments)"}
    G -->|"nicht erfüllt"| F
    G -->|"erfüllt"| H{"Wartungsmodus?"}
    H -->|"Normalbetrieb"| I["Task ausführen<br>(before → Body → after)"]
    H -->|"Wartungsmodus"| J{"evenInMaintenanceMode()<br>gesetzt?"}
    J -->|"ja"| I
    J -->|"nein"| F
```

## Zeitpläne definieren

Zeitpläne stehen in `routes/console.php`.
Alternativ nutzen Sie die Methode `withSchedule` in `bootstrap/app.php`.

```php theme={null}
// 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.

```php theme={null}
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.

```php theme={null}
Artisan::command('delete:recent-users', function () {
    DB::table('recent_users')->delete();
})->purpose('Delete recent users')->daily();
```

### Queue-Jobs

Mit `job` planen Sie [Queue-Jobs](/de/queues) direkt, ohne Closure.

```php theme={null}
use App\Jobs\Heartbeat;
use Illuminate\Support\Facades\Schedule;

Schedule::job(new Heartbeat)->everyFiveMinutes();
```

Sie können Queue-Name und Verbindung angeben.

```php theme={null}
// Dispatch auf Queue "heartbeats" der Verbindung "sqs"
Schedule::job(new Heartbeat, 'heartbeats', 'sqs')->everyFiveMinutes();
```

### Shell-Kommandos

Mit `exec` führen Sie OS-Kommandos aus.

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::exec('node /home/forge/script.js')->daily();
```

### Closures

Mit `call` planen Sie beliebige PHP-Closures.

```php theme={null}
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.

```php theme={null}
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.

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::command('emails:send')->cron('0 9 * * *'); // Täglich um 9
```

### Kombinierte Bedingungen

Frequenz- und Wochentagsmethoden lassen sich kombinieren.

```php theme={null}
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.

```php theme={null}
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`.

```php theme={null}
// config/app.php
'schedule_timezone' => 'Europe/Berlin',
```

<Warning>
  In Zeitzonen mit Sommerzeit können Tasks bei der Umstellung doppelt oder gar nicht ausgeführt werden.
  Nach Möglichkeit UTC verwenden.
</Warning>

## Bedingungen

### `when` / `skip`

`when` führt aus, wenn die Closure `true` liefert; `skip` überspringt bei `true`.

```php theme={null}
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.

```php theme={null}
Schedule::command('emails:send')
    ->daily()
    ->environments(['staging', 'production']);
```

### Zeitfenster

`between` / `unlessBetween` beschränken das Zeitfenster.

```php theme={null}
// 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

| Methode                         | Beschreibung                               |
| ------------------------------- | ------------------------------------------ |
| `->weekdays()`                  | Nur Werktage                               |
| `->weekends()`                  | Nur Wochenenden                            |
| `->mondays()` bis `->sundays()` | Bestimmter Wochentag                       |
| `->days([0, 3])`                | Mehrere Tage (z. B. Sonntag=0, Mittwoch=3) |

```php theme={null}
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.

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::command('emails:send')->withoutOverlapping();
```

Die Lock-Dauer (in Minuten) ist konfigurierbar (Standard: 24 Stunden).

```php theme={null}
// Lock läuft nach 10 Minuten ab
Schedule::command('emails:send')->withoutOverlapping(10);
```

<Info>
  `withoutOverlapping` verwaltet den Lock über den Application-Cache.
  Bei stecken gebliebenen Tasks können Sie den Lock mit `schedule:clear-cache` freigeben.

  ```shell theme={null}
  php artisan schedule:clear-cache
  ```
</Info>

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

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::command('report:generate')
    ->fridays()
    ->at('17:00')
    ->onOneServer();
```

<Warning>
  Voraussetzung: Der Standard-Cache-Treiber ist `database`, `memcached`, `dynamodb` oder `redis`, und alle Server verwenden denselben Cache-Server.
</Warning>

### Ablauf mit `onOneServer()`

```mermaid theme={null}
flowchart TD
    A["Auf mehreren Servern läuft<br>artisan schedule:run gleichzeitig"] --> B["Server 1"]
    A --> C["Server 2"]
    A --> D["Server 3"]

    B --> E["Versuch, atomaren Lock<br>im gemeinsamen Cache zu holen"]
    C --> E
    D --> E

    E -->|"Erfolg (erster Server)"| F["Task ausführen"]
    E -->|"Fehlschlag (andere Server)"| G["Task überspringen"]

    F --> H["Lock nach Ausführung freigeben"]
```

## Tasks gruppieren

Mit `group` wenden Sie dieselben Einstellungen auf mehrere Tasks an.

```php theme={null}
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.

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::command('analytics:report')
    ->daily()
    ->runInBackground();
```

<Warning>
  `runInBackground` funktioniert nur mit `command` und `exec`.
</Warning>

## Wartungsmodus

Im Wartungsmodus werden Tasks nicht ausgeführt. Mit `evenInMaintenanceMode` erzwingen Sie die Ausführung trotzdem.

```php theme={null}
Schedule::command('emails:send')->evenInMaintenanceMode();
```

### Ablauf im Wartungsmodus

```mermaid theme={null}
flowchart TD
    A["artisan schedule:run"] --> B{"App im Wartungsmodus?<br>(artisan down)"}
    B -->|"Normal"| C["Task normal ausführen"]
    B -->|"Wartungsmodus"| D{"evenInMaintenanceMode()<br>gesetzt?"}
    D -->|"ja"| C
    D -->|"nein"| E["Task überspringen"]
```

## Scheduler pausieren

Sie können den Scheduler ohne Codeänderung anhalten.

```shell theme={null}
# 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`.

```php theme={null}
Schedule::command('emails:send')->evenWhenPaused();
```

## Ausgaben verarbeiten

### In eine Datei schreiben

Mit `sendOutputTo` schreiben Sie die Ausgabe in eine Datei.

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::command('emails:send')
    ->daily()
    ->sendOutputTo(storage_path('logs/emails-send.log'));
```

Mit `appendOutputTo` wird die Ausgabe angehängt.

```php theme={null}
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](/de/mail) voraus.

```php theme={null}
Schedule::command('report:generate')
    ->daily()
    ->sendOutputTo($filePath)
    ->emailOutputTo('admin@example.com');
```

Nur bei Fehlern versenden: `emailOutputOnFailure`.

```php theme={null}
Schedule::command('report:generate')
    ->daily()
    ->emailOutputOnFailure('admin@example.com');
```

## Task-Hooks

Mit `before` und `after` fügen Sie Code vor bzw. nach dem Task ein.

```php theme={null}
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`.

```php theme={null}
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

<Steps>
  <Step title="Cron-Eintrag hinzufügen">
    Fügen Sie in der crontab eine einzige Zeile hinzu, um den Scheduler jede Minute laufen zu lassen.

    ```shell theme={null}
    * * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
    ```

    Mit `crontab -e` bearbeiten Sie die crontab.
  </Step>

  <Step title="Scheduler-Betrieb prüfen">
    Listen Sie die Tasks und die nächsten Ausführungen auf.

    ```shell theme={null}
    php artisan schedule:list
    ```
  </Step>
</Steps>

<Tip>
  Mit [Laravel Cloud](https://cloud.laravel.com) verwalten Sie Zeitpläne ganz ohne Cron-Konfiguration.
</Tip>

### Lokale Entwicklung

Statt Cron nutzen Sie lokal `schedule:work`, um den Scheduler dauerhaft laufen zu lassen.

```shell theme={null}
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.

```php theme={null}
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.

<Tip>
  Delegieren Sie Sub-Minuten-Tasks an Queue-Jobs oder Hintergrund-Kommandos.
  Ein langlaufender Task verzögert sonst die nachfolgenden Sub-Minuten-Läufe.
</Tip>

Um einen laufenden `schedule:run` während eines Deployments zu unterbrechen, fügen Sie dem Deploy-Skript Folgendes hinzu:

```shell theme={null}
php artisan schedule:interrupt
```

## Wichtige Befehle

```shell theme={null}
# 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
```


## Related topics

- [Artisan-Konsole](/de/artisan.md)
- [Bot-Tutorial - Laravel Bluesky](/de/packages/laravel-bluesky/bot-tutorial.md)
- [Feed Generator](/de/packages/laravel-bluesky/feed-generator.md)
- [Tutorial - Laravel Console Starter](/de/packages/laravel-console-starter/tutorial.md)
- [Labeler](/de/packages/laravel-bluesky/labeler.md)
