Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Win32Servicebundle Laravel Package

win32service/win32servicebundle

Symfony bundle for running and managing PHP applications as native Windows services using win32service. Provides service commands, configuration, and straightforward integration for Windows deployments.

View on GitHub
Deep Wiki
Context7

Getting Started

Before installing, be clear on what this is: win32service/Win32ServiceBundle is a Symfony bundle, not a Laravel package. It has no service provider, no facade, and no artisan commands. It gives you a thin, config-driven wrapper around the ext-win32service PECL extension so a PHP CLI app can be registered, started, stopped, and deleted as a native Windows service. In a Laravel codebase you can still use its console binary for the "run a long-lived process as a service" job, but you will be hand-wiring it rather than composer require-ing it into config/app.php.

Minimum path to something working:

  1. Windows host only. Confirm PHP_OS_FAMILY === 'Windows' and that ext-win32service is installed and enabled in the same php.ini used by the CLI binary (php -m | findstr win32service). The extension build must match your PHP version, architecture (x64/x86), and thread-safety mode.
  2. Install into a Symfony-style app or a dedicated runner. composer require win32service/win32service-bundle. If you are in a Laravel app, the pragmatic pattern is a small separate runner project (or a bin/-level console) that boots your Laravel kernel and is what the service actually executes.
  3. Look first at the bundle's console commands, not the docs: win32service:bundle:dump, :import, :create, :delete, :run. dump shows the resolved config for the current machine; import reverse-engineers an existing Windows service into config you can version-control. Those two are the fastest way to learn the config schema.
  4. First use case: register a single long-running consumer (queue worker, scheduler loop, websocket server) as an auto-start service, then verify with sc query <name> and the Windows Event Viewer.

Minimum config, in config/packages/win32service.yaml (or a standalone YAML you pass to the binary):

win32service:
    services:
        app_worker:
            display_name: "App Queue Worker"
            description: "Runs the queue consumer"
            start_type: WIN32_SERVICE_AUTO_START
            delayed_start: true
            script: "C:\\php\\php.exe"
            params: ["C:\\app\\artisan", "queue:work", "--tries=3"]
            log_level: 2

Then win32service:bundle:create app_worker.

Implementation Patterns

Treat the service config as deployable code. Keep the YAML in the repo, have CI produce it per environment (paths and the PHP binary differ between staging and production), and drive every change through create / delete rather than the Windows Services GUI. Services edited by hand drift from the repo and dump will eventually surprise you.

One service per long-lived process. Windows service recovery, start types, and stop signals are per-service. Bundling a queue worker and a scheduler into one console that forks internally makes recovery actions meaningless — if the scheduler crashes, Windows restarts everything. Register app_worker, app_scheduler, app_horizon-equivalent as separate entries with their own start_type, recovery, and delayed_start.

Use delayed_start for anything that depends on the network or database. Services started at boot race the network stack and SQL Server. delayed_start: true plus explicit dependencies on MSSQLSERVER/Tcpip is far more reliable than retry loops inside your command.

Wire recovery actions explicitly. Default recovery is "take no action," so a crashed worker just stays dead. Set recovery to restart on first/second/subsequent failure with a recovery_delay, and keep the delay long enough that a fast-crashing process doesn't spin the CPU. This is the single highest-value config knob and it is easy to forget.

Run as a dedicated service account, not LocalSystem. user / password / domain in the service definition. LocalSystem has a different %USERPROFILE%, different network identity, and more privilege than your app needs. Whichever account you pick needs read/execute on the PHP binary, the app directory, and the storage/log paths.

Log to files you control. The service has no console and no TTY; dump-style stdout goes nowhere useful. Configure the bundle's log_level for the wrapper's own messages and make your application log to disk unconditionally. You will be reading those files plus the Event Viewer during every incident.

Debug on the console first, service second. Anything that works when you run php artisan queue:work interactively but fails as a service is almost always: wrong php.ini (extension not loaded for the service-run binary), working-directory assumptions, environment variables not present in the service context, or the account lacking permissions. Reproduce by running the exact script + params string from a shell as the service account.

Deployment flow that stays sane: stop service → deploy code → bundle:delete + bundle:create (or just create with overwrite) if the service definition changed → start service → smoke-test. Do not rely on the service picking up config changes; the definition is registered with the SCM, not re-read per start.

In a Laravel shop, keep the runner thin. The service entry point should be "boot framework, dispatch one command, exit." Do not put domain logic in the runner — you lose testability and can only debug it on a Windows box.

Gotchas and Tips

  • This release changes nothing operationally. v2.1.0 is maintenance only: CI action bumps, PHP 8.4/8.5 added to the test matrix, and Symfony 7.4 compatibility. There are no new config options, no deprecations, and no breaking changes from 2.0.4 — upgrading is a safe minor bump with zero migration work. If you are currently pinned to 2.0.x, the reason to move is simply that 8.4/8.5 and Symfony 7.4 are now covered by the maintainers' tests.
  • PHP 8.4/8.5 support is a test-matrix claim, not a guarantee for your extension build. The bundle being tested against 8.5 does not mean the ext-win32service PECL release you have installed supports it. Verify the extension loads before assuming compatibility — the extension is the fragile half of this stack, not the bundle.
  • Symfony 7.4 compatibility matters only if you run the bundle inside Symfony. For Laravel users this line in the changelog is irrelevant; don't let it imply Laravel support.
  • Never composer require this into a Linux container. The extension is Windows-only, so an unconditional requirement breaks your CI and your Docker build. Require it conditionally or keep it out of the main app entirely. Guard any code that references it with a PHP_OS_FAMILY check.
  • Admin rights are required for create / delete, and for starting/stopping services owned by another account. An "access denied" on create is a UAC/Session-0 issue, not a config syntax error.
  • Quoting in script and params is the #1 source of silent failures. Paths with spaces break unless every element is quoted correctly; a service can register successfully and then fail instantly on start. Test the assembled command line manually before blaming the bundle.
  • Exit codes are the contract. Return 0 for a clean stop; anything non-zero marks the service as failed and triggers your recovery actions. A worker that exits with a non-zero code on ordinary shutdown will look like a crash loop in the Event Viewer forever.
  • Handle stop requests. Windows expects the process to respond to the stop control within a timeout. Long-running jobs that ignore it get killed hard, mid-transaction. Check for stop signals between jobs.
  • dump and import are your debugging primitives. When a service behaves differently from what's in the YAML, dump the live definition — someone edited it in the GUI, or a previous create left stale values.
  • Extension points are thin on purpose. The bundle is a config-to-SCM adapter; customisation happens by writing your own command and configuring the service around it, not by extending bundle internals. If you find yourself wanting to subclass the bundle, you probably want your own small runner instead.
  • Watch the upstream activity signal. A release consisting of Dependabot bumps plus one compatibility PR is a "still maintained, low velocity" signal — fine for a stable, narrow-scope tool, but don't expect rapid fixes for the extension-level problems that will actually bite you.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log