Worker kolejki uruchomiony ręcznie przez SSH zginie przy pierwszym rozłączeniu sesji i nie wstanie po restarcie serwera — dlatego na produkcji każdy php artisan queue:work (albo proces php artisan horizon) musi działać pod nadzorcą: Supervisorem lub systemd. Ten poradnik to gotowa konfiguracja obu wariantów, komendy operacyjne i lista błędów, które psują workery najczęściej.
Najważniejsze: Najważniejsze — Supervisor to standard w ekosystemie Laravel (konfiguracja w
/etc/supervisor/conf.d/), systemd to natywna alternatywa bez dodatkowego pakietu. W obu przypadkach kluczowe są:autorestart/Restart=always,numprocsdopasowane do obciążenia istopwaitsecsdłuższy niż--timeoutzadania. Przy Horizonze nadzorujesz jeden procesphp artisan horizon, nie pojedyncze workery.
Dlaczego worker musi mieć nadzorcę
Proces workera ma trzy cechy, które wykluczają ręczne uruchamianie na produkcji
- Ginie z sesją SSH — zamknięcie terminala wysyła SIGHUP i worker kończy pracę.
- Nie wstaje po awarii — wyjątek w rozszerzeniu PHP albo OOM killer ubiją proces i kolejka stoi, dopóki ktoś tego nie zauważy.
- Nie przetrwa restartu serwera — po
rebootnikt go nie uruchomi ponownie.
Nadzorca (Supervisor lub systemd) rozwiązuje wszystkie trzy problemy: startuje workery przy starcie systemu, restartuje po awarii i pozwala zarządzać całą pulą jednym poleceniem. Bez tego „mamy kolejki" oznacza „mamy kolejki, dopóki działa moja sesja SSH".
Supervisor — instalacja i konfiguracja krok po kroku
Supervisor to menedżer procesów dla systemów Linux. Na Debianie/Ubuntu instalujesz go jednym poleceniem
sudo apt install supervisor
sudo systemctl enable supervisor
Konfiguracja workera trafia do /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=4
redirect_stderr=true
stdout_logfile=/var/www/app/storage/logs/worker.log
stopwaitsecs=3600
Po zapisaniu pliku przeładowujesz konfigurację i uruchamiasz procesy
sudo supervisorctl reread # wczytaj nowe pliki konfiguracyjne
sudo supervisorctl update # zastosuj zmiany
sudo supervisorctl status # stan wszystkich procesów
sudo supervisorctl restart laravel-worker:* # restart całej puli
Co robi każda opcja
| Opcja | Znaczenie | Typowy błąd |
|---|---|---|
numprocs |
Liczba równoległych workerów | Za mało → kolejka rośnie; za dużo → workery konkurują o CPU i bazę |
autostart |
Start przy starcie Supervisora | false → po restarcie serwera kolejka stoi |
autorestart |
Restart po zakończeniu procesu | false → awaria workera zatrzymuje kolejkę na zawsze |
stopwaitsecs |
Ile sekund czekać na zakończenie bieżącego zadania przy stopie | Krótszy niż --timeout → Supervisor zabija zadanie w połowie |
user |
Użytkownik procesu | root → pliki tworzone przez zadania mają złe uprawnienia |
stdout_logfile |
Log wyjścia procesu | Brak → problemy workerów są niewidoczne |
Uwaga:
stopwaitsecsmusi być dłuższy niż najdłuższe zadanie (uwzględnij--timeoutworkera). Jeśli Supervisor ubije proces w trakcie zadania, zadanie wróci do kolejki i wykona się drugi raz — a bez idempotencji zdubluje operację.
Ile procesów (numprocs)?
- Punkt startowy: liczba rdzeni CPU. Zadania I/O-bound (integracje API, maile, PDF) mogą mieć 2–3× więcej procesów niż rdzeni — większość czasu czekają na sieć. Zadania CPU-bound (przetwarzanie obrazów, raporty) nie powinny przekraczać liczby rdzeni, inaczej workery wzajemnie się spowalniają. Sygnał do zmiany: w Horizon dashboard widać stałą kolejkę oczekujących i wysokie czasy oczekiwania przy niewykorzystanym CPU — dodaj procesy.
systemd — alternatywa bez dodatkowego pakietu
Jeśli nie chcesz instalować Supervisora, każdy nowoczesny Linux ma systemd. Unit dla workera: /etc/systemd/system/laravel-worker@.service (wariant z wieloma instancjami):
[Unit]
Description=Laravel queue worker %i
After=network.target postgresql.service redis.service
[Service]
User=www-data
Group=www-data
Restart=always
RestartSec=5
WorkingDirectory=/var/www/app
ExecStart=/usr/bin/php /var/www/app/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
TimeoutStopSec=3600
[Install]
WantedBy=multi-user.target
Uruchamiasz cztery instancje
sudo systemctl daemon-reload
sudo systemctl enable --now laravel-worker@1 laravel-worker@2 laravel-worker@3 laravel-worker@4
systemctl status 'laravel-worker@*'
Odpowiedniki opcji Supervisora: Restart=always → autorestart, RestartSec=5 → odstęp między restartami, TimeoutStopSec → stopwaitsecs. Nazwa instancji (%i) pozwala skalować pulę bez tworzenia osobnych plików.
Supervisor czy systemd? Oba działają. Supervisor daje czytelniejszy podgląd wielu procesów (supervisorctl status pokazuje całą pulę w jednej tabeli) i jest lepiej udokumentowany w kontekście Laravela. systemd nie wymaga instalacji pakietu i integruje się z resztą systemu (journalctl -u). W projektach na VPS używamy obu — wybór zależy od tego, co już działa na serwerze.
Supervisor + Horizon — konfiguracja dla Laravel Horizon
Gdy używasz Laravel Horizon, nie konfigurujesz workerów ręcznie. Horizon sam zarządza pulą procesów na podstawie config/horizon.php, a Supervisor nadzoruje tylko jeden proces:
[program:laravel-horizon]
process_name=%(program_name)s
command=php /var/www/app/artisan horizon
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/www/app/storage/logs/horizon.log
stopwaitsecs=3600
Po wdrożeniu nowego kodu wywołujesz php artisan horizon:terminate — Horizon kończy bieżące zadania i kończy proces, a Supervisor natychmiast startuje go ponownie z nowym kodem. To horizonowy odpowiednik queue:restart.
- Zaleta tego układu: liczbę procesów zmieniasz w
config/horizon.php(bez dotykania Supervisora i bez restartu serwera), a strategiaautosama dokłada procesy, gdy kolejka rośnie. Konfigurację Horizon z autoskalowaniem opisuję w poradniku o kolejkach i Horizonie.
Wdrożenie (deploy) — restart workerów w skrypcie
Każdy deploy, który zmienia kod, musi zrestartować workery — inaczej działają na starym kodzie w pamięci. Minimalny skrypt wdrożeniowy:
php artisan down --render="errors::503"
git pull origin main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache && php artisan route:cache && php artisan view:cache
php artisan queue:restart # albo: php artisan horizon:terminate
php artisan up
Przy Supervisorze queue:restart nie restartuje procesów — wysyła sygnał do workerów, żeby zakończyły się po bieżącym zadaniu; Supervisor natychmiast je wskrzesza z nowym kodem. Cały mechanizm jest bezpieczny: żadne zadanie nie zostaje ubite w połowie. Więcej o automatyzacji wdrożeń w tekście CI/CD i DevOps dla aplikacji Laravel.
Typowe błędy i jak je diagnozować
- Worker nie startuje —
sudo supervisorctl statuspokażeFATAL; log zwykle wskazuje zły ścieżki wcommandalbo brak uprawnień. Sprawdźphp /var/www/app/artisan --versionz poziomu użytkownika z konfiguracji. - Zadania „wykonują się" po wdrożeniu na starym kodzie — brak
queue:restart/horizon:terminatew skrypcie deployu. - Pliki z zadań mają właściciela
root— worker działa jako root; ustawuser=www-datai napraw uprawnienia istniejących plików. - Zadanie zabijane w połowie —
stopwaitsecskrótszy niż--timeout; zwiększ oba. - Kolejka rośnie mimo wolnego CPU — za mało procesów albo zewnętrzne API odpowiada wolno; sprawdź czasy w Horizon i rozważ rate limiting.
- Workery padają co kilka godzin — wyciek pamięci w zadaniu; ustaw
--max-time=3600, żeby worker regularnie się odświeżał, i poszukaj źródła (najczęściej statyczne cache w bibliotece).
Najczęściej zadawane pytania
Czy Supervisor jest wymagany, żeby używać kolejek w Laravel?
Nie — kolejki działają też bez nadzorcy, ale tylko lokalnie. Na produkcji worker bez Supervisora (lub systemd) ginie przy rozłączeniu SSH, nie wstaje po awarii i nie przetrwa restartu serwera. Każde poważne wdrożenie używa jednego z tych dwóch nadzorców.
Supervisor czy systemd — co wybrać?
Oba są poprawne. Supervisor daje wygodniejszy podgląd wielu procesów i jest standardem w dokumentacji Laravel. systemd nie wymaga instalowania pakietu i jest spójny z resztą systemu. Jeśli na serwerze działa już Supervisor (np. dla innych aplikacji) — używaj Supervisora; jeśli nie — systemd wystarczy.
Jak zmienić liczbę workerów?
- W Supervisorze: zmień
numprocsi wykonajsupervisorctl reread && supervisorctl update. W systemd: włącz lub wyłącz kolejne instancje (systemctl enable --now laravel-worker@5). W Horizonie: zmieńmaxProcesseswconfig/horizon.phpi wywołajhorizon:terminate— dalej Horizon zarządza pulą sam.
Co się dzieje z zadaniami podczas restartu workera?
Nic złego — queue:restart i horizon:terminate są „grzeczne": worker kończy bieżące zadanie, dopiero potem kończy proces. Zadanie w trakcie wykonywania nie zostaje ubite, a nowy worker startuje z nowym kodem. Warunek: stopwaitsecs/TimeoutStopSec musi być dłuższy niż najdłuższe zadanie.
Jak sprawdzić, czy workery działają?
supervisorctl status (Supervisor) albo systemctl status 'laravel-worker@*' (systemd) pokaże stan procesów. Głębiej: Horizon dashboard (przepustowość i czasy oczekiwania) oraz php artisan queue:monitor redis:default --max=100 — komenda zwraca błąd, gdy kolejka przekroczy próg, więc nadaje się do monitoringu zewnętrznego.
👉 Workery padają, kolejka rośnie, a po wdrożeniach „nic się nie zmienia"? To klasyczne objawy braku nadzorcy albo pominiętego restartu. Opisz projekt na kontakt@informatix.com.pl lub przez formularz — wycena jest bezpłatna.
📖 Zobacz również: Kolejki w Laravel i Laravel Horizon — poradnik, queue:work vs queue:listen, CI/CD i DevOps dla Laravel oraz usługa Laravel development.