pierotto/supervisor
A Laravel package for working with Supervisor process control: generate and manage supervisor configurations, control and monitor worker processes, and simplify running queue workers and background jobs in your application.
Install the package with Composer and publish/define its config so you have a single source of truth for the programs this app needs:
composer require pierotto/supervisor
Look at the config file first. Everything of interest is the supervisor array: a group key (new in 3.1.0) and a programs map. The README's examples use Symfony's %kernel.project_dir% placeholder syntax, but the generator itself is framework-agnostic — in Laravel use base_path() / config() values instead.
Define one program and generate the file. Start with a single queue worker, run the package's dump/generate command (or call SupervisorConfigGenerator directly from a small artisan command or test), and inspect the resulting .ini. Confirm the [program:...] section carries the command, log paths and restart policy you expect.
Wire it into supervisor. Point supervisor's include directive at the directory where you write the generated file (/etc/supervisor/conf.d/ on most distros), then:
supervisorctl reread
supervisorctl update
supervisorctl status
Set group on day one if you share a host. This is the headline feature of 3.1.0 and is much cheaper to adopt before you have running processes than after:
'group' => 'myapp',
'programs' => [
'worker' => ['command' => 'php '.base_path('artisan').' queue:work'],
],
That emits an extra [group:myapp] section listing worker, and the process becomes addressable as myapp:worker.
First real use case: replace a hand-written .conf for a Horizon/queue worker with a generated one you can regenerate in CI and diff in code review.
Generate at deploy time, not by hand. Treat the supervisor .ini as a build artefact. A deploy step that runs the generator and then reread + update against the app's group is the core loop:
supervisorctl stop myapp:*
# deploy: composer install, migrate, etc.
supervisorctl reread
supervisorctl update myapp
supervisorctl start myapp:*
Quote the glob if your shell would expand it (supervisorctl stop 'myapp:*').
One group per application. The group name should be stable and app-specific (myapp, myapp-staging). This is what lets several projects share a single supervisord without one project's deploy touching another's processes.
Environment-aware config. Build the program array from config() and env() rather than hardcoding paths: log directory under storage_path('logs'), numprocs from an env var, command prefixed with the correct PHP binary. Since the config is plain PHP, you get per-environment program sets for free — just make sure the generated filename and group name differ per environment so staging can't overwrite production.
Group the long-running workers, not one-shot commands. Schedule-style tasks belong in the scheduler/cron, not supervisor. Reserve programs for things that must be supervised: queue workers, websocket servers, consumers.
Program-level tuning belongs in the config array. numprocs, autorestart, stopwaitsecs, stopsignal, stdout_logfile, redirect_stderr are all normal supervisor options and should live next to the command so they're reviewable in the same diff. For workers that fork children, prefer stopasgroup/killasgroup so a stop doesn't orphan grandchildren.
Generator as a collaborator. If you need custom output (multiple files, header comments, per-program groups), instantiate SupervisorConfigGenerator yourself instead of relying on the packaged command. The 3.1.0 constructor takes the group as the last argument with a default, so existing positional instantiation keeps working and you only add the argument — or a named argument — when you want grouping.
Test the generated text. A small PHPUnit test asserting the rendered .ini contains [group:myapp] and programs = worker catches regressions from package upgrades far earlier than a broken deploy.
supervisorctl update <group> needs supervisor 3.2+. Check supervisord --version on the target host before you build the whole deploy script around group-scoped updates.group to an already-running project renames its processes. The old ungrouped worker still exists with the same command, and a group-scoped update will not remove it — you'll end up with duplicate workers competing for the same queue. The README documents a one-off migration; do that (stop the old ungrouped processes, remove their sections, reread, update) in the same deploy that introduces group. Do not simply add the option and redeploy.stop all / reload on shared hosts. supervisorctl reload restarts the supervisord daemon itself and therefore every project on the machine. That's the exact failure mode group exists to prevent.reread then update. reread only parses the config files; update applies them. Running update alone after regenerating the file does nothing useful.group. It defaults to an empty string, so output is byte-for-byte identical to 3.0.0. Safe to bump and deploy without touching config.conf.d, supervisor will complain about a program being claimed twice — usually surfaced as an error during reread. Keep one generated file per group per host.supervisorctl status worker or log filenames containing the bare program name needs updating to the myapp:worker form (and worker_00 style suffixes once numprocs > 1).supervisord -n -c /path/to/generated.ini in the foreground, or watch the reread output — supervisor reports malformed sections there, and it's far better to see that than to stop every worker on the box during a deploy.supervisord (often root). Write to a temp file and rename, or set permissions explicitly, otherwise a partially written file can be picked up by a concurrent reread.storage/logs/supervisor/). Symlinked release folders change target on each deploy, and supervisor keeps the file handle it opened at start — so logs silently land in a deleted release if you put them under the release path.SupervisorConfigGenerator, update your subclass signature to account for the new trailing argument; it's defaulted, so you won't get a fatal error, but a subclass that reorders arguments will silently drop the group.How can I help you explore Laravel packages today?