Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

MySQL stops being killed only when its peak memory fits inside the limit that actually applies to it. First determine whether the kill came from host-wide Linux OOM, a container or cgroup limit, or systemd-oomd. Then budget the InnoDB buffer pool, global allocations, per-connection buffers, temporary tables and other services together. Lower max_connections and oversized per-thread buffers when necessary; changing OOM scores or adding swap can reduce risk but cannot compensate for an oversized budget.

Identify which out-of-memory mechanism killed mysqld

“Out of memory” is not one condition. The kernel can exhaust RAM on the whole host, a cgroup can hit its own memory.max, or systemd-oomd can terminate a service after sustained memory pressure. The fix depends on the scope. Host RAM does not override a container limit, and protecting mysqld from one killer does not prevent another policy from acting.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Kernel OOM on the host

The kernel OOM killer runs when an allocation cannot be satisfied in the memory context that triggered it. That context may be the entire machine or a constrained memory controller. Kernel messages normally name the killed process, its PID, and memory statistics. A kill in this category often affects several services competing for the same RAM.

A cgroup or container limit

Docker, Kubernetes, LXC and systemd scopes can impose a hard memory ceiling below physical RAM. When the group reaches that ceiling, the kernel can kill a process inside the group while the host still shows free memory. Inspect the group’s effective limit and current usage rather than relying on free output from the host.

systemd-oomd before a kernel OOM

systemd-oomd is a userspace service. It monitors cgroups-v2 and pressure stall information (PSI), then can kill eligible units when configured swap or memory-pressure policies are met. A service terminated by oomd may have no classic “Out of memory: Kill process” line from the kernel. Check the oomd journal and unit properties before changing MySQL settings.

Confirm the event and capture evidence

Collect evidence immediately after a kill, before restarting or clearing logs. Record the time, killed PID, unit or container name, peak connections, RAM, swap and the applicable cgroup path.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Search kernel messages: journalctl -k -b. On systems that provide it, also run dmesg -T. Look for “Out of memory,” “Killed process,” “oom-kill,” or a memory-cgroup failure.
  2. Read the MySQL error log around the same timestamp. A sudden disappearance, followed by normal startup messages on the next launch, is consistent with an external kill; an InnoDB error or assertion indicates a different failure.
  3. Inspect service status and recent unit logs with systemctl status mysql (the unit may be named mysqld) and journalctl -u mysql --since "-2 hours".
  4. Check current capacity with free -h, swapon --show, and cat /proc/meminfo. Capture both available memory and swap usage; “free” RAM alone is not a peak measurement.
  5. For a systemd service, inspect limits and drop-ins: systemctl show mysql -p MemoryMax -p MemoryHigh -p ManagedOOMSwap -p ManagedOOMMemoryPressure -p ControlGroup and systemctl cat mysql.
  6. Check whether oomd is active: systemctl status systemd-oomd, then search journalctl -u systemd-oomd for the unit or cgroup.
  7. In a container, read the container’s cgroup files (commonly /sys/fs/cgroup/memory.max, memory.current, memory.events, or their cgroup-v1 equivalents). A value of max means no explicit limit in that file; it does not guarantee unlimited host memory.

The key result is a named scope: host, cgroup/container, or systemd-oomd policy. Do not tune a buffer pool until that scope is known.

Build a realistic MySQL memory budget

innodb_buffer_pool_size is the largest familiar allocation, but it is not MySQL’s total memory. Global caches, per-thread work areas, thread stacks, temporary tables, Performance Schema, replication, client libraries and the operating-system page cache all compete for the same limit.

Component How it grows What to inspect
InnoDB buffer pool Global; normally the largest fixed allocation innodb_buffer_pool_size
Connection and thread memory Can multiply by active sessions; some allocations occur per connection or per operation max_connections, thread cache and stack settings
Sort, join and read buffers Per-thread or per-operation; large values become expensive under concurrency sort_buffer_size, join_buffer_size, read_buffer_size, read_rnd_buffer_size
Temporary tables Each concurrent workload can create in-memory temporary tables, then spill or fail when limits are reached tmp_table_size, max_heap_table_size
Binary-log and other caches Per-session or global, depending on variable Binary-log cache settings and global cache variables
Instrumentation and replication Additional global or workload-dependent memory Performance Schema, relay logs, replication configuration
Non-MySQL processes Competes inside the same host or cgroup Web servers, agents, backups, JVMs and filesystem cache

MySQL’s documented starting range for the InnoDB buffer pool is typically 50%–75% of system memory. That is a starting range for a dedicated host, not a promise that 75% is safe in a container or on a busy shared server. Subtract the memory required by the OS, every co-resident service and a safety reserve first.

For initial planning, MySQL gives this illustrative lower-bound estimate:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

innodb_buffer_pool_size + key_buffer_size + max_connections*(sort_buffer_size + read_buffer_size + binlog_cache_size) + max_connections*2MB

The expression is not a peak guarantee. Add room for join and random-read buffers, temporary-table growth, thread stacks, Performance Schema, replication, allocator overhead, filesystem cache and short bursts. Oracle-provided MySQL binaries commonly use a 256 KB thread stack; a 2 MB stack is also common, so verify the actual build and setting rather than assuming one number.

Measure peaks, not idle RSS

Idle mysqld RSS can look safe while a reporting job opens many sessions and allocates sort or temporary buffers simultaneously. Track process RSS and cgroup memory.current during the busiest period. Compare the highest observed connection count with max_connections; sizing for a theoretical maximum that applications never reach wastes memory, while sizing for today’s idle count misses bursts.

Step-by-step: make the budget fit

1. Reserve memory for the scope outside MySQL

Start with the effective limit, not the machine’s installed RAM. Reserve explicit headroom for the kernel, filesystem cache, monitoring, backups and every other service. On a 16 GiB container capped at 8 GiB, the budget starts at 8 GiB, not 16 GiB. Keep a reserve for bursts and reclaim latency; a configuration that equals the limit will eventually hit it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

2. Set a conservative buffer pool

Lower innodb_buffer_pool_size until the remaining budget covers measured non-buffer usage and peak concurrency. MySQL supports dynamic buffer-pool resizing in current versions, so a change can often be made without a restart:

SET GLOBAL innodb_buffer_pool_size = 4294967296;

The value is in bytes; use a value appropriate to the measured limit. Confirm the change with:

Rank #3
Sale
MySQL Cookbook
  • Used Book in Good Condition
SHOW GLOBAL VARIABLES LIKE 'innodb_buffer_pool_size';

Resizing can temporarily consume resources and may affect cache hit rate. If the desired value cannot be changed dynamically on your version or packaging, schedule the required restart and verify the persistent configuration afterward.

3. Cap concurrency at a measured requirement

max_connections multiplies memory that is cheap at one session and dangerous at hundreds. Find the high-water mark:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Max_used_connections';
SHOW GLOBAL VARIABLES LIKE 'max_connections';

Set a limit above the measured legitimate peak, with a small operational margin, rather than an arbitrary four-digit value. Apply it persistently in the server configuration and use a connection pool to queue excess application work. A lower limit can expose application errors or queueing, so test clients for proper retry and back-pressure behavior.

4. Reduce oversized per-thread buffers

Inspect and lower unusually large values for sort_buffer_size, join_buffer_size, read_buffer_size, read_rnd_buffer_size, binary-log cache variables and thread-stack settings. These buffers are not reserved once for the server; concurrent operations can allocate them repeatedly. Change one class of variable at a time, then observe query latency, disk-based temporary tables and peak RSS. A smaller buffer may increase sorting, joining or temporary-table spills, trading memory for I/O.

5. Control in-memory temporary tables

tmp_table_size and max_heap_table_size limit internal in-memory temporary tables. Large limits can multiply across concurrent queries. Set them to a value justified by workload measurements, and monitor whether lowering them causes excessive disk temporary tables or unacceptable latency. Do not assume that raising these limits is a free performance improvement.

6. Recalculate and observe

Re-run the planning formula with the new values, then observe peak cgroup usage, mysqld RSS, connection count, swap activity and query latency over a representative busy period. Keep a reserve below the hard limit. If memory still climbs, identify which category grew instead of repeatedly shrinking the buffer pool.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Container and systemd limits: fixes outside MySQL

When a cgroup limit is too low

If the workload genuinely needs more memory, raise the container or service limit and account for the infrastructure cost. Otherwise, reduce MySQL’s budget to fit the existing ceiling. Raising a host’s RAM without raising memory.max changes nothing for a capped container. Check both the parent and descendant cgroups; a nested limit can remain lower than the value you changed.

When systemd applies a hard or soft limit

MemoryMax is a hard ceiling; MemoryHigh applies reclaim and throttling pressure before that ceiling. Drop-ins can override the vendor unit, so inspect all effective properties with systemctl show and the files shown by systemctl cat. After editing a drop-in, run:

systemctl daemon-reload
systemctl restart mysql

Restarting the service is required for the unit-limit change to take effect, and it will interrupt connections. Coordinate it with application failover or maintenance procedures.

When systemd-oomd is selecting the unit

Check ManagedOOMSwap= and ManagedOOMMemoryPressure= on the unit and relevant slice. Review oomd journal entries for the exact cgroup and threshold. You can change policy or eligibility, but disabling protection without fixing the memory budget simply moves the eventual failure to the kernel or another service. Validate any policy change during a controlled pressure test.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Swap: useful cushion, not a cure

Some swap gives the kernel room to move cold pages and can delay an immediate kill. Verify that swap exists with swapon --show, then monitor paging latency and application response time. Heavy swapping can make MySQL unusably slow, and systemd-oomd policies may deliberately act when swap is exhausted or pressure remains high. Adding swap does not reduce MySQL’s allocation; it only changes how pressure is handled. Never count swap as a substitute for a RAM budget that fits the workload.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

OOM-score tuning and victim selection

Linux uses oom_score_adj to adjust a process’s badness score. A lower score can make mysqld less likely to be selected, but it does not free memory or lower MySQL’s demand. The kernel may then kill a web server, backup process or monitoring agent that is more important to recovery. Treat score changes as a last-resort failure-priority decision after capacity and cgroup settings are correct.

Inspect the current value with cat /proc/$(pidof mysqld)/oom_score_adj. If you deliberately change it, make the setting persistent through the service manager, document which process is allowed to die instead, and test that the value survives a restart. Do not use it to conceal a recurring budget error.

Compare remediation choices before applying them

Option Peak-memory effect Performance or operational trade-off Restart? Reversibility and blast radius
Reduce buffer pool Direct, predictable global reduction Potentially more disk reads and lower cache hit rate Often no; dynamic resizing is documented Usually reversible; mainly affects MySQL
Lower max_connections Caps connection-driven growth Applications may queue or receive “too many connections” Depends on how the persistent setting is applied Reversible; requires client testing
Reduce per-thread buffers Reduces concurrent-work spikes More temporary files, I/O or query latency Variable-specific; verify documentation Reversible; query classes may differ
Raise cgroup or unit limit Increases available headroom Uses more host or cloud capacity and may endanger neighbors Usually requires unit/container update and restart Reversible, but affects infrastructure and other tenants
Add swap Delays hard failure during bursts Swap latency; may trigger oomd policy Usually no service restart Reversible; affects the whole scope
Lower oom_score_adj None Moves the likely victim to another process Service-manager dependent Reversible, but blast radius is unpredictable

Troubleshooting branches

The buffer pool was lowered but mysqld is still killed

  • Check whether the kill is actually from systemd-oomd or a cgroup limit.
  • Compare peak, not idle, connection count with max_connections.
  • Inspect per-thread buffers and temporary-table limits; these can dominate during bursts.
  • Account for Performance Schema, replication, binary logging, thread stacks and non-MySQL processes.
  • Read memory.events for cgroup OOM counters and correlate timestamps with kernel and unit logs.

The host has free RAM, but the container reports OOM

The container’s memory controller is the applicable scope. Read its effective memory.max and current usage, then either raise that limit or fit the MySQL budget inside it. Host-level free output cannot override a lower cgroup ceiling.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Connections fail after lowering max_connections

Confirm the application uses pooling, bounded retries and a queue. Set the limit above the measured legitimate peak, deploy clients that release idle sessions, and watch Threads_connected and connection errors. Increasing the limit again without reducing per-session memory restores the original risk.

Performance collapses after memory reductions

Check disk temporary-table rates, sort and join latency, buffer-pool hit behavior, swap-in activity and storage latency. Restore only the setting that caused the regression, and fund that increase by reducing another memory consumer or raising the cgroup limit. Avoid restoring every previous value at once.

Operational checklist

  • Identify kernel OOM, cgroup OOM or systemd-oomd in logs.
  • Record the effective memory limit, peak usage, swap and killed PID.
  • Measure Max_used_connections during the busiest period.
  • Calculate a budget including global, per-thread, temporary-table and non-MySQL memory.
  • Leave an explicit reserve below MemoryMax or memory.max.
  • Reduce the buffer pool, connection cap or oversized buffers in measured steps.
  • Use a pool to queue excess application concurrency.
  • Verify swap behavior and oomd thresholds after every policy change.
  • Persist settings, document restart requirements and monitor peak RSS and cgroup usage.

Conclusion

A durable fix is a memory budget that fits the real scope and the real peak. Diagnose the killer first, reserve memory outside MySQL, then tune the buffer pool, concurrency and per-thread allocations together. Cgroup limits, swap and OOM scores determine how failure appears, but only reducing demand or providing adequate capacity prevents the next kill.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.