Context & Why — The 2 AM Wake-Up Call
It’s 2:14 AM. Your PostgreSQL server just crashed with an OOM error. The Slack alerts won’t stop. The server had 16GB of RAM, and somehow the kernel decided to kill the process that was using only 4GB of it. If you’ve been there, you already know the culprit: memory overcommit gone wrong.
Linux doesn’t hand out physical RAM the moment a process asks for it. When your application calls malloc() and gets a pointer back, the kernel hasn’t actually reserved real memory yet — it just made a promise. This is memory overcommit: the kernel commits to giving memory that may not yet physically exist, betting that not every process will touch everything it reserved.
That gamble usually works fine on a desktop. On a production server running PostgreSQL, Redis, or a PyTorch inference service, it can end your night very quickly.
The kernel setting that controls all of this is vm.overcommit_memory, and it has three modes:
- Mode 0 (default): Heuristic overcommit. The kernel uses guesswork — it allows most overcommits but rejects obviously unreasonable requests. The problem is that “obvious” varies wildly between workloads.
- Mode 1: Always overcommit. The kernel says yes to every allocation request regardless of available memory. Fast and optimistic — dangerous for production databases.
- Mode 2: Strict overcommit. The kernel calculates a hard commit limit and refuses requests that exceed it. More predictable, more conservative. The right call for stateful services.
The companion setting vm.overcommit_ratio (default: 50) controls how aggressively mode 2 allows commitment:
CommitLimit = (TotalRAM × overcommit_ratio / 100) + SwapTotal
On a 16GB machine with no swap, mode 2 at ratio 50 allows only 8GB total committed memory. A database process asking for 12GB gets rejected upfront — which beats a silent OOM kill mid-transaction at 3 AM.
Checking the Baseline Before Touching Anything
Three years managing 10+ Linux VPS instances drilled one habit into me: understand the baseline before you change anything. Skipping this step is how a routine config update turns into an incident. These commands take under a minute and tell you exactly where your system stands:
# Check current overcommit mode (0, 1, or 2)
cat /proc/sys/vm/overcommit_memory
# Check the overcommit ratio (only relevant in mode 2)
cat /proc/sys/vm/overcommit_ratio
# See the commit budget vs actual committed memory
grep -E 'CommitLimit|Committed_AS' /proc/meminfo
The CommitLimit vs Committed_AS gap is the number that matters most. If Committed_AS is hovering near or above CommitLimit, you’re already at OOM risk — even if free -h shows 4GB “available.”
Also check whether the OOM killer has been silently active:
# Recent OOM events in kernel ring buffer
dmesg | grep -i "oom\|killed process" | tail -20
# Persistent OOM logs via journalctl
journalctl -k | grep -i "oom\|killed" | tail -20
Entries you didn’t know about mean the OOM killer has been active in the background. Fix the baseline before you load it further.
Configuration — Tuning for Your Workload Type
Database Servers (PostgreSQL, MySQL, MariaDB)
Databases are the worst victims of aggressive overcommit. PostgreSQL’s shared_buffers and MySQL’s innodb_buffer_pool_size allocate large memory regions that they actually use, not just speculatively reserve. Mode 0’s heuristics don’t protect them reliably.
Database servers need mode 2 with a carefully calculated ratio:
# Example: 16GB RAM, 4GB swap, want to allow up to 14GB committed
# CommitLimit = (16 × ratio/100) + 4
# Solve for ratio: (14 - 4) / 16 × 100 = 62.5 → use 70 for safety margin
# Apply immediately (runtime only, does not survive reboot)
sysctl -w vm.overcommit_memory=2
sysctl -w vm.overcommit_ratio=70
# Verify the CommitLimit recalculated correctly
grep CommitLimit /proc/meminfo
To persist the configuration across reboots, write to a dedicated sysctl drop-in file — never edit /etc/sysctl.conf directly if you can avoid it:
# Create a dedicated config file
cat > /etc/sysctl.d/99-memory-overcommit.conf << 'EOF'
vm.overcommit_memory = 2
vm.overcommit_ratio = 70
EOF
# Apply without rebooting
sysctl --system
# Verify
sysctl vm.overcommit_memory vm.overcommit_ratio
After applying mode 2, restart your database service. If shared_buffers or innodb_buffer_pool_size is configured higher than the new CommitLimit allows, the service will fail to start — which is the behavior you want. A startup failure is recoverable. A mid-query OOM kill is not.
AI and Machine Learning Workloads
AI inference and training jobs behave differently. PyTorch and TensorFlow allocate memory speculatively, in bursts, and often in ways that confuse the mode 2 commit accounting. Forcing strict overcommit on ML workloads frequently causes legitimate allocation failures mid-computation.
ML workloads need a different approach entirely. Mode 1 paired with cgroup-level memory limits is more effective than kernel-level overcommit restriction:
# Allow the ML framework to allocate freely at the kernel level
sysctl -w vm.overcommit_memory=1
# Constrain the specific service with systemd resource control instead
systemctl set-property ai-inference.service MemoryMax=12G MemoryHigh=10G
systemctl daemon-reload
systemctl restart ai-inference.service
Running AI workloads in Docker? Set hard memory limits at the container level:
# Hard memory + swap limit for a container
docker run \
--memory="10g" \
--memory-swap="10g" \
--name inference-server \
my-model-image python serve.py
Or in docker-compose.yml:
services:
inference:
image: my-model-image
deploy:
resources:
limits:
memory: 10g
This way the AI framework gets the overcommit flexibility it needs while the OS still has a hard ceiling to enforce. The OOM killer will target the container before touching anything else on the host.
Verification & Monitoring
Never assume a sysctl change worked as intended. Verify it immediately, then watch it under real load. That habit has prevented more production incidents than any configuration change I’ve made.
Start with a quick health check script you can run right after any config change:
#!/bin/bash
echo "=== Memory Overcommit Status ==="
echo "Mode : $(cat /proc/sys/vm/overcommit_memory)"
echo "Ratio : $(cat /proc/sys/vm/overcommit_ratio)%"
echo ""
grep -E 'MemTotal|MemAvailable|SwapTotal|CommitLimit|Committed_AS' /proc/meminfo
echo ""
LIMIT=$(grep CommitLimit /proc/meminfo | awk '{print $2}')
COMMITTED=$(grep Committed_AS /proc/meminfo | awk '{print $2}')
PCT=$((COMMITTED * 100 / LIMIT))
echo "Commit usage: ${PCT}% of limit"
if [ "$PCT" -gt 85 ]; then
echo "WARNING: Memory commitment critically high — OOM risk elevated"
fi
In production, node_exporter (Prometheus) already exports these values as node_memory_CommitLimit_bytes and node_memory_Committed_AS_bytes. Add an alert rule so you find out before the OOM killer does:
# prometheus/rules/memory.yml
groups:
- name: memory_overcommit
rules:
- alert: MemoryCommitHigh
expr: node_memory_Committed_AS_bytes / node_memory_CommitLimit_bytes > 0.80
for: 5m
labels:
severity: warning
annotations:
summary: "High memory commitment on {{ $labels.instance }}"
description: "Committed memory is {{ $value | humanizePercentage }} of limit"
During load testing, watch these two values in parallel — they tell a clearer story than free or top alone:
# Terminal 1 — watch for OOM kill events
watch -n 2 'dmesg | grep -c "Out of memory"'
# Terminal 2 — watch commit ratio live
watch -n 2 'grep -E "CommitLimit|Committed_AS" /proc/meminfo'
Run your actual workload — a database restore, a model inference batch, a traffic spike replay. If Committed_AS climbs toward CommitLimit rapidly and flattens just below it, your ratio is well-calibrated. If it never breaks 50% of the limit under peak load, tighten the ratio and reduce your OOM exposure further.
One last thing: add vm.overcommit_memory and vm.overcommit_ratio to your server provisioning checklist. Every new instance gets a baseline /etc/sysctl.d/99-memory-overcommit.conf written before any application is deployed. The 2 AM incident that opened this article happened on a freshly provisioned server where that step was skipped. It won’t happen twice.

