Nginx uses just 33 MB of RAM at idle across four worker processes. Apache’s Prefork default consumes 640 MB before a single request arrives. That gap — documented in controlled benchmark testing — summarizes why server resource planning cannot rely on assumptions. This article compiles verified 2024–2026 benchmark data for Nginx, Apache, MySQL, PostgreSQL, and PHP-FPM, covering idle memory, peak throughput, per-connection costs, and query latency.
Average Resource Usage of Common Linux Services: Key Statistics
- Nginx handles 1,000 concurrent connections at 0.14 MB per connection versus Apache Event MPM’s 1.85 MB, according to CubePath benchmark analysis.
- PostgreSQL completes a SELECT query across one million unindexed records in 0.6–0.8 ms; MySQL requires 9–12 ms for the same operation, per MDPI Future Internet research published October 2024.
- PostgreSQL recorded 21,338 single-row INSERT operations per second against MySQL’s 4,383 in January 2026 testing on Ubuntu 24.04 with an AMD Ryzen 7 PRO processor.
- PHP-FPM execution time dropped from 150 ms to 23 ms — an 85% reduction — after enabling OPcache and correctly sizing pm.max_children, with no hardware changes involved.
- PostgreSQL held a 51.9% professional adoption rate in the Stack Overflow Developer Survey 2024, compared to MySQL’s 39.4% across 65,437 surveyed developers.
Nginx vs Apache: Memory and Throughput Under Linux
The clearest dividing line between Nginx and Apache is how they handle connections. Nginx uses an event-driven, non-blocking model. Apache Prefork spawns a separate process per connection. At idle, Nginx’s four worker processes consume about 33 MB total; Apache Prefork’s 25 default workers use roughly 640 MB before any traffic arrives.
Apache’s Event MPM changes this considerably — idle memory drops to around 192 MB — but the per-connection cost gap persists once traffic scales. At 1,000 concurrent connections, Apache Event MPM requires approximately 1.85 MB per connection versus Nginx’s 0.14 MB. On a server receiving 10,000 simultaneous connections, that arithmetic produces 18.5 GB of RAM needed for Apache Event MPM against 1.4 GB for Nginx.
You can restart Nginx on Linux after configuration changes without dropping active connections using systemctl reload nginx, which validates the config before applying it.
| Metric | Apache 2.4 Prefork | Apache 2.4 Event MPM | Nginx 1.24 |
|---|---|---|---|
| Idle memory total | ~640 MB | ~192 MB | ~33 MB |
| Memory at 1,000 concurrent connections | — | ~1,850 MB | ~142 MB |
| Per-connection memory cost | — | ~1.85 MB | ~0.14 MB |
| Requests/second at 1,000 concurrency | — | 8,420 req/s | 12,850 req/s |
| CPU at 1,000 concurrency | — | 42% | 28% |
| Memory at extreme concurrency | — | ~4,200 MB + 328 timeouts | ~285 MB, 0 failures |
| SSL/TLS CPU overhead | — | ~65% | ~48% |
| Peak req/s (16-core EPYC, static) | — | ~70,000/s | ~120,000/s |
| p95 latency under static load | — | ~30 ms | ~12 ms |
Source: CubePath Apache vs Nginx Performance benchmark analysis; Perlod Nginx vs Apache performance comparison, January 2026
SSL/TLS Efficiency Under Linux
With roughly 87% of web traffic encrypted as of 2024, SSL/TLS CPU cost matters at scale. Nginx consumed approximately 48% CPU under encrypted load at 1,000 concurrent connections; Apache Event MPM consumed about 65% under the same conditions. That 17-percentage-point difference translates directly to available headroom for application processing.
At extreme concurrency in the CubePath test, Apache Event MPM produced 328 connection timeouts and required around 4,200 MB. Nginx handled the same load at 285 MB with zero failures.
MySQL vs PostgreSQL Resource Usage on Linux
Query performance differences between MySQL and PostgreSQL are larger than most operators expect. A peer-reviewed study in MDPI Future Internet (October 2024) tested both databases under default configuration on workloads modeled on high-frequency behavioral biometric reads. PostgreSQL completed SELECT queries against one million unindexed records in 0.6–0.8 ms. MySQL required 9–12 ms — roughly 13 times slower on that specific query type.
Filtered lookups showed a similar gap. PostgreSQL returned WHERE clause results on primary key lookups in 0.09–0.13 ms; MySQL took 0.9–1.0 ms, about nine times longer.
For context on how RAM usage behaves on Linux servers, the free -h command gives a clear breakdown of physical memory, used memory, and available memory at a glance.
| Metric | PostgreSQL | MySQL | Conditions |
|---|---|---|---|
| SELECT on 1M records (no index) | 0.6–0.8 ms | 9–12 ms (~13x slower) | Default config |
| SELECT with WHERE clause | 0.09–0.13 ms | 0.9–1.0 ms (~9x slower) | Primary key lookup |
| Single-row INSERT throughput | 21,338 ops/sec | 4,383 ops/sec | Ubuntu 24.04, AMD Ryzen 7 PRO, 32 GB RAM |
| index_join latency | 1.96 ms | 1.37 ms (faster) | Sysbench, AWS EC2 m5a.xlarge |
| Idle RAM per connection/thread | 2–3 MB per connection | ~256 KB per thread | Default connection settings |
| Professional adoption (2024) | 51.9% | 39.4% | Stack Overflow Developer Survey 2024 |
Source: Salunke & Ouda, MDPI Future Internet vol. 16(10), DOI 10.3390/fi16100382, October 2024; BinaryIgor via commandlinux.com, January 2026; DoltHub nightly Sysbench data July 2024; Stack Overflow Developer Survey 2024
Per-Connection Memory: Why MySQL Has an Advantage at Scale
PostgreSQL allocates 2–3 MB per idle connection before any query runs. At 1,000 concurrent idle connections, that amounts to 2–3 GB of shared memory consumed purely by connection state. MySQL’s per-thread cost of approximately 256 KB is roughly ten times smaller at the connection layer.
This explains why MySQL historically performed better in high-connection-count deployments even when PostgreSQL outperformed it on individual query speed. Connection pool sizing — typically using PgBouncer for PostgreSQL — directly addresses this memory pressure and is standard practice on high-traffic servers. When managing active processes consuming unexpected memory, checking Linux processes with ps aux or top can identify which database connections are still alive.
PHP-FPM Worker Memory and Configuration Statistics
PHP-FPM’s total memory footprint equals the number of active worker processes multiplied by per-worker RSS consumption. The default PHP memory_limit is 128 MB per process, though real-world RSS usage ranges from 20 MB for simple applications to 128 MB for heavier ones. A production WordPress site typically lands in the 50–128 MB range per child process.
On a 16 GB server, after reserving roughly 20–30% of RAM for the OS, Nginx, and database, the available headroom supports 72–96 concurrent PHP-FPM workers at 128 MB each. The practical ceiling is lower when PostgreSQL’s shared_buffers are set at the recommended 25% of total system RAM.
| Metric | Figure | Source |
|---|---|---|
| Default PHP memory_limit per process | 128 MB | PHP documentation |
| Typical production RSS per worker | 20–128 MB | Geoligard PHP-FPM analysis, March 2024 |
| WordPress PHP-FPM worker memory | ~50–128 MB per child | Multiple hosting benchmarks |
| Max workers on 16 GB server (128 MB each) | ~72–96 children | Inspector.dev PHP-FPM guide |
| PHP execution time before OPcache (Laravel) | ~150 ms average | Mateusguimaraes.com case study |
| PHP execution time after OPcache + FPM tuning | ~23 ms average | Mateusguimaraes.com case study |
| CPU usage before tuning | ~15–30% average | Mateusguimaraes.com case study |
| CPU usage after tuning | ~2% average | Mateusguimaraes.com case study |
| pm.max_requests recommended range | 500–1,000 | Oneuptime tuning guide, March 2026 |
Source: PHP documentation; Geoligard.com, March 2024; Oneuptime.com, March 2026; Inspector.dev; Mateusguimaraes.com
OPcache: The Single Highest-Impact PHP Tuning Change
The Mateusguimaraes.com production case study compared a misconfigured Laravel application — OPcache disabled, FPM undersized — against the same codebase after enabling OPcache and setting pm.max_children correctly. Average execution time fell from 150 ms to 23 ms. CPU load dropped from 15–30% to approximately 2%. No hardware was changed.
This 85% execution time reduction from configuration alone makes OPcache the highest-return optimization in the default PHP-FPM stack. The pm.max_requests setting, recommended at 500–1,000, recycles workers periodically to prevent memory leaks from accumulating over long-lived processes.
For servers where PHP library dependencies cause errors after package updates, the shared libraries error on Linux typically points to a missing or mismatched .so file that ldconfig or package reinstallation resolves.
Full Linux Service Stack Idle Memory Comparison
A standard LEMP stack on a 16 GB server at idle occupies roughly 4.5–5 GB: Nginx at about 33 MB, MySQL with a production-tuned 2–4 GB InnoDB buffer pool, PHP-FPM at a few hundred MB with sparse workers active, and the OS baseline at 200–500 MB depending on the distribution.
That leaves approximately 10–11 GB for MySQL’s active query cache expansion and PHP-FPM worker scaling under traffic. The database consistently holds the largest share — not from misconfiguration, but because deliberately large buffer pools eliminate disk I/O for frequently accessed rows.
Server memory allocation strategies also connect to Linux kernel architecture. The Linux kernel reached 40 million lines of code in January 2025, with memory subsystem changes appearing in nearly every release cycle. Distribution choice also affects baseline OS memory consumption — a minimal Ubuntu server installation sits at the lower end of the 200–500 MB OS baseline range, while feature-heavier distributions push toward the upper end. The Ubuntu vs Debian vs Fedora usage comparison covers how adoption splits across server environments.
| Service | Approx. Idle RAM | CPU at Idle | Notes |
|---|---|---|---|
| Nginx (4 workers) | ~33 MB | <1% | Scales efficiently with connections |
| Apache 2.4 Event MPM | ~192 MB | <1% | Reduced from Prefork’s ~640 MB default |
| Apache 2.4 Prefork (default) | ~640 MB | <1% | Each worker is a separate process |
| PostgreSQL (shared_buffers at 25% of 16 GB) | 4 GB + 2–3 MB per connection | <1% | Aggressive caching by design |
| MySQL InnoDB (default config) | 128 MB buffer pool + ~256 KB per thread | <1% | Buffer pool tuned up in production |
| PHP-FPM worker (typical) | 20–128 MB per worker process | 0% (idle) | RSS varies by application |
| Linux OS baseline (minimal server) | ~200–500 MB | ~0.5–1% | Varies by distro and enabled services |
Source: CubePath benchmark analysis; PostgreSQL Wiki; Google Cloud documentation; MySQL documentation; Percona; PHP documentation; Geoligard.com, March 2024
On memory-constrained servers, zram for Linux compresses in-memory block devices to extend effective RAM capacity without adding physical hardware. It works at the kernel level and is especially relevant when PHP-FPM worker counts push against available physical memory.
FAQs
How much RAM does Nginx use on a Linux server?
Nginx uses approximately 33 MB total at idle with four worker processes. At 1,000 concurrent connections, total memory rises to roughly 142 MB at about 0.14 MB per connection, according to CubePath benchmark analysis.
How much memory does PostgreSQL use per connection?
PostgreSQL allocates 2–3 MB per idle connection in shared memory under default settings, per Google Cloud documentation. At 1,000 idle connections, that amounts to 2–3 GB before any queries run.
Is PostgreSQL faster than MySQL on Linux?
PostgreSQL completed SELECT queries on one million unindexed records 13 times faster than MySQL in MDPI research published October 2024. MySQL was faster on index_join queries in DoltHub Sysbench testing (1.37 ms vs 1.96 ms).
How many PHP-FPM workers can a 16 GB Linux server run?
With the default 128 MB memory limit per worker and 20–30% of RAM reserved for the OS and other services, a 16 GB server supports approximately 72–96 concurrent PHP-FPM workers, according to Inspector.dev’s PHP-FPM guide.
Does enabling OPcache significantly reduce PHP-FPM resource usage?
Yes. A documented production case study recorded average PHP execution time dropping from 150 ms to 23 ms and CPU usage falling from 15–30% to approximately 2% after enabling OPcache and resizing pm.max_children — no hardware changes were made.