When analyzing your Munin memory graph, three key metrics stand out:
apps ............. 0.7G (actual physical memory used by applications)
cached ........... varies (disk cache using free memory)
committed ........ ~1.5G (green line, what the kernel has promised to processes)
Committed memory represents the total memory the kernel has "promised" to all processes, including:
- Currently allocated physical RAM
- Potential future allocations (even if not yet used)
- Memory that could be swapped out
In your case with multiple services running:
# Check per-process memory with smem
sudo smem -t -k -P "mysql|node|apache|nginx|jenkins"
PID User Command Swap USS PSS RSS
1234 mysql /usr/sbin/mysqld 72.3M 312.4M 328.1M 412.8M
5678 node /usr/bin/node app.js 48.1M 158.2M 163.4M 201.5M
Linux starts swapping when:
Committed Memory > (Physical RAM + Swap Space) * vm.overcommit_ratio
Check your current thresholds:
cat /proc/sys/vm/swappiness
cat /proc/sys/vm/overcommit_ratio
For MySQL (often the biggest consumer):
# In my.cnf
[mysqld]
innodb_buffer_pool_size = 256M # Reduce from default
table_open_cache = 400 # Down from 2000
Node.js Memory Limits:
# Launch Node with explicit memory limit
node --max-old-space-size=384 app.js
Apache/Nginx Tuning:
# Apache (prefork)
MaxRequestWorkers 20 # Down from 150
# Nginx
worker_processes 2; # Match CPU cores
worker_connections 768; # Down from 1024
Consider cgroups for strict memory containment:
# Create memory-limited group
cgcreate -g memory:/limited_group
echo 800M > /sys/fs/cgroup/memory/limited_group/memory.limit_in_bytes
# Run Jenkins in the constrained group
cgexec -g memory:limited_group java -jar jenkins.war
html
When monitoring server performance with Munin, the "committed memory" metric (green line) often causes confusion. Unlike "apps" memory (0.7G/1.5G in your case), committed memory represents the total memory promised to processes by the Linux kernel - including both currently used memory and potential future allocations.
Your setup running MySQL, Node.js, Apache, and Nginx simultaneously creates competing memory demands:
# Check current memory commitments
$ grep Commit /proc/meminfo
CommitLimit: 1548284 kB
Committed_AS: 1423568 kB
When Committed_AS approaches CommitLimit (as in your graph), the kernel starts aggressively using swap to honor memory promises.
MySQL Tuning
# In my.cnf
[mysqld]
innodb_buffer_pool_size = 256M # Reduce from default
key_buffer_size = 32M
table_open_cache = 400
Node.js Memory Management
// Limit heap in your Node app
node --max-old-space-size=256 app.js
Apache/Nginx Coexistence
Running both web servers is redundant. Consider:
# Nginx as reverse proxy
server {
listen 80;
location / {
proxy_pass http://localhost:8080;
}
}
If after optimizations your committed memory still exceeds 70% of physical RAM during normal operation, hardware upgrade becomes necessary. Use this calculation:
# Memory headroom needed
awk '/MemTotal/ {total=$2} /MemAvailable/ {avail=$2}
END {print (total-avail)/total*100}' /proc/meminfo
For deeper analysis, combine Munin with:
# Real-time memory breakdown
sudo smem -t -k -P "mysql|node|apache|nginx"