Skip to content

Growing swap usage notification

Growing swap is a quiet harbinger of trouble. While an application slowly “leaks” memory, the system silently pushes pages out to swap: the server is still alive but already starting to crawl on disk I/O. It’s far better to notice this at 50 % swap usage than to catch the OOM killer once memory has run out completely.

The script reads /proc/meminfo every 5 minutes, computes the percentage of used swap (SwapTotal minus SwapFree) and:

  • if usage is ≥ 50 % — sends a normal warning;
  • if usage is ≥ 80 % — sends a high-priority message.

As a nice-to-have, at high usage we also include the current swap rate (si/so from vmstat) — active swap-in/out is far more dangerous than statically occupied but idle swap.

To avoid receiving the same message every 5 minutes, we store the state (ok/warn/crit) in /var/lib/notifly-swap/ — just like in the disk space recipe.

Save as /usr/local/bin/notifly-swap-check:

#!/usr/bin/env bash
set -eu
set -a; source /etc/notifly.env; set +a
WARN=50
CRIT=80
STATE_DIR=/var/lib/notifly-swap
STATE_FILE="$STATE_DIR/level"
mkdir -p "$STATE_DIR"
HOST=$(hostname -s)
# SwapTotal / SwapFree — in kilobytes
SWAP_TOTAL=$(awk '/^SwapTotal:/{print $2}' /proc/meminfo)
SWAP_FREE=$(awk '/^SwapFree:/{print $2}' /proc/meminfo)
# No swap at all — exit quietly
[ "${SWAP_TOTAL:-0}" -gt 0 ] || exit 0
SWAP_USED=$(( SWAP_TOTAL - SWAP_FREE ))
SWAP_PCT=$(( SWAP_USED * 100 / SWAP_TOTAL ))
USED_MB=$(( SWAP_USED / 1024 ))
TOTAL_MB=$(( SWAP_TOTAL / 1024 ))
LAST=$(cat "$STATE_FILE" 2>/dev/null || echo ok)
LEVEL=ok
[ "$SWAP_PCT" -ge "$WARN" ] && LEVEL=warn
[ "$SWAP_PCT" -ge "$CRIT" ] && LEVEL=crit
if [ "$LEVEL" != "$LAST" ]; then
# Current swap rate (si/so, KB/s) — useful when swap is actively churning
read -r SI SO <<<"$(vmstat 1 2 | tail -1 | awk '{print $7, $8}')"
case "$LEVEL" in
warn)
/usr/local/bin/notifly-send \
"⚠️ Swap заполнен на $SWAP_PCT% — $HOST" \
"Занято $USED_MB из $TOTAL_MB МБ swap. Похоже, кто-то подтекает по памяти." 5
;;
crit)
/usr/local/bin/notifly-send \
"🔥 КРИТИЧНО: swap $SWAP_PCT% — $HOST" \
"Занято $USED_MB из $TOTAL_MB МБ swap. Подкачка: si=${SI} so=${SO} КБ/с. До OOM недалеко!" 9
;;
ok)
/usr/local/bin/notifly-send \
"✅ Swap в норме — $HOST" \
"Использование swap вернулось в норму ($SWAP_PCT%)." 3
;;
esac
echo "$LEVEL" > "$STATE_FILE"
fi

Make it executable:

Окно терминала
sudo chmod +x /usr/local/bin/notifly-swap-check
Окно терминала
sudo crontab -e

Add the line:

*/5 * * * * /usr/local/bin/notifly-swap-check >/dev/null 2>&1

/etc/systemd/system/notifly-swap.service:

[Unit]
Description=Notifly swap usage check
[Service]
Type=oneshot
ExecStart=/usr/local/bin/notifly-swap-check

/etc/systemd/system/notifly-swap.timer:

[Unit]
Description=Run notifly-swap every 5 minutes
[Timer]
OnBootSec=5min
OnUnitActiveSec=5min
[Install]
WantedBy=timers.target

Activate it:

Окно терминала
sudo systemctl daemon-reload
sudo systemctl enable --now notifly-swap.timer
sudo systemctl list-timers notifly-swap.timer

Equivalent for Windows servers. Windows has no swap in the Linux sense, but the pagefile plays the role of swap — we watch its usage via Win32_PageFileUsage (CurrentUsage / AllocatedBaseSize) and the total committed memory via Win32_OperatingSystem. Uses the common function Send-Notifly from the sysadmin template/index.

C:\scripts\Notifly-Swap-Check.ps1
. C:\scripts\Notifly.ps1
$Warn = 50
$Crit = 80
$StateDir = "C:\ProgramData\Notifly\swap-state"
New-Item -ItemType Directory -Path $StateDir -Force | Out-Null
$StateFile = Join-Path $StateDir "level.txt"
$Host = $env:COMPUTERNAME
# Pagefile usage: CurrentUsage / AllocatedBaseSize (in MB)
$page = Get-CimInstance Win32_PageFileUsage -ErrorAction SilentlyContinue |
Sort-Object CurrentUsage -Descending | Select-Object -First 1
if (-not $page -or $page.AllocatedBaseSize -le 0) { exit 0 }
$usedPct = [int](($page.CurrentUsage / $page.AllocatedBaseSize) * 100)
$usedMB = [int]$page.CurrentUsage
$totalMB = [int]$page.AllocatedBaseSize
# Total committed memory for context in the message
$os = Get-CimInstance Win32_OperatingSystem
$committedMB = [int](($os.TotalVirtualMemorySize - $os.FreeVirtualMemory) / 1024)
$last = if (Test-Path $StateFile) { Get-Content $StateFile -Raw } else { "ok" }
$level = "ok"
if ($usedPct -ge $Warn) { $level = "warn" }
if ($usedPct -ge $Crit) { $level = "crit" }
if ($level -ne $last.Trim()) {
switch ($level) {
"warn" { Send-Notifly -Title "⚠️ Pagefile $usedPct% — $Host" `
-Message "Занято $usedMB из $totalMB МБ pagefile (закоммичено $committedMB МБ)." -Priority 5 }
"crit" { Send-Notifly -Title "🔥 КРИТИЧНО: pagefile $usedPct% — $Host" `
-Message "Занято $usedMB из $totalMB МБ pagefile. Закоммичено $committedMB МБ. Память на исходе!" -Priority 9 }
"ok" { Send-Notifly -Title "✅ Pagefile в норме — $Host" `
-Message "Использование pagefile вернулось в норму ($usedPct%)." -Priority 3 }
}
$level | Set-Content $StateFile
}

Register in Task Scheduler (as administrator, every 5 minutes as SYSTEM):

Окно терминала
$Action = New-ScheduledTaskAction `
-Execute "powershell.exe" `
-Argument "-NoProfile -ExecutionPolicy Bypass -File C:\scripts\Notifly-Swap-Check.ps1"
$Trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) `
-RepetitionInterval (New-TimeSpan -Minutes 5)
$Princ = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
Register-ScheduledTask -TaskName "Notifly Swap Check" `
-Action $Action -Trigger $Trigger -Principal $Princ -Description "Notifly: проверка swap/pagefile"
  • Early warning about memory leaks — growing swap is visible hours before the OOM killer fires and something crashes.
  • A clear reason for the slowdown. “The server seems alive but everything is wildly slow” is almost always active swapping; si/so in the message confirm the guess right away.
  • Three-level logic (okwarncrit) prevents spam: as long as the level doesn’t change, there are no notifications.
  • Attach the top memory consumers ps -eo pid,user,pmem,rss,comm --sort=-rss | head -6 to immediately see the culprit — pass it via the extras field.
  • Alert not on the absolute percentage, but on the growth rate of swap over the last hour — slowly occupied swap on an idle server is often harmless.
  • Tie the threshold to the push message priority: during the day a quiet notification is enough, at night critical swap should wake you up. All hosts fit neatly into a single monitoring channel — the hostname is already in the title.