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

# Queues und Jobs

> Wie Sie mit Queues und Jobs in Laravel schwere Aufgaben wie E-Mail-Versand oder Bildverarbeitung asynchron im Hintergrund ausführen.

## Was sind Queues

In Web-Anwendungen gibt es Vorgänge – z. B. E-Mail-Versand, Bildskalierung oder Aufrufe externer APIs – die mehrere Sekunden dauern können.
Erledigen Sie diese synchron innerhalb eines HTTP-Requests, muss der Nutzer bis zur Antwort warten.

Mit Laravels Queues erledigen Sie solche schweren Aufgaben **asynchron im Hintergrund**.
Der Request liefert sofort die Antwort, die eigentliche Arbeit übernimmt ein Worker-Prozess.

<Info>
  Queues unterstützen mehrere Backends wie Datenbank, Redis oder Amazon SQS.
  In der Entwicklung führt der Treiber `sync` Jobs direkt aus, ohne eine echte Queue zu nutzen.
</Info>

```mermaid theme={null}
flowchart LR
    A["Job erzeugen<br>dispatch()"] --> B["Queue-Treiber<br>(Redis/DB o. Ä.)"]
    B --> C["Worker<br>queue:work"]
    C --> D{"Erfolgreich?"}
    D -->|"Ja"| E["Fertig"]
    D -->|"Nein"| F{"Retry möglich?"}
    F -->|"Ja"| B
    F -->|"Nein"| G["Fehler protokollieren<br>failed_jobs"]
```

## Queue-Konfiguration

### config/queue.php

Die Queue-Konfiguration liegt in `config/queue.php`.
Über die Umgebungsvariable `QUEUE_CONNECTION` wählen Sie den Treiber.

```php theme={null}
// config/queue.php
'default' => env('QUEUE_CONNECTION', 'database'),
```

### `.env`-Konfiguration

```ini theme={null}
# Treiber wählen
QUEUE_CONNECTION=database

# Für Redis
# QUEUE_CONNECTION=redis
# REDIS_HOST=127.0.0.1
# REDIS_PORT=6379
```

### Vorbereitung des Datenbank-Treibers

Für den `database`-Treiber braucht es eine Tabelle. In neuen Laravel-11-Projekten ist die Migration bereits enthalten – andernfalls erzeugen Sie sie so:

```shell theme={null}
php artisan make:queue-table
php artisan migrate
```

### Vorbereitung des Redis-Treibers

Für den `redis`-Treiber ergänzen Sie in `config/database.php` die Redis-Verbindung und installieren den Client.

```shell theme={null}
composer require predis/predis
```

### SQS Overflow Storage

Amazon SQS begrenzt die Größe der Nachrichten-Payload. Für Jobs mit großen Payloads speichern Sie den Überschuss in einem Cache-Store und schicken an SQS nur einen Pointer.

```php theme={null}
'sqs' => [
    // ...
    'overflow' => [
        'enabled' => env('SQS_OVERFLOW_ENABLED', false),
        'store' => env('SQS_OVERFLOW_STORE'),
        'always' => false,
        'delete_after_processing' => true,
        'flush_on_clear' => env('SQS_OVERFLOW_FLUSH_ON_CLEAR', false),
    ],
],
```

* Mit `enabled` werden Payloads **ab 1 MB** in den angegebenen Cache-Store ausgelagert.
* Mit `always: true` werden alle SQS-Payloads unabhängig von der Größe im Cache-Store abgelegt.
* `delete_after_processing` entfernt gespeicherte Payloads nach erfolgreicher Verarbeitung (Standard: `true`).
* `flush_on_clear` löscht bei `queue:clear` den Overflow-Store. Nutzen Sie hier einen dedizierten Store, damit nicht der normale Cache mitgeleert wird.

## Job-Klassen erstellen

### `make:job`

Erzeugen Sie ein Grundgerüst mit dem Artisan-Befehl `make:job`.

```shell theme={null}
php artisan make:job SendWelcomeEmail
```

Dies erzeugt `app/Jobs/SendWelcomeEmail.php`.

### Aufbau einer Job-Klasse

```php theme={null}
<?php

namespace App\Jobs;

use App\Models\User;
use App\Mail\WelcomeMail;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Mail;

class SendWelcomeEmail implements ShouldQueue
{
    use Queueable;

    /**
     * Erzeugt eine neue Job-Instanz.
     */
    public function __construct(
        public User $user,
    ) {}

    /**
     * Führt den Job aus.
     */
    public function handle(): void
    {
        Mail::to($this->user->email)->send(new WelcomeMail($this->user));
    }
}
```

Das Interface `ShouldQueue` signalisiert Laravel, dass der Job in einer Queue asynchron verarbeitet werden soll.
Das Trait `Queueable` liefert die für Queue-Operationen benötigten Methoden.

<Tip>
  Übergeben Sie dem Konstruktor ein Eloquent-Modell, serialisiert Laravel automatisch nur die ID.
  Bei der Ausführung werden die Daten frisch aus der Datenbank geladen – die Payload bleibt schlank.
</Tip>

## Jobs dispatchen

### `dispatch()`

Aus Controllern oder Services stellen Sie einen Job mit `dispatch()` in die Queue.

```php theme={null}
use App\Jobs\SendWelcomeEmail;

// In einer Route oder einem Controller
public function register(Request $request): RedirectResponse
{
    $user = User::create($request->validated());

    // Job in die Queue stellen
    SendWelcomeEmail::dispatch($user);

    return redirect('/dashboard');
}
```

### Verzögerter Dispatch

Mit `delay()` verschieben Sie die Ausführung.

```php theme={null}
// In 5 Minuten ausführen
SendWelcomeEmail::dispatch($user)->delay(now()->addMinutes(5));
```

### `dispatchAfterResponse()`

Mit `dispatchAfterResponse()` läuft der Job **unmittelbar nach** dem Ausliefern der HTTP-Antwort. Es funktioniert auch mit dem `sync`-Treiber und eignet sich daher für kleine Aufgaben, die keinen eigenen Worker brauchen.

```php theme={null}
SendWelcomeEmail::dispatchAfterResponse($user);
```

### Dispatch in eine bestimmte Queue

```php theme={null}
SendWelcomeEmail::dispatch($user)->onQueue('emails');
```

### Queue Routing

Um bestimmte Job-Klassen standardmäßig einer bestimmten Verbindung oder Queue zuzuordnen, verwenden Sie in der `boot()`-Methode eines Service-Providers `Queue::route()`. So verwalten Sie Zuordnungen zentral, ohne in jeder Job-Klasse `onQueue()` / `onConnection()` zu schreiben.

```php theme={null}
use App\Concerns\RequiresVideo;
use App\Jobs\ProcessPodcast;
use App\Jobs\ProcessVideo;
use Illuminate\Support\Facades\Queue;

public function boot(): void
{
    Queue::route(ProcessPodcast::class, connection: 'redis', queue: 'podcasts');
    Queue::route(RequiresVideo::class, queue: 'video');
}
```

Sie können auch Interfaces, Traits oder Elternklassen angeben – die Regel gilt dann für alle Jobs, die diese implementieren, verwenden oder erweitern.

Mehrere Jobs auf einmal:

```php theme={null}
Queue::route([
    ProcessPodcast::class => ['podcasts', 'redis'], // queue und connection
    ProcessVideo::class => 'videos',                // nur queue (Standardverbindung)
]);
```

<Info>
  Queue Routing lässt sich vom Job aus über `onQueue()` / `onConnection()` überschreiben.
</Info>

### Synchrone Ausführung (Test/Entwicklung)

`dispatchSync()` führt den Job unmittelbar aus, ohne die Queue zu benutzen.

```php theme={null}
SendWelcomeEmail::dispatchSync($user);
```

### Bulk-Dispatch

Wenn Sie viele unabhängige Jobs auf einmal dispatchen möchten, verwenden Sie die Methode `bulk()` der Fassade `Bus`. Ideal, wenn Sie keine Batch-Nachverfolgung oder Callbacks benötigen.

`Bus::bulk()` gruppiert die Jobs nach Verbindung und Queue-Name und pusht jede Gruppe gesammelt – das ist effizient.

```php theme={null}
use App\Jobs\ProcessUser;
use Illuminate\Support\Facades\Bus;

Bus::bulk(
    $users->map(fn ($user) => new ProcessUser($user))
);
```

<Info>
  `Bus::bulk()` schickt Jobs **gesammelt als Batch** in die Queue. Anders als bei Batch-Verarbeitung (`Bus::batch()`) gibt es kein Fortschritts-Tracking und keine Completion-Callbacks. Ideal, um viele unabhängige Jobs einfach in großem Volumen zu senden.
</Info>

## Jobs verarbeiten

### `queue:work`

Starten Sie einen Queue-Worker, um Jobs abzuarbeiten.

```shell theme={null}
php artisan queue:work
```

Sie können Treiber oder Queue gezielt angeben.

```shell theme={null}
# Nur die Queue "emails" auf Redis
php artisan queue:work redis --queue=emails

# database-Treiber
php artisan queue:work database
```

<Warning>
  `queue:work` läuft dauerhaft. Nach Codeänderungen starten Sie die Worker mit `queue:restart` neu.
  In Produktion managen Sie sie typischerweise mit einem Prozessmanager wie Supervisor.
</Warning>

## Optionen zur Worker-Steuerung

Häufig genutzte Optionen können Sie kombinieren.

```shell theme={null}
php artisan queue:work --tries=3 --timeout=60 --sleep=3
```

| Option         | Beschreibung                                                                               |
| -------------- | ------------------------------------------------------------------------------------------ |
| `--tries=N`    | Maximale Versuche eines Jobs. Danach gilt der Job als fehlgeschlagen                       |
| `--timeout=N`  | Maximaldauer eines Jobs in Sekunden. Bei Überschreitung wird der Worker abgebrochen        |
| `--sleep=N`    | Wartezeit in Sekunden bis zur nächsten Poll-Anfrage, wenn die Queue leer ist (Standard: 3) |
| `--max-jobs=N` | Beendet den Worker nach N verarbeiteten Jobs                                               |
| `--max-time=N` | Beendet den Worker nach N Sekunden                                                         |
| `--queue=A,B`  | Bearbeitet die Queues mit Priorisierung (A zuerst)                                         |

### Retry-Einstellungen in der Job-Klasse

Statt Kommandozeilenoptionen kann es übersichtlicher sein, die Einstellungen direkt in der Job-Klasse zu definieren.

```php theme={null}
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Queue\Attributes\Tries;
use Illuminate\Queue\Attributes\Timeout;

#[Tries(3)]
#[Timeout(60)]
class SendWelcomeEmail implements ShouldQueue
{
    use Queueable;

    // ...
}
```

## Job wieder freigeben (Release-Middleware)

Wenn ein Job unter bestimmten Bedingungen nicht ausgeführt, sondern zurück in die Queue gelegt werden soll, verwenden Sie die `Release`-Middleware.

```php theme={null}
use Illuminate\Queue\Middleware\Release;

/**
 * Middleware zurückgeben, die der Job durchläuft.
 */
public function middleware(): array
{
    return [
        // Wenn $condition true ist, in 60 Sekunden erneut freigeben
        Release::when($this->order->isPending(), releaseAfter: 60),
    ];
}
```

`Release::unless()` gibt frei, wenn die Bedingung `false` ist.

```php theme={null}
return [
    // Solange die Bestellung nicht bezahlt ist, in 60 Sekunden erneut freigeben
    Release::unless($this->order->isPaid(), releaseAfter: 60),
];
```

Für komplexere Bedingungen übergeben Sie eine Closure.

```php theme={null}
return [
    Release::when(function (): bool {
        return ! $this->order->isPaid();
    }, releaseAfter: 60),
];
```

<Warning>
  Ein Release erhöht den Versuchszähler des Jobs. Setzen Sie `#[Tries]` bzw. `$tries` passend.
</Warning>

## Fehlgeschlagene Jobs

### Tabelle `failed_jobs` einrichten

Überschreitet ein Job die maximale Anzahl an Versuchen, landet er in der Tabelle `failed_jobs`.
Falls die Tabelle fehlt:

```shell theme={null}
php artisan make:queue-failed-table
php artisan migrate
```

### Aufräumen im Fehlerfall

Definieren Sie in der Job-Klasse die Methode `failed()`, um im Fehlerfall aufzuräumen.

```php theme={null}
use Throwable;

public function failed(?Throwable $exception): void
{
    // z. B. eine Slack-Benachrichtigung an Admins senden
    // Notification::route('slack', config('app.slack_webhook'))
    //     ->notify(new JobFailedNotification($this, $exception));
}
```

### Retries für bestimmte Exceptions unterbinden

Manchmal möchte man bei bestimmten Exceptions gar nicht erst retryen. In `bootstrap/app.php` innerhalb von `withExceptions()` verwenden Sie `dontRetry`.

```php theme={null}
use App\Exceptions\InvalidPodcastSourceException;
use Illuminate\Foundation\Configuration\Exceptions;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->dontRetry([
        InvalidPodcastSourceException::class,
    ]);
})
```

Für feinere Kontrolle nutzen Sie `dontRetryWhen` mit einer Closure. Gibt sie `true` zurück, wird der Job sofort als fehlgeschlagen markiert und nicht mehr retryet.

```php theme={null}
use App\Exceptions\PodcastProcessingException;
use Illuminate\Foundation\Configuration\Exceptions;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->dontRetryWhen(function (PodcastProcessingException $e) {
        return $e->reason() === 'Subscription expired';
    });
})
```

<Tip>
  Bei Fehlern, deren Ergebnis sich durch Retries nicht ändert – etwa Validierungsfehler oder eine abgelaufene Zahlungs-Subscription – ist ein sofortiges Fehlschlagen effizienter.
</Tip>

### Liste fehlgeschlagener Jobs

```shell theme={null}
php artisan queue:failed
```

### Fehlgeschlagene Jobs erneut ausführen

```shell theme={null}
# Bestimmte Job-ID retryen
php artisan queue:retry ce7bb17c-cdd8-41f0-a8ec-7b4fef4e5ece

# Alle retryen
php artisan queue:retry all
```

### Fehlgeschlagene Jobs löschen

```shell theme={null}
# Einzelnen Job löschen
php artisan queue:forget ce7bb17c-cdd8-41f0-a8ec-7b4fef4e5ece

# Alle löschen
php artisan queue:flush
```

## Häufig verwendete Queue-Treiber

### `database`-Treiber

Ein einfacher Treiber, ohne zusätzliche Infrastruktur direkt einsetzbar.
Jobs landen in der Tabelle `jobs`, Worker pollen diese und verarbeiten sie.

* **Vorteile**: einfache Einrichtung, nutzt Ihre vorhandene RDBMS
* **Nachteile**: erhöht die DB-Last – für sehr viele Jobs weniger geeignet

```ini theme={null}
QUEUE_CONNECTION=database
```

### `redis`-Treiber

Der in Produktion am häufigsten verwendete, schnelle Treiber.
Da Redis im Speicher arbeitet, sind Durchsatz und Skalierung deutlich besser.

* **Vorteile**: schnell, skalierbar
* **Nachteile**: Redis-Server erforderlich

```ini theme={null}
QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
```

<Tip>
  Für Redis-Queues in Produktion sollten Sie [Laravel Horizon](https://laravel.com/docs/horizon) in Betracht ziehen.
  Es bietet ein schönes Dashboard mit Echtzeit-Überblick über die Jobs.
</Tip>

## Produktivbetrieb mit Supervisor

In Produktion sollten `queue:work`-Prozesse bei einem Ausfall automatisch neu starten.
Unter Linux ist **Supervisor** der Klassiker.

```ini theme={null}
# /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/your-app/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/your-app/storage/logs/worker.log
stopwaitsecs=3600
```

Mit `numprocs=2` starten Sie zwei Worker parallel.
Danach Supervisor neu einlesen:

```shell theme={null}
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start laravel-worker:*
```

## Beispiel: E-Mail-Versand via Queue

<Steps>
  <Step title="Job-Klasse erstellen">
    ```shell theme={null}
    php artisan make:job SendOrderConfirmation
    ```
  </Step>

  <Step title="Job-Logik implementieren">
    ```php theme={null}
    <?php

    namespace App\Jobs;

    use App\Models\Order;
    use App\Mail\OrderConfirmed;
    use Illuminate\Contracts\Queue\ShouldQueue;
    use Illuminate\Foundation\Queue\Queueable;
    use Illuminate\Queue\Attributes\Tries;
    use Illuminate\Queue\Attributes\Timeout;
    use Illuminate\Support\Facades\Mail;

    #[Tries(3)]
    #[Timeout(30)]
    class SendOrderConfirmation implements ShouldQueue
    {
        use Queueable;

        public function __construct(
            public Order $order,
        ) {}

        public function handle(): void
        {
            Mail::to($this->order->user->email)
                ->send(new OrderConfirmed($this->order));
        }
    }
    ```
  </Step>

  <Step title="Aus dem Controller dispatchen">
    ```php theme={null}
    use App\Jobs\SendOrderConfirmation;

    public function store(Request $request): RedirectResponse
    {
        $order = Order::create($request->validated());

        SendOrderConfirmation::dispatch($order);

        return redirect()->route('orders.show', $order)
            ->with('success', 'Bestellung angenommen.');
    }
    ```
  </Step>

  <Step title="Worker starten">
    ```shell theme={null}
    php artisan queue:work --tries=3 --timeout=30
    ```
  </Step>
</Steps>

## Zusammenfassung

<AccordionGroup>
  <Accordion title="Wann Queues nutzen?">
    * E-Mail-/SMS-Versand
    * Bilder-/Videokonvertierung
    * Aufrufe externer APIs
    * Report- oder CSV-Erstellung
    * Webhook-Versand
  </Accordion>

  <Accordion title="Tipp für die Entwicklung">
    Setzen Sie in der `.env` `QUEUE_CONNECTION=sync`, werden Jobs direkt ausgeführt.
    So funktioniert alles auch ohne Worker – ideal beim Entwickeln.

    ```ini theme={null}
    QUEUE_CONNECTION=sync
    ```
  </Accordion>

  <Accordion title="Wichtige Befehle">
    ```shell theme={null}
    # Worker starten
    php artisan queue:work

    # Worker neu starten (nach Deployment)
    php artisan queue:restart

    # Liste fehlgeschlagener Jobs
    php artisan queue:failed

    # Alle fehlgeschlagenen Jobs erneut ausführen
    php artisan queue:retry all

    # Alle fehlgeschlagenen Jobs löschen
    php artisan queue:flush
    ```
  </Accordion>
</AccordionGroup>


## Related topics

- [Context](/de/context.md)
- [Task-Scheduling](/de/scheduling.md)
- [Einführung in Laravel Nightwatch](/de/blog/nightwatch-introduction.md)
- [Laravel Telescope – Praxistechniken](/de/blog/telescope-introduction.md)
- [E-Mail-Versand](/de/mail.md)
