feat(setup): add swap check and optional swapfile creation

Detect insufficient swap from RAM heuristics, suggest additional size,
and let the user create a persistent /swapfile during server setup.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
FlintyLemming
2026-05-22 13:59:05 +08:00
parent 4d8f0b15ab
commit 7e7d94cc6b
+146
View File
@@ -521,6 +521,151 @@ disable_selinux() {
esac esac
} }
# ─── Virtual Memory (Swap) ────────────────────────────────────────────────────
# Read a numeric field from /proc/meminfo (value in kB).
meminfo_kb() {
local key="$1"
awk -v k="$key" '$1 == k":" { print $2; exit }' /proc/meminfo
}
format_size_kb() {
local kb="${1:-0}"
if (( kb >= 1048576 )); then
awk -v n="$kb" 'BEGIN { printf "%.1f GiB", n / 1048576 }'
elif (( kb >= 1024 )); then
awk -v n="$kb" 'BEGIN { printf "%.0f MiB", n / 1024 }'
else
echo "${kb} KiB"
fi
}
# Round up to the nearest 512 MiB (sensible swapfile size).
round_swap_kb() {
local kb="${1:-0}"
local unit=524288
echo $(( (kb + unit - 1) / unit * unit ))
}
# Recommended total swap (kB) from RAM — common server/desktop heuristics.
recommend_swap_kb() {
local ram_kb="${1:-0}"
local rec=0
if (( ram_kb < 2097152 )); then
rec=$(( ram_kb * 2 ))
elif (( ram_kb < 8388608 )); then
rec=$ram_kb
elif (( ram_kb < 67108864 )); then
rec=4194304
else
rec=8388608
fi
round_swap_kb "$rec"
}
setup_swap() {
step "Virtual Memory (Swap)"
if [ ! -r /proc/meminfo ]; then
warn "Cannot read /proc/meminfo — skipping swap check"
return
fi
local ram_kb swap_kb swap_free_kb recommended_kb deficit_kb add_kb
ram_kb="$(meminfo_kb MemTotal)"
swap_kb="$(meminfo_kb SwapTotal)"
swap_free_kb="$(meminfo_kb SwapFree)"
recommended_kb="$(recommend_swap_kb "$ram_kb")"
info "Physical memory (RAM): $(format_size_kb "$ram_kb")"
info "Swap (total): $(format_size_kb "$swap_kb")"
if (( swap_kb > 0 )); then
info "Swap (free): $(format_size_kb "$swap_free_kb")"
fi
info "Recommended total swap: $(format_size_kb "$recommended_kb")"
if [ -r /proc/sys/vm/swappiness ]; then
info "Swappiness: $(cat /proc/sys/vm/swappiness)"
fi
if swapon --show=NAME 2>/dev/null | grep -q .; then
info "Active swap devices:"
swapon --show=NAME,TYPE,SIZE,USED,PRIO 2>/dev/null | while IFS= read -r line; do
echo " $line"
done
fi
# Within ~5% of recommendation counts as adequate.
local threshold_kb=$(( recommended_kb * 95 / 100 ))
if (( swap_kb >= threshold_kb )); then
success "Swap is adequate (current ≥ recommended)"
return
fi
deficit_kb=$(( recommended_kb - swap_kb ))
add_kb="$(round_swap_kb "$deficit_kb")"
if (( add_kb < 524288 )); then
add_kb=524288
fi
warn "Swap is below recommended — consider increasing virtual memory"
echo -e " Suggested ${BOLD}additional${NC} swap: $(format_size_kb "$add_kb")"
echo -e " (would bring total swap to about $(format_size_kb "$(( swap_kb + add_kb ))"))"
prompt_read ans "Create a swap file now? [y/N] "
case "$ans" in
[Yy]*)
create_swapfile "$add_kb"
;;
*)
info "Skipping swap creation — you can add swap manually later"
;;
esac
}
create_swapfile() {
local add_kb="${1:?swap size in kB required}"
local swapfile="/swapfile"
if [ -f "$swapfile" ]; then
if swapon --show=NAME 2>/dev/null | grep -qF "$swapfile"; then
warn "$swapfile is already active — not creating a second file"
return
fi
warn "$swapfile exists but is not active — remove or enable it manually before re-running"
return 1
fi
local size_human
size_human="$(format_size_kb "$add_kb")"
info "Creating ${size_human} swap file at ${swapfile} ..."
local size_bytes=$(( add_kb * 1024 ))
if psudo fallocate -l "$size_bytes" "$swapfile" 2>/dev/null; then
success "Allocated swap file with fallocate"
else
warn "fallocate failed — falling back to dd (this may take a while)"
local count_mib=$(( add_kb / 1024 ))
if ! psudo dd if=/dev/zero of="$swapfile" bs=1M count="$count_mib" status=progress 2>/dev/null; then
psudo dd if=/dev/zero of="$swapfile" bs=1M count="$count_mib"
fi
fi
psudo chmod 600 "$swapfile"
psudo mkswap "$swapfile" >/dev/null
psudo swapon "$swapfile"
if ! psudo grep -qF "$swapfile" /etc/fstab 2>/dev/null; then
echo "$swapfile none swap sw 0 0" | psudo tee -a /etc/fstab > /dev/null
success "Persisted swap in /etc/fstab"
fi
local new_swap_kb
new_swap_kb="$(meminfo_kb SwapTotal)"
success "Swap enabled — total swap is now $(format_size_kb "$new_swap_kb")"
}
# ─── Sudo Privilege Check ───────────────────────────────────────────────────── # ─── Sudo Privilege Check ─────────────────────────────────────────────────────
ensure_sudo() { ensure_sudo() {
step "Sudo Privilege Check" step "Sudo Privilege Check"
@@ -618,6 +763,7 @@ main() {
set_hostname set_hostname
disable_selinux disable_selinux
setup_swap
setup_proxy setup_proxy
setup_ssh_key setup_ssh_key