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
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user