crazy-goat/workerman-bundle
Run Symfony on Workerman: a pure-PHP event loop HTTP server, scheduler, and supervisor replacing php-fpm, nginx, cron, and supervisord. The kernel and DI container persist between requests for speed, with a composable middleware pipeline, console commands, and no external binaries.
Bottom line for the decision log: this is an infrastructure consolidation package, not a feature package. It informs four decisions and nothing else.
php-fpm + nginx + cron + supervisord into a single PHP process tree? This bundle is the concrete option that makes that question answerable with a spike rather than a rewrite: one composer require, a servers config block, and you have an HTTP server, a cron-expression scheduler, and a process supervisor. The measurable upside is worker boot amortisation — the kernel and DI container persist across requests — plus 3–4 fewer systemd units, fewer config surfaces, and one language to hire and debug in.workerman:build:phar / build:bin commands and runtime_dir / WORKERMAN_RUNTIME_DIR support open a genuinely different product option: shipping the application as a single artifact to on-prem, edge, or air-gapped customers where "install nginx, PHP, supervisord" is a support burden. This is the strongest differentiated use case in the README and it is not something the standard fpm stack offers at all.processes is CPU cores × 2, one supervisor, no nginx worker pool, no cron daemon. For a portfolio of small internal services, that is a real line item in infra cost and in on-call surface.ws:// and wss:// listeners mean real-time features (live status, notifications, dashboards) stop being a separate Node/Go service and a separate deploy target. This is a roadmap enabler: it turns "we'd need a second runtime" into "it's a config key".Explicitly not supported: this is a Symfony-only bundle (^6.4 | ^7.0, plus a claimed ^8.0), not a Laravel package. If the codebase under discussion is Laravel, this decision ends here — the equivalent conversation is about Laravel Octane + RoadRunner/Swoole, and Workerman has no first-class Laravel integration. Do not let the "pure PHP, replaces your stack" framing leak into a Laravel roadmap without that correction.
Adopt for a spike when all of these hold:
pcntl/posix-capable Linux and is comfortable owning process supervision semantics.services_resetter integration (calling it after kernel termination) is the single most load-bearing correctness feature in a long-running worker. Any service holding per-request data in a property, or any singleton with a growing cache, becomes a leak or a cross-request data bug here in a way fpm made impossible.Look elsewhere — or stay on fpm — when any of these hold:
CAP_NET_BIND_SERVICE, and the README itself recommends fronting it with nginx or Caddy in production. So the "replace nginx" pitch largely collapses at the perimeter: you keep a reverse proxy for TLS termination, WAF, rate limiting, and DDoS absorption anyway. Be honest internally that this is "replace php-fpm + cron + supervisord, keep the proxy."luzrain/workerman-bundle with 2 stars, 0 dependents, and a single maintainer. The engineering hygiene is genuinely strong for the size — PHPStan level 8, an 80% coverage floor, PHP-CS-Fixer, Rector, shellcheck/hadolint, a changelog linter, an exception hierarchy of 21 classes, and CI across PHP 8.2–8.5 × Symfony 6.4–8.1 — but hygiene is not a support contract. If the service is revenue-critical and you cannot afford to vendor and maintain a fork yourself, that is a disqualifier regardless of code quality.Two documented sharp edges to raise before any commitment: the README states that static_files.allowed_extensions is silently ignored when using the recommended StaticFilesMiddleware service setup — a security-relevant config knob that no-ops is exactly the kind of thing that becomes an incident. And the Symfony version claim is inconsistent between the README table (^8.0) and the CI matrix (6.4 to 8.1); small, but it tells you the docs are maintained by one person under load.
For executives — one paragraph. We have an option to remove three pieces of infrastructure (php-fpm, cron, supervisord) and run our Symfony services as a single managed PHP process, which cuts per-request boot cost, reduces the number of things that can page us at 3am, and — most interesting commercially — lets us ship our product to on-premise customers as one file instead of an installation guide. The technology is mature (Workerman is widely used) and this integration is well-engineered but very young and maintained by one person, so the recommendation is a time-boxed spike on one non-critical internal service, not a platform migration. Cost of finding out: roughly one engineer-week. Cost of being wrong later: a rewrite of our deploy tooling.
For engineering — the honest version. It's a Symfony bundle wrapping Workerman: async HTTP, a cron-expression scheduler, and a process supervisor, with a middleware pipeline, static file serving with ETag/304 and path-traversal protection, slowloris and keep-alive timeouts, per-server body caps, trusted-host regex checks, binary/streamed response strategies that don't buffer to memory, and PHAR/binary packaging. The trade is: we get a persistent kernel and container (faster, and it unlocks WebSockets), and in exchange we take on long-running-worker discipline. Every stateful service must be reset-safe; the bundle calls services_resetter after termination, but that only covers services Symfony knows are stateful. We lose nginx's per-request process isolation, which is a real safety net. We still need a reverse proxy for TLS and edge protection. And we are adopting a 2-star fork with no dependents — so if this sticks, we vendor it, we pin it, and we budget for maintaining our own patch set.
For the security reviewer. Positives: symlinked SSL cert/key paths are rejected, path traversal and dot-file blocking are handled in the static middleware, trusted_hosts rejects bad Host headers with a 400, and request/keepalive timeouts ship on by default (120s / 30s). Negatives: the perimeter story gets weaker, the allowed_extensions no-op is a config trap to audit, and running as root to bind low ports must be replaced with CAP_NET_BIND_SERVICE or a proxy. Ask for a review of docs/security.md before the spike touches anything with real traffic.
Recommendation. Fund a one-sprint spike behind a feature flag on a single internal, stateless, low-traffic Symfony service. Success criteria: p95 latency no worse than fpm, memory stable over 72 hours under synthetic load, no cross-request state bugs, and the on-call runbook is no longer than the one it replaces. If the real driver is single-artifact distribution to customers, run the spike against the PHAR build path specifically — that is where the differentiated value lives, and it is the only justification strong enough to outweigh the single-maintainer risk.
How can I help you explore Laravel packages today?