Une file d'attente qui marche sur votre poste peut se comporter autrement sur un serveur : worker qui s'arrête sans bruit, job rejoué deux fois, jobs en échec qui s'accumulent, code qui n'est pas pris en compte après un déploiement. Voici les réglages qui évitent ces cas, d'après la documentation de Laravel.
Pourquoi un worker doit-il être supervisé ?
php artisan queue:work est un processus long. Il s'arrête quand un job dépasse son délai, après queue:restart ou si le serveur redémarre. Il faut donc un gestionnaire de processus qui le relance, en général Supervisor :
; /etc/supervisor/conf.d/laravel-worker.conf [program:laravel-worker] process_name=%(program_name)s_%(process_num)02d command=php /var/www/app/artisan queue:work redis --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/app/storage/logs/worker.log stopwaitsecs=3600
numprocs fixe le nombre de workers en parallèle : sur un petit serveur, deux suffisent souvent. --max-time=3600 fait quitter le worker au bout d'une heure pour libérer la mémoire accumulée. stopwaitsecs doit être supérieur à la durée de votre job le plus long, sinon Supervisor tue le job en cours. Puis :
sudo supervisorctl reread sudo supervisorctl update sudo supervisorctl start "laravel-worker:*"
Quelles valeurs choisir pour Redis, retry_after et timeout ?
Dans config/queue.php, la connexion Redis a un paramètre retry_after (90 secondes par défaut). Au bout de ce délai, un job pas terminé est considéré comme perdu et remis dans la file. Le timeout du job (60 secondes par défaut) doit donc toujours être plus court que retry_after, sinon un second worker reprend le job alors que le premier travaille encore : doublon.
// config/queue.php 'redis' => [ 'driver' => 'redis', 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), 'queue' => env('REDIS_QUEUE', 'default'), 'retry_after' => env('REDIS_QUEUE_RETRY_AFTER', 90), 'block_for' => 5, 'after_commit' => false, ],
block_for fait attendre le worker quelques secondes côté Redis au lieu d'interroger en boucle. Le timeout d'un job demande l'extension PHP PCNTL. En mode Redis Cluster, le nom de la file doit contenir une accolade de hachage, par exemple {default}.
Comment régler les tentatives et le délai entre deux essais ?
Un appel à une API distante échoue de temps en temps : mieux vaut réessayer en espaçant les essais que de rejouer immédiatement. Les réglages se posent sur le job, et priment sur ceux de la ligne de commande :
// app/Jobs/SyncOrderToErp.php namespace App\Jobs; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Queue\Queueable; use Illuminate\Queue\Attributes\Backoff; use Illuminate\Queue\Attributes\Timeout; use Illuminate\Queue\Attributes\Tries; #[Tries(5)] #[Backoff([10, 60, 300])] #[Timeout(60)] class SyncOrderToErp implements ShouldQueue { use Queueable; // ... }
Ici, le job est tenté 5 fois, avec 10 s, puis 60 s, puis 300 s d'attente (le dernier délai se répète). Avant Laravel 13, on écrit les mêmes réglages en propriétés (public $tries, public $backoff, public $timeout).
Un job qui peut être rejoué doit pouvoir l'être sans rien dupliquer (facture, e-mail). Pour éviter deux exécutions simultanées sur le même objet, utilisez ShouldBeUnique ou le middleware WithoutOverlapping.
Que faire des jobs en échec ?
Un job qui épuise ses tentatives est enregistré dans la table failed_jobs (migration livrée avec Laravel, sinon php artisan make:queue-failed-table). Surveillez-la au lieu de la laisser grossir :
php artisan queue:failed php artisan queue:retry <id> php artisan queue:retry all php artisan queue:prune-failed --hours=48
Planifiez queue:prune-failed (par exemple chaque jour) pour supprimer les échecs anciens, et lisez les erreurs avant de relancer : relancer en boucle un job qui échoue pour une raison permanente ne sert à rien.
Que faire à chaque déploiement ?
Un worker garde l'ancien code en mémoire. Après chaque mise en production :
php artisan queue:restart
Les workers finissent leur job en cours puis quittent, et Supervisor les relance avec le nouveau code. C'est aussi le bon réflexe après un changement de configuration, par exemple quand env() renvoie null à cause des caches, ou après une mise à jour de PHP.
- Supervisor relance les workers :
numprocs,--max-timeet unstopwaitsecsplus long que le job le plus long. timeoutdu job toujours inférieur àretry_after, sinon doublons.#[Tries]et#[Backoff]pour espacer les essais, jobs idempotents.queue:prune-failedplanifié, etqueue:restartà chaque déploiement.
Source : documentation officielle des files d'attente de Laravel.
Questions fréquentes
Pourquoi mon job est-il exécuté deux fois ?
Souvent parce que son timeout dépasse retry_after : la file le remet à disposition alors que le premier worker travaille encore. Gardez le timeout plus court que retry_after, et rendez le job idempotent.
Combien de workers faut-il lancer ?
Autant que de jobs à traiter en parallèle sans saturer le serveur. Sur un petit serveur, deux workers (numprocs=2) suffisent souvent pour commencer ; augmentez si la file s'allonge.
Mon code ne change pas après un déploiement, pourquoi ?
Les workers sont des processus longs qui gardent l'ancien code en mémoire. Lancez php artisan queue:restart après chaque déploiement : Supervisor les relancera avec le nouveau code.