A Symfony CQRS demo for exploring how modern PHP runtimes scale. The same app runs three ways - PHP-FPM, FrankenPHP classic, and FrankenPHP worker - so you can load-test them side by side with k6 and watch the difference live. Everything runs in Docker Compose and is driven by the Makefile.
-
One Symfony app (Products / Customers / Orders) using CQRS: writes go to the database, reads come from fast Redis projections.
-
The same app served by three runtimes, so you can compare them fairly:
Runtime Port What it is PHP-FPM 8088 Traditional process-per-request FrankenPHP classic 8080 Modern PHP server on Caddy FrankenPHP worker 8081 Long-lived workers, app kept warm in memory -
Live metrics for all of them (FPM status, OPcache, Caddy/Prometheus) plus Grafana dashboards.
-
🔥 Ember - a one-command live demo that ramps traffic up and down while you watch the numbers move.
Runtimes run PHP 8.4 (FrankenPHP
1.12.4). New here? Start with ember.md.
| Service | URL | Description |
|---|---|---|
| FPM App | http://localhost:8088 | Main Symfony app (FPM) |
| Franken | http://localhost:8080 | FrankenPHP (HTTP, regular mode) |
| Franken Worker | http://localhost:8081 | FrankenPHP Worker (HTTP, optimized) |
| Grafana | http://localhost:3000 | Metrics dashboard (symfony/symfony) |
| Prometheus | http://localhost:9090 | Prometheus metrics |
| Opcache Dashboard | http://localhost:42042 | PHP Opcache dashboard |
| Opcache Metrics (FPM) | http://localhost:8088/metrics | PHP Opcache metrics via FPM app |
| Franken Metrics | http://localhost:2019/metrics | Caddy/FrankenPHP metrics (non-worker) |
| Worker Metrics | http://localhost:2020/metrics | Caddy/FrankenPHP metrics (worker) |
-
Docker & Docker Compose
- Docker Desktop (includes Docker Compose)
- Or install separately: Docker and Docker Compose
-
Make
- macOS: Usually pre-installed, or install via Homebrew:
brew install make - Linux: Install via package manager:
- Ubuntu/Debian:
sudo apt-get install make - CentOS/RHEL:
sudo yum install make - Fedora:
sudo dnf install make
- Ubuntu/Debian:
- macOS: Usually pre-installed, or install via Homebrew:
-
K6 (Load Testing Tool)
- macOS:
brew install k6 - Linux:
- Ubuntu/Debian:
sudo gpg -k
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69 echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list sudo apt-get update sudo apt-get install k6
- CentOS/RHEL/Fedora:
sudo yum install k6orsudo dnf install k6
- Ubuntu/Debian:
- macOS:
Run the test script to verify all dependencies are installed:
./test-system.shThis will check for Docker, Docker Compose, Make, and K6 and report their status.
Before starting the application, you need to pull the required Docker images.
make pull-dockerThis ensures all required Docker images are available locally before starting the services.
- Build and start all services:
make up- Set up the database and seed data:
make setupThen open the app in your browser:
make open # opens http://localhost:8088 (works on macOS, Linux, WSL, Windows)
make open http://localhost:8081/en/products/db # or open any URL directly
make urls # or print every dashboard URL to Ctrl+clickThe quickest way to see what this project is about. We use Ember, a terminal dashboard for FrankenPHP, and watch it react to a wave of traffic.
# one-time setup: install Ember, start the stack, create + seed the database
make ember-install
make up && make up-worker && make setup
make setup(re)seeds the database (10k products + projections) - it's a one-time step. On later runs just start the services:make up && make up-franken.
# terminal 1 - the live dashboard
make ember# terminal 2 - send a spiky wave of traffic
make ember-loadWatch RPS and busy threads climb and fall as FrankenPHP handles the wave - no flags needed, both commands default to the stable classic server.
Want the side-by-side FPM vs classic vs worker race? Use make compare + make compare-load.
Full beginner walkthrough (with screenshots) in ember.md.
For detailed end-to-end guides on PHP-FPM and OPcache configuration, monitoring, and optimization:
See fpm.md for complete PHP-FPM documentation including:
- Booting and running PHP-FPM
- FPM settings and configuration
- FPM math and right-sizing calculations
- FPM calculator tool
- K6 load testing
- FPM status page and monitoring
- FPM exporter metrics
- Grafana dashboard integration
See php.ini.md for complete OPcache documentation including:
- Booting and accessing the OPcache dashboard
- PHP OPcache .ini settings and configuration
- Metrics endpoint and export
- Prometheus integration for OPcache
- Grafana dashboard for OPcache monitoring
For optimal performance, this project uses Composer autoload optimizations configured in composer.json:
{
"config": {
"optimize-autoloader": true,
"classmap-authoritative": true
}
}Performance Impact:
optimize-autoloader: ~10-15% faster autoloading (converts PSR-0/PSR-4 to classmap)apcu-autoloader: ~50-70% faster (requires APCu extension)classmap-authoritative: Set tofalsefor development,truefor production only
Reference: See the official Symfony Performance Documentation for detailed autoloader optimization guidelines and best practices.
This project uses FrankenPHP (a modern PHP runtime built on Caddy) with two different configurations for performance comparison and monitoring.
See frankenphp.md for complete FrankenPHP documentation including:
- Service configuration and differences
- Auto-reload (file watching) setup
- Caddy configuration and environment variables
- Performance testing and monitoring
- Troubleshooting guide
- Resource optimization guidelines
A detailed PHP-FPM and OPcache monitoring dashboard is available in Grafana. It includes:
- PHP-FPM health, queue, and process metrics
- Request rate, duration, and memory usage
- OPcache hit ratio, memory, and script cache stats
- JIT and interned strings monitoring
- Alerts and color-coded panels for quick health checks
See grafana-dashboard.md for a full description of all panels and dashboard features.
For detailed PHP configuration documentation and settings explanation, see php.ini.md.
This covers all settings in ./docker/symfony.prod.ini including:
- OPcache configuration
- Memory management
- Security settings
- Session management
- Performance optimization