After our bare metal application server benchmarks and the PHP 8.5 JIT deep tuning, many readers asked: Does Docker really slow down PHP application servers? Today we benchmark PHP-FPM, RoadRunner, and Swoole in three scenarios: bare metal, Docker default bridge network, and Docker host network + CPU pinning. The results are surprising—with the right flags, Docker can actually outperform bare metal.
🧪 Test Environment
- Server: 4 vCPU, 8 GB RAM, Ubuntu 22.04 (both bare metal and Docker host)
- PHP: 8.5.7 (OPcache + JIT tracing, buffer 128M)
- Database: MySQL 8.4 on same host (
SELECT 1query) - Workers: All environments configured with 20 parallel workers
- Business logic: 1000 sqrt iterations + one
SELECT 1query (CPU‑intensive) - Load generator:
ab -c 100 -t 60(ApacheBench, 100 concurrent connections, 60 seconds), 3+ runs per configuration
Bare‑metal data reused from our application server benchmark (same business logic, JIT tracing, 20 workers). Docker images built from official php:8.5-fpm, php:8.5-cli, and Swoole compiled from source.
📊 Performance Results
All values are average requests per second (RPS) over three runs; P99 latency in milliseconds.
| Environment | Deployment | Network | CPU Pinning | Avg RPS | P99 (ms) | vs Bare Metal |
|---|---|---|---|---|---|---|
| PHP‑FPM | Bare metal | — | — | 2979 | 55 | 100% |
| PHP‑FPM | Docker | bridge | No | 1944 | 62 | 65% |
| PHP‑FPM | Docker | host | 0,1 | 5543 | 25 | 186% |
| RoadRunner | Bare metal | — | — | 2591 | 73 | 100% |
| RoadRunner | Docker | bridge | No | 1230 | 142 | 47% |
| RoadRunner | Docker | host | 0,1 | 2838 | 70 | 109% |
| Swoole | Bare metal | — | — | 4981 | 62 | 100% |
| Swoole | Docker | bridge | No | 2012 | 230 | 40% |
| Swoole | Docker | host | 0,1 | 9700 | 37 | 195% |

Key insight: Default Docker bridge network costs 35–60% performance across all PHP application servers. Switching to
--network hostand binding CPU cores (--cpuset-cpus) not only recovers that loss – for Swoole and PHP‑FPM, it delivers 1.95× and 1.86× the bare‑metal RPS.

🔍 Deep Analysis: Why Such Dramatic Differences?
Why Default Bridge Network Is So Slow
- Network virtualization overhead: Under bridge mode, every request traverses veth pair → docker0 bridge → iptables NAT rules before reaching the container. Each layer adds latency and consumes CPU. Bridge mode can introduce 8–12ms extra latency compared to host mode.
- CPU scheduler overhead: Docker containers run as regular processes and can migrate across CPU cores, causing cache misses and TLB flushes that cost 10–30% performance.
- Memory & file system isolation: OverlayFS and separate page caches increase memory access costs. Each request’s file stat operations (even with OPcache) add extra overhead.
Why Host Network + CPU Pinning Works So Well
--network host completely removes all network virtualization layers. The container talks directly to the host’s network stack with zero iptables overhead.
--cpuset-cpus="0,1" pins container processes to dedicated CPU cores, preventing migration and improving L1/L2 cache locality. For event‑driven servers like Swoole (and even PHP‑FPM with 20 workers), this combination eliminates virtually all container overhead, sometimes exposing optimizations that were hidden on bare metal.
Why does Swoole benefit the most? Swoole’s coroutine scheduler is extremely sensitive to CPU migration. Pin it to fixed cores and remove network virtualization, and its performance skyrockets from 2012 to 9700 RPS – a 382% gain.
🚀 Production Recommendations
- For Swoole / RoadRunner / FrankenPHP: Always run with
--network hostand CPU pinning. In Docker Compose, add:
network_mode: host
cpuset: "0,1"- For PHP‑FPM behind Nginx: Host network + CPU pinning provides massive gains (from 1944 to 5543 RPS). If you must use bridge mode, at least pin CPUs.
- Additional Docker performance tips:
- Use
--cpuset-cpusto reserve dedicated cores for each container. - Avoid
--privilegedunless absolutely necessary. - Use
--mount type=bindfor high‑I/O workloads; avoid volume drivers unless required. - Set
--restart=unless-stoppedfor production containers. - Prefer Alpine‑based PHP images (e.g.,
php:8.5-fpm-alpine) for smaller attack surface and faster cold start, but test thoroughly – Alpine uses musl libc, which can cause compatibility issues with some PHP extensions.
- Enable OPcache JIT in containers: Don’t forget to set
opcache.jit=tracingandopcache.jit_buffer_size=128Min your Dockerfile – JIT is not automatically enabled in containers.
📁 Reproducibility – Full Source Code Available
All test scripts, Dockerfiles, docker-compose files, and raw ab output logs are available on GitHub:
- Repository: github.com/phpbenchlab/docker-php-bench
- Contents:
- Dockerfiles for PHP‑FPM, RoadRunner, and Swoole with JIT pre‑configured
- Docker Compose files for all three environments (bridge mode)
- Raw benchmark result files (
fpm_docker_*.txt,rr_docker_*.txt,swoole_docker_*.txt) - Test scripts for bare metal, bridge, and host+CPU pinning scenarios
To replicate our results on your own server:
git clone https://github.com/phpbenchlab/docker-php-bench.git
cd docker-php-bench
# Start MySQL and build images
docker-compose -f docker-compose.mysql.yml up -d
docker-compose -f docker-compose.fpm.yml build
docker-compose -f docker-compose.fpm.yml up -d
# Run benchmark (bridge mode)
ab -c 100 -t 60 http://localhost:8080/index.php
# For host mode + CPU pinning
docker run -d --name bench-swoole --network host --cpuset-cpus="0,1" bench-swoole🧵 Final Words
- Docker does not have to be slow – with the right parameters, it can match or beat bare metal.
- Always benchmark your own workload, but our data shows that
--network hostand CPU pinning are the two most impactful Docker flags for PHP application servers. - Swoole is extremely sensitive to CPU scheduling – pinning cores stabilizes its performance and unlocks huge gains.
- RoadRunner sees modest but real gains – from 1230 to 2838 RPS, crossing the bare‑metal threshold (2591).
This PHP application server Docker benchmark shows that environment configuration matters as much as code optimization. Whether you’re deploying Swoole, RoadRunner, or PHP-FPM, remember: Docker’s defaults are safe, but not fast. A few runtime flags can deliver near‑metal performance.
Have you tried these Docker optimizations in production? Share your experience in the comments!
Published on June 13, 2026 – PHP application server Docker performance benchmark.