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:
@@ -521,6 +521,151 @@ disable_selinux() {
|
||||
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 ─────────────────────────────────────────────────────
|
||||
ensure_sudo() {
|
||||
step "Sudo Privilege Check"
|
||||
@@ -618,6 +763,7 @@ main() {
|
||||
|
||||
set_hostname
|
||||
disable_selinux
|
||||
setup_swap
|
||||
|
||||
setup_proxy
|
||||
setup_ssh_key
|
||||
|
||||
Reference in New Issue
Block a user