...

Memory Pressure in the Linux Kernel: Impact on Hosting Systems

Memory pressure in the Linux kernel directly affects hosting systems: As pressure increases, CPU time and I/O shift to more resource-intensive operations Reclaim, response times increase, and the risk of out-of-memory (OOM) errors rises. I’ll clearly explain how I identify, measure, and mitigate memory pressure so that Hosting-Respond consistently to workloads.

Key points

I focus on the key factors that determine performance and outages in hosting environments. The following points serve as the guiding principle for my diagnostic and optimization efforts. This overview helps me avoid misinterpreting „full RAM“ and identify genuine Pressure in a timely manner.

  • PSI Metrics They show wait times rather than just occupancy and identify delays early on.
  • Swap load indicates reclaim issues that exacerbate I/O and latency.
  • Cgroups Limits Control throttling, protection, and OOM behavior per service.
  • Cache Eviction directly affects web and database performance.
  • Capacity planning And tuning maintains headroom and prevents thrashing.

This is how I structure my analyses—from the kernel to the application—and implement appropriate measures in order of priority. The focus remains on measurable effects, not piecework.

What does "memory pressure" mean in the Linux kernel?

Memory pressure means that the kernel spends a noticeable amount of time freeing up memory instead of continuing to process user processes; the CPU then spends more time on scans, writes, and evictions while requests wait. I make a clear distinction between „full RAM“ and „lack of usable headroom“: A „full“ cache is healthy; bottlenecks only occur when reclaim costs rise. The kernel scans inactive lists and writes dirty-removes pages, discards the file cache, and evicts anonymous pages as soon as the watermarks are breached. The time spent on these activities is critical; it is reflected in task wait times and extended response times. A host can run smoothly at 95% % utilization as long as the cache can be easily reclaimed, but it can stall severely during periods of low load when active anonymous pages must be evicted.

Understanding and Measuring PSI

Pressure Stall Information (PSI) makes memory pressure tangible because I measure delays rather than memory usage. In /proc/pressure/memory I see „some“ and „full“: „some“ describes periods when at least one task is waiting for memory, while „full“ indicates periods when all tasks are waiting at the same time. Example: „some avg10=4.67“ means that, over the last 10 seconds, 4.67 % of the time was spent in stalls due to memory bottlenecks; „full avg10=0.30“ indicates rare instances of complete stalls. I correlate rising „some“ values early on with response times and scale, tune, or offload before hard OOMs occur. This perspective prevents me from being misled by seemingly „high free“ RAM, because free pages without fast Reclaim-They make little use of the opportunity.

Measured variable Guideline value Symptom Action
PSI memory (avg10) > 2–3 % sustained Response times are increasing Check RAM headroom, fine-tune cgroup limits
PSI memory full (avg10) > 0.1 % noticeable Short Downtime Identify the cause, stop thrashing
MemAvailable < 10 % of RAM Small buffer Offload Cache/Workload, Plan Capacity
vmstat si/so permanently > 0 Swap Pressure Swappiness/Adjust Swap, Protect Hotset

Symptoms in Hosting Environments

On busy hosts, I first notice latency spikes, while the CPU load appears to remain moderate; the kernel is stuck in reclaim loops, I/O is backed up, and requests are queued. The load average climbs even though cores appear to be free, because many tasks are blocked waiting for memory or I/O; this is a key indicator of increased Prints. Persistent si/so values in vmstat indicate that the system is actively swapping, which slows down TLS handshakes, dynamic content, and query paths. If left unaddressed, the system will enter a state of thrashing: the CPU spends most of its time paging and swapping instead of performing useful work. As the situation escalates, the OOM killer intervenes and terminates processes with high scores; a targeted Analysis of the OOM Killer helps me identify patterns and misconfigurations.

Relevance for Hosting Workloads and Cgroups

In shared environments, even a few memory-intensive applications are enough to increase latency for many users; cgroups mitigate the impact, but they cannot fix an incorrectly sized Instances. In VPS and cloud instances, insufficient RAM or a poor swap strategy leads more quickly to spikes in load; isolation protects other services, but not your own. Databases rely on large buffer pools; if Reclaim displaces them or swap kicks in, query times increase and throughput drops significantly. Container orchestration systems use `memory.low`, `memory.high`, and `memory.max` to protect critical services, mitigate stalls, and terminate them selectively in an emergency. I therefore choose limits carefully and monitor PSI per service to take timely corrective action and ensure reserves for critical Workloads Keep clear.

Monitoring Strategy and Metrics

I check MemAvailable, Buffers, and Cached to understand how much memory can be reclaimed in the short term; MemFree values alone can be misleading. At the same time, I check vmstat: Persistent si/so values indicate swap pressure, which significantly increases I/O and drives up latencies; for background information on Swap utilization I use proven diagnostic patterns. PSI provides me with the missing piece because „some“ and „full“ quantify actual delays; I set alerts based on thresholds and distinguish load spikes from chronic bottlenecks. Time series from `sar` or the observability stack reveal patterns and help me confirm the success of my tuning efforts. `dmesg` reveals OOM events that indicate hard limits or misconfigurations; this allows me to build a coherent picture from the kernel’s perspective, I/O behavior, and Application.

Typical Workloads Under Pressure

Web servers such as Nginx or Apache deliver content more slowly when Reclaim and Swap are running in the background; keep-alive connections remain open longer, which exacerbates queues. PHP and Python stacks consume RAM through framework caches, JIT components, and session data; when paged out, this data shuttles back and forth between RAM and storage, significantly prolonging response times. Databases lose speed as soon as buffer pools shrink or parts of them end up on swap; even a small amount of additional latency per I/O adds up when there are many Queries. Caching services like Redis or Memcached rely on RAM hits; if key areas end up in swap, the benefit is lost and the risk of being terminated under heavy load increases. In all cases, PSI and swap metrics provide the clearest indications that memory has become the bottleneck, rather than the CPU.

System Tuning and Kernel Parameters

I start with vm.swappiness: A moderately reduced setting prevents excessive swapping without blocking necessary reclaim; I consistently measure the effects using PSI. Next, I optimize vm.dirty_ratio and related limits so that I don’t trigger long flush waves while still avoiding excessive small-write operations; both have noticeable Effects Latencies. On Cgroups v2, I set `memory.low` for critical services, `memory.high` for throttling during overload, and `memory.max` as a hard limit with controllable OOMs. I pay special attention to NUMA topologies: Local pressure can arise even though there is still free RAM globally; process and memory binding help mitigate such pitfalls. Finally, I check page cache behavior; unnecessary eviction lowers hit rates and directly costs time in web and DB workloads, which is why the Page Cache Optimization provides helpful insights.

An In-Depth Look at Reclaim Paths

To choose the right measures, I distinguish between kswapd and Direct Reclaim. kswapd operates asynchronously when memory levels fall below the watermarks; it is relatively gentle as long as there is sufficient cache that can be easily reclaimed. Direct Reclaim intervenes synchronously in execution contexts when threads urgently need pages—this is where the stalls noticeable to users occur. I monitor whether reclaim primarily affects file cache or anonymous pages: If the kernel primarily evicts file cache, cache misses increase; if it evicts anonymous memory (e.g., the heap), hard stops and swap activity become likely. Modern working set mechanisms take refault distances into account to retain useful pages longer; if I still see many repeated refaults, I know that hot sets are larger than the available headroom have become.

I also pay attention to compaction and defragmentation: kcompactd attempts to create contiguous blocks, for example for large allocations or THP. If compaction lags behind, I see increased CPU usage by kcompactd, rising latencies, and higher „full“ PSI values during peak loads. In such cases, it often makes more sense to reduce the load or adjust THP policies rather than simply adding „more swap.“.

Swap Strategies in Detail

Swap is not an enemy, but a tool—though if used incorrectly, it can increase latency. Let me clarify:

  • No swap: Safe against swap lags, but risky during peaks—OOMs occur sooner, and Reclaim has no fallback buffer.
  • Moderate Swap On a fast SSD: Good for swapping out cold, anonymous pages; protects hot sets in RAM when swappiness and cgroup limits are set wisely.
  • zswap/zram: Compression reduces the load on I/O; suitable for hosts with lower I/O loads or as a buffer against short-term traffic spikes. I check the CPU budget and compression ratio to ensure I'm not CPU-constrained.

I don't set swappiness to a low value across the board; for workloads with a large file cache, a slightly higher swappiness makes sense to push out cold, anonymous pages and keep the file cache stable. I protect critical services (e.g., databases) with `memory.low` and, if necessary, by locking their hot sets in RAM so that swap doesn’t target the wrong things. The key point is that vmstat si/so and PSI consistently falls when I adjust the strategy; otherwise, I make corrections.

THP, Compaction, and Fragmentation

Transparent Huge Pages (THP) They save TLB hits and help CPU-intensive, memory-intensive applications. Under heavy load, however, they trigger compaction; „always“ can then lead to significant stalls. I use „madvise“ specifically for workloads that benefit from it (e.g., certain in-memory engines), and for latency-sensitive web stacks, I tend to disable THP selectively or enable it only via madvise. In addition, I monitor vm.compaction_proactiveness and check whether proactive compaction delays or actually reduces stalls. If THP pages are frequently fragmented or compaction is running hot, that indicates there is too little headroom or inappropriate allocation patterns in the application.

NUMA Traps and Local Pressure

On NUMA hosts, global „free RAM“ can be misleading: one socket may be under pressure while another remains idle. I check NUMA statistics and anchor processes locally (CPU/memory binding) so that hotsets remain close to the computational load. Direct reclaim on a node despite global reserves signals NUMA imbalances; in this case, interleaved allocations for widely distributed services or strict binding for monolithic workloads can help. PSI per cgroup combined with NUMA statistics shows me whether a single node is generating the queues.

Application-Specific Measures

I analyze memory profiles using ps, top, htop, and profiler tools to identify real memory hogs and leaks; in doing so, I monitor how hot sets change over time. I choose application caches deliberately: too large creates pressure, too small sacrifices performance; I make adjustments based on PSI and response times, not on gut feeling. When pressure signals are detected, the application can voluntarily release less critical caches or temporary data; this allows me to reduce stalls without touching global limits. I adjust startup parameters and GC tuning (e.g., for JVMs) so that working sets stay neatly within RAM; I mitigate aggressive allocation patterns through batching. I also keep an eye on build artifacts and debug symbols, because overlooked remnants silently cost Memory and increase the risk of stalls later on.

Cgroups v2 Features and OOM Strategies

With Cgroups v2, I clearly separate protection, throttling, and hard limits: memory.low Reserves headroom for critical services; the remaining headroom is allocated to less important groups. memory.high throttles performance when limits are exceeded through targeted throttling and forces applications to free up memory before the system is affected. memory.max is the last line of defense—crossing it means OOM within a controlled environment. I set PSI per cgroup, so that alarms are triggered where stalls occur; the global PSI remains stable while a single service crashes—that’s exactly the pattern I want to detect. Together with OOM priorities, I set clear sacrifice rules: unimportant batch workers die first, while core APIs retain their headroom.

Virtualization: Ballooning, KSM, and Overcommit

In virtualized environments, I face a dual challenge: The guest appears to see free RAM, while the hypervisor, via Ballooning This game increases reclaim costs on both sides. I measure PSI on the guest and correlate it with hypervisor metrics; if PSI rises during ballooning events, the VM needs more guaranteed capacity or better cgroup policies on the host. KSM Saves RAM by deduplicating identical pages, but consumes CPU resources; in hosting setups with many similar VMs, it can be worthwhile as long as the additional CPU load does not jeopardize the SLOs. I only plan for overcommitment (e.g., aggressive allocation of many small VMs) with fixed SLO reserves and strict memory.low for latency-critical systems.

Architectural Decisions in Hosting

I rely on horizontal scaling so that individual instances experience fewer traffic spikes; scalable pools smooth out outliers and keep latencies tighter. I clearly separate roles: databases, applications, and caching each get their own resource pools so that reclaim processes don’t cause unexpected side effects across system boundaries. I choose storage based on write latency, because dirty-page flushes directly impact response times; a fast path noticeably reduces reclaim times. In clusters, I allocate RAM reserves per node and use scheduler policies to ensure that load and memory consumption remain more evenly distributed. I automate scaling using PSI thresholds so that rising „some“ values trigger actions before the system comes to a screeching halt and Kill-events.

Capacity Planning and Headroom Models

I define headroom in measurable terms: I maintain sufficient reserves so that „some“ PSI remains below defined thresholds during normal peaks and „full“ practically never occurs. To do this, I use percentiles (e.g., the 99th percentile of hourly load) and plan for 10–30 % of additional RAM depending on the workload’s volatility. Databases are allocated larger fixed reserves, while web frontends scale more dynamically. I calibrate rebound times: How quickly do PSI and si/so drop after a peak? If they remain elevated, this is a sign of insufficient reserves or an inappropriate swap/dirty strategy. This turns capacity planning into a continuous process rather than an annual estimate.

Alarm Notification and SLO-Controlled Tuning

I link PSI to user SLOs: If „some avg10“ increases at the same time as API latencies, I take action. I categorize alerts as „yellow“ (persistent 2–3 % „some,“ „full“ near 0) and „red“ (more than 5 % „some“ or „full“ > 0.1 %). cgroup-based alerts help isolate the noisy minority. Additionally, I set alerts for rising dirty queues and write wait times so I can smooth out dirty waves in a timely manner. The goal is to ensure that tuning measures (swappiness, memory.high, cache sizes) are observable and reversible; I roll out changes incrementally and compare before-and-after results using the same metrics.

Step-by-Step Diagnosis in Everyday Life

First, I check `free -h` and `MemAvailable`: If the value drops significantly, I look for caches that can be safely flushed and for services with growing hot sets. Then I run `vmstat` at short intervals to identify any trends; persistent swapping confirms pressure and leads me to the I/O path. Next, I read /proc/pressure/memory and evaluate „some“ and „full“ over 10, 60, and 300 seconds; I directly correlate rising averages with observed latencies. dmesg shows me OOM traces and reveals which processes most recently triggered or were affected by memory crises; from this, I derive limits and priorities for cgroups. From all of this, I form a hypothesis, implement small tuning steps, verify them using PSI, and keep the Response time at a glance.

Runbooks and Typical Chains of Causes

There are some patterns I keep coming across:

  • Backup or scan jobs displace the page cache: Cache hit rates suddenly drop, and web/DB performance slows down. Action: Throttle jobs (I/O priority), reschedule time slots, set `memory.high` for the job cgroup, and protect the page cache budget of critical services.
  • Leaks in Worker Processes: Slowly increasing anonymous memory; PSI „some“ continues to rise over several hours. Action: Identify the leak, implement auto-restart/recycling policies, and set memory limits to prevent leaks from compromising the entire host.
  • THP-related stalls: kcompactd load increases during traffic peaks. Action: Set THP to „madvise,“ adjust affected services, check compaction parameters, and increase headroom.
  • NUMA Local Print: A socket is thrashing even though there is free global RAM. Solution: Correct affinities, use interleaving for widely distributed workloads, and adjust load balancing in the scheduler.
  • Swap on slow disks: If si/so increases, response times skyrocket. Action: Move swap to faster storage, evaluate zswap/zram, and fine-tune swappiness and cgroup policies.

Every runbook ends with validation: Do „some/full“ occur, and do latencies stabilize? If not, the assumption was incorrect or incomplete—in which case, I continue iterating.

Tools and Tracing in the Workplace

In addition to traditional tools, I rely on a more in-depth analysis: I monitor page cache and anon ratios, page fault rates, refault patterns, and writeback queues. eBPF and tracing approaches pinpoint exactly where delays occur—for example, along reclaim paths, in the writeback process, or during the allocation of large blocks. It’s important to me to use lightweight, production-ready instrumentation: short activation windows, sampling instead of continuous logging, and clear correlation with application metrics. This way, I can identify root causes before making widespread adjustments to parameters.

Key Points and Next Steps

Memory pressure describes the time lost due to memory scarcity, not just occupied RAM; I measure it with PSI, identify trends early, and take data-driven action. By analyzing MemAvailable, vmstat si/so, PSI, and dmesg together, you can identify the true causes of latency spikes and thrashing. Through swappiness, dirty, and cgroup tuning, I specifically reduce stalls and ensure that critical services have their headroom. At the architectural level, horizontal distribution, clearly defined roles, and fast storage paths mitigate the impact of any traffic spikes. Ultimately, what matters is that I continuously link diagnostics and countermeasures: measure, adjust, measure again—until performance and Stability fit again.

Current articles

Server rack with Linux systems and visualized storage usage
Servers and Virtual Machines

Understanding the OOM Killer: When Linux Terminates Processes

Learn how the OOM killer in Linux works when memory is low, how it terminates processes, and how you, as an admin in hosting environments, can prevent out-of-memory problems using the keyword "oom killer linux.".