The fastest way I know to get a database OOM-killed is to enable innodb_dedicated_server inside a container and trust it. We ran mysqld in a container limited to 12GB on a 256GB build host, turned the flag on because the docs said dedicated server, and the instance promptly sized its buffer pool to roughly three quarters of the host's RAM. Under load the container hit its cgroup limit and the kernel shot mysqld in the head, mid-write, twice in one week. The flag did exactly what it promises. The problem was that its premise — this machine belongs to MySQL — was false.
Auto-sizing is genuinely good when the premise holds. Here is the formula it applies, the cases where I enable it without a second thought, the cases where I never do, how to verify what it picked at startup, and the manual checklist for when you opt out.
What formula does innodb_dedicated_server actually apply?
Three knobs, derived from detected hardware, applied only to variables you did not set yourself. The buffer pool is sized off detected RAM: below 1GB it stays at the 128MB default, from 1GB to 4GB it gets half of detected memory, and above 4GB it gets three quarters. Redo is sized off detected memory since 8.0.30: innodb_redo_log_capacity starts at 100MB on small boxes and scales through memory-tiered fractions of detected RAM, capping at 128GB past roughly 170GB. Before 8.0.30 it sized innodb_log_file_size and innodb_log_files_in_group from memory tiers instead, from 48MB up to 2GB per file. And the flush method is set to O_DIRECT_NO_FSYNC where the filesystem supports it.
The only-if-you-did-not-set-it clause is the part people miss. An explicit innodb_buffer_pool_size in my.cnf beats the auto value, silently. So a config that enables the flag while carrying a stale buffer_pool_size=512M from a smaller machine gets a 512MB pool on a 128GB box, and nothing in the error log will argue with you. Every audit of a surprising buffer pool I have done started by diffing my.cnf against SHOW VARIABLES, and Every audit of a surprising buffer pool I have done started by diffing my.cnf against SHOW VARIABLES, and this clause was the answer about half the time.
Just as useful is the list of what the feature refuses to touch. It does not size innodb_io_capacity or innodb_io_capacity_max, so your IOPS budget still needs a manual decision on anything faster than a hard disk. It does not touch per-connection buffers, tmp_table_size, or max_connections. It stays away from the binary log, the doublewrite buffer, and performance_schema. And it never reduces a value you set by hand — auto-sizing fills gaps, it does not govern. Treat it as a sensible floor for the two biggest InnoDB memory structures, not as a complete tuning strategy.
When is the autosizing right?
When the box truly belongs to mysqld. A single-tenant VM or bare-metal database host, where three quarters of RAM for the buffer pool is exactly what you would have picked by hand, is the designed-for case, and the feature nails it. It is also a gift for dev and CI instances, where the alternative is the absurd 128MB default on a 32GB laptop and nobody notices until a test suite crawls. Managed services have voted the same way: RDS for MySQL 8.4 enables it by default on dedicated instance classes, which tells you how trustworthy the formula is when one instance owns one machine. If your fleet is a set of single-purpose database VMs, turn it on and spend your tuning hours somewhere better.
When does the autosizing go wrong?
Whenever the premise breaks, and it breaks in three common ways. Shared hosts: if app servers live on the same box, handing three quarters of RAM to InnoDB starves everything else, and the remaining quarter is not reserved for MySQL's other needs — connections, temp tables, sort buffers — it is simply what is left for the OS. Multi-instance boxes: two mysqld processes each sizing themselves to 75 percent of the same RAM is a knife fight the kernel settles. And containers: the detection reads what the host reports, and older 8.0 builds did not consistently honor cgroup memory limits, which is precisely the OOM story from the top of this note. Current releases handle container limits far better, but I still verify the detected RAM at the first startup on any new platform before trusting the flag there.
How do you verify the effective values at startup?
The error log is the source of truth: at startup InnoDB reports how much memory it detected and what it sized, and reading those lines once per environment has saved me from two bad deploys. After boot, confirm the live values directly:
SELECT variable_name, variable_value
FROM performance_schema.global_variables
WHERE variable_name IN (
'innodb_dedicated_server',
'innodb_buffer_pool_size',
'innodb_redo_log_capacity',
'innodb_flush_method'
);
Then do the division yourself. If detected RAM is 64GB, you should see a 48GB pool. If the number is wildly different, either someone set the variable explicitly or detection saw different hardware than you think. On containers, compare against the cgroup limit — memory.max on v2, memory.limit_in_bytes on v1 — not against the hosting VM's spec sheet, because the spec sheet is what fooled us.
What is the manual checklist when you opt out?
Disable the flag and size five things deliberately. Buffer pool first: 60 to 75 percent of RAM on a dedicated box, scaled to the working set rather than the spec sheet — the InnoDB buffer pool field guide has the working-set method I use. Redo second: innodb_redo_log_capacity large enough to absorb your write burst without forcing checkpoints, which the redo capacity and checkpoint stalls note walks through with real graphs. Third, per-connection memory: sort, join, read, and read-rnd buffers multiplied by max_connections, plus tmp_table_size and max_heap_table_size for in-memory temp tables. Fourth, leave the OS real page cache for the binlog, relay logs, and the redo log — InnoDB bypasses that cache with O_DIRECT for its data files, doublewrite included, while the server-layer logs stay buffered. Fifth, monitor the total, because the sum of the parts is what kills you; memory usage diagnosis is the instrument map.
On a 64GB box shared with app servers, my starting point is a 24GB pool, 8GB of redo capacity, conservative per-thread buffers, and a hard ceiling on max_connections — then I let a week of production traffic argue for adjustments. That is less elegant than one flag, and it has never OOM-killed anything.
Where MonPG stands on MySQL
I build MonPG, so to be plain: MonPG monitors PostgreSQL today, and MySQL support is in active development, not shipped. Sizing drift is one of the quiet failure modes this kind of monitoring exists for — a restart silently changing the effective buffer pool, redo capacity lagging a growing write rate, memory climbing toward a cgroup ceiling — and the MySQL work is being built to graph exactly those signals. The MySQL monitoring (coming soon) page is where that lands as it ships. Until then, the same approach runs on the PostgreSQL side, and the rest of these MySQL notes are on the blog.