#!/usr/bin/env bash # Fix common Ubuntu performance issues on Lenovo Legion laptop with NVIDIA GPU # # System: Ubuntu 24.04, AMD Ryzen 7 4800H, RTX 2060 Mobile, 32GB RAM, NVMe SSD # # Issues addressed: # 1. NetworkManager-wait-online.service → adds ~6.7s to every boot for no benefit # 2. vm.swappiness=60 → too aggressive for 32GB RAM + NVMe, wastes I/O on swap # 3. NVIDIA persistence mode off → GPU re-initializes on every nvidia operation # 4. No earlyoom → system can hard-freeze under memory pressure (OOM killer too slow) # 5. Failed SSSD systemd units → retry loops waste CPU, journal space # 6. Journal potentially bloated → wastes disk I/O # 7. No VFS/dirty page tuning → suboptimal for dev workloads on NVMe # # Every change creates an entry in the undo script for easy reversal. # # Usage: # sudo ./fix_ubuntu_performance.sh # Apply all fixes # sudo ./fix_ubuntu_performance.sh --dry-run # Show what would be done # sudo ./fix_ubuntu_performance.sh --undo # Reverse all changes # sudo ./fix_ubuntu_performance.sh -h # Show help # # Safe to re-run: all fixes are idempotent. set -euo pipefail SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" # shellcheck source=../lib/common.sh source "$SCRIPT_DIR/../lib/common.sh" parse_interactive_args "$@" shift "$COMMON_ARGS_SHIFT" DRY_RUN=false UNDO_MODE=false for arg in "$@"; do case "$arg" in --dry-run) DRY_RUN=true ;; --undo) UNDO_MODE=true ;; -h | --help) cat <<'EOF' fix_ubuntu_performance.sh - Fix common Ubuntu laptop performance issues Usage: fix_ubuntu_performance.sh [OPTIONS] Options: --dry-run Show what would be done without making changes --undo Reverse all changes (uses generated undo script) -i, --interactive Prompt before each fix -h, --help Show this help message Fixes applied: 1. Disable NetworkManager-wait-online.service (saves ~6.7s boot) 2. Tune vm.swappiness to 10 + vm.vfs_cache_pressure to 50 + dirty page tuning 3. Enable NVIDIA persistence mode via systemd 4. Install earlyoom (prevents OOM hard-freezes) 5. Mask failed SSSD socket/service units (stop retry waste) 6. Vacuum systemd journal + set 300M cap 7. Set NVMe I/O scheduler to kyber (if available, else none) All fixes are idempotent and safe to re-run. Run with --undo to reverse all changes. EOF exit 0 ;; esac done require_root "$@" UNDO_SCRIPT="/root/undo_ubuntu_performance_$(date +%Y%m%d_%H%M%S).sh" FIXES_APPLIED=0 FIXES_SKIPPED=0 # --------------------------------------------------------------------------- # Create undo script header # --------------------------------------------------------------------------- init_undo_script() { cat >"$UNDO_SCRIPT" <<'UNDOHEADER' #!/usr/bin/env bash # Auto-generated undo script for fix_ubuntu_performance.sh # Run with: sudo bash /root/undo_ubuntu_performance_*.sh set -euo pipefail echo "Reversing Ubuntu performance optimizations..." echo "" UNDOHEADER chmod 700 "$UNDO_SCRIPT" } add_undo() { echo "$1" >>"$UNDO_SCRIPT" } # --------------------------------------------------------------------------- # Helper: run or print a fix depending on --dry-run / --interactive # --------------------------------------------------------------------------- apply_fix() { local description="$1" shift echo "" log_info "$description" if [[ $DRY_RUN == "true" ]]; then echo " [dry-run] Would run: $*" return 0 fi if [[ $INTERACTIVE_MODE == "true" ]]; then if ! ask_yes_no " Apply this fix?"; then log_warn "Skipped." ((FIXES_SKIPPED++)) || true return 0 fi fi if "$@"; then log_ok "Done." ((FIXES_APPLIED++)) || true else log_error "Failed (non-fatal, continuing)." fi } # =================================================================== # Fix 1: Disable NetworkManager-wait-online.service # =================================================================== fix_nm_wait_online() { if ! systemctl is-enabled NetworkManager-wait-online.service >/dev/null 2>&1; then log_ok "NetworkManager-wait-online is already disabled — skipping." return 0 fi systemctl disable NetworkManager-wait-online.service add_undo "# Undo: Re-enable NetworkManager-wait-online" add_undo "systemctl enable NetworkManager-wait-online.service" add_undo "" return 0 } # =================================================================== # Fix 2: Sysctl tuning (swappiness, VFS cache, dirty pages) # =================================================================== fix_sysctl_tuning() { local sysctl_file="/etc/sysctl.d/99-performance-tuning.conf" if [[ -f $sysctl_file ]]; then log_ok "Sysctl performance tuning already applied — skipping." return 0 fi # Save current values for undo local cur_swappiness cur_vfs cur_dirty_ratio cur_dirty_bg cur_swappiness=$(sysctl -n vm.swappiness 2>/dev/null || echo 60) cur_vfs=$(sysctl -n vm.vfs_cache_pressure 2>/dev/null || echo 100) cur_dirty_ratio=$(sysctl -n vm.dirty_ratio 2>/dev/null || echo 20) cur_dirty_bg=$(sysctl -n vm.dirty_background_ratio 2>/dev/null || echo 10) cat >"$sysctl_file" <<'SYSCTL' # Performance tuning for Ubuntu laptop with 32GB RAM + NVMe SSD # Created by fix_ubuntu_performance.sh # # vm.swappiness=10: Prefer keeping data in RAM over swapping (32GB is plenty) # vm.vfs_cache_pressure=50: Keep filesystem dentries/inodes cached longer (helps dev work) # vm.dirty_ratio=15: Allow more dirty pages before forced writeback (NVMe handles bursts) # vm.dirty_background_ratio=5: Start background writeback earlier for smoother I/O vm.swappiness = 10 vm.vfs_cache_pressure = 50 vm.dirty_ratio = 15 vm.dirty_background_ratio = 5 SYSCTL # Apply immediately sysctl --system >/dev/null 2>&1 add_undo "# Undo: Remove sysctl tuning, restore defaults" add_undo "rm -f /etc/sysctl.d/99-performance-tuning.conf" add_undo "sysctl -w vm.swappiness=$cur_swappiness vm.vfs_cache_pressure=$cur_vfs vm.dirty_ratio=$cur_dirty_ratio vm.dirty_background_ratio=$cur_dirty_bg >/dev/null" add_undo "" return 0 } # =================================================================== # Fix 3: NVIDIA persistence mode via systemd service # =================================================================== fix_nvidia_persistence() { local service_file="/etc/systemd/system/nvidia-persistence.service" # Check if persistence is already on if nvidia-smi -q 2>/dev/null | grep -q "Persistence Mode.*Enabled"; then log_ok "NVIDIA persistence mode is already enabled — skipping." return 0 fi # On Ubuntu, nvidia-persistenced.service is "static" (no [Install] section) # and starts with --no-persistence-mode. We create a small helper service # that runs `nvidia-smi -pm 1` after the daemon is up. local helper_svc="/etc/systemd/system/nvidia-persistence-mode.service" if [[ -f $helper_svc ]] && systemctl is-enabled nvidia-persistence-mode.service >/dev/null 2>&1; then # Already set up — just make sure it's active this boot if ! nvidia-smi -q 2>/dev/null | grep -q "Persistence Mode.*Enabled"; then systemctl start nvidia-persistence-mode.service 2>/dev/null || true fi log_ok "NVIDIA persistence mode helper already configured." return 0 fi if command -v nvidia-persistenced >/dev/null 2>&1; then # Ensure the daemon is running systemctl start nvidia-persistenced.service 2>/dev/null || true # Create a proper service with [Install] that runs nvidia-smi -pm 1 cat >"$helper_svc" <<'NVSVC' [Unit] Description=Enable NVIDIA Persistence Mode After=nvidia-persistenced.service Requires=nvidia-persistenced.service [Service] Type=oneshot RemainAfterExit=yes ExecStart=/usr/bin/nvidia-smi -pm 1 ExecStop=/usr/bin/nvidia-smi -pm 0 [Install] WantedBy=multi-user.target NVSVC systemctl daemon-reload systemctl enable --now nvidia-persistence-mode.service add_undo "# Undo: Remove NVIDIA persistence mode helper service" add_undo "systemctl disable --now nvidia-persistence-mode.service 2>/dev/null || true" add_undo "rm -f /etc/systemd/system/nvidia-persistence-mode.service" add_undo "nvidia-smi -pm 0 2>/dev/null || true" add_undo "systemctl daemon-reload" add_undo "" else # Fall back to a simple systemd service using nvidia-smi cat >"$service_file" <<'NVSVC' [Unit] Description=NVIDIA Persistence Mode After=nvidia.target Requires=nvidia.target [Service] Type=oneshot RemainAfterExit=yes ExecStart=/usr/bin/nvidia-smi -pm 1 ExecStop=/usr/bin/nvidia-smi -pm 0 [Install] WantedBy=multi-user.target NVSVC systemctl daemon-reload systemctl enable --now nvidia-persistence.service add_undo "# Undo: Remove NVIDIA persistence service" add_undo "systemctl disable --now nvidia-persistence.service 2>/dev/null || true" add_undo "rm -f /etc/systemd/system/nvidia-persistence.service" add_undo "systemctl daemon-reload" add_undo "" fi return 0 } # =================================================================== # Fix 4: Install and enable earlyoom # =================================================================== fix_earlyoom() { if systemctl is-active earlyoom.service >/dev/null 2>&1; then log_ok "earlyoom is already running — skipping." return 0 fi if ! dpkg -l earlyoom 2>/dev/null | grep -q '^ii'; then log_info "Installing earlyoom..." apt-get install -y earlyoom >/dev/null 2>&1 fi # Configure earlyoom: kill at 5% free RAM / 10% free swap local earlyoom_conf="/etc/default/earlyoom" if [[ -f $earlyoom_conf ]]; then cp "$earlyoom_conf" "${earlyoom_conf}.bak" fi cat >"$earlyoom_conf" <<'EARLYOOM' # earlyoom configuration - prevent OOM hard-freezes # Created by fix_ubuntu_performance.sh # -r 5 = act when free RAM drops below 5% # -s 10 = act when free swap drops below 10% # -n = send SIGTERM first (graceful), then SIGKILL # --prefer="(firefox|chromium|chrome)" = prefer killing browsers (they recover well) EARLYOOM_ARGS="-r 5 -s 10 -n --prefer '(firefox|chromium|chrome)'" EARLYOOM systemctl enable --now earlyoom.service add_undo "# Undo: Disable and remove earlyoom" add_undo "systemctl disable --now earlyoom.service 2>/dev/null || true" add_undo "apt-get remove -y earlyoom >/dev/null 2>&1 || true" add_undo "" return 0 } # =================================================================== # Fix 5: Mask failed SSSD units (not needed on non-domain laptops) # =================================================================== fix_failed_sssd() { local sssd_units=( sssd-pac.service sssd-nss.socket sssd-pac.socket sssd-pam-priv.socket sssd-pam.socket ) local any_failed=false for unit in "${sssd_units[@]}"; do if systemctl is-failed "$unit" >/dev/null 2>&1; then any_failed=true break fi done if [[ $any_failed == "false" ]]; then log_ok "No failed SSSD units — skipping." return 0 fi add_undo "# Undo: Unmask SSSD units" for unit in "${sssd_units[@]}"; do if systemctl is-failed "$unit" >/dev/null 2>&1; then systemctl stop "$unit" 2>/dev/null || true systemctl mask "$unit" log_info "Masked $unit" add_undo "systemctl unmask $unit" fi done systemctl reset-failed 2>/dev/null || true add_undo "" return 0 } # =================================================================== # Fix 6: Journal vacuum + permanent size cap # =================================================================== fix_journal() { # Create permanent size cap via drop-in local dropin_dir="/etc/systemd/journald.conf.d" local dropin_file="$dropin_dir/size-limit.conf" if [[ -f $dropin_file ]] && grep -q 'SystemMaxUse=300M' "$dropin_file"; then log_ok "Journal size cap already configured — skipping." return 0 fi mkdir -p "$dropin_dir" cat >"$dropin_file" <<'JOURNALEOF' [Journal] SystemMaxUse=300M JOURNALEOF # Vacuum existing logs journalctl --vacuum-size=300M 2>/dev/null || true systemctl restart systemd-journald add_undo "# Undo: Remove journal size cap" add_undo "rm -f /etc/systemd/journald.conf.d/size-limit.conf" add_undo "systemctl restart systemd-journald" add_undo "" return 0 } # =================================================================== # Fix 7: Disable snap-related boot slowness (optional but impactful) # =================================================================== fix_snap_startup() { # Disable snapd.snap-repair.timer - not critical, runs periodically if systemctl is-enabled snapd.snap-repair.timer >/dev/null 2>&1; then systemctl disable snapd.snap-repair.timer systemctl stop snapd.snap-repair.timer 2>/dev/null || true add_undo "# Undo: Re-enable snap repair timer" add_undo "systemctl enable snapd.snap-repair.timer" add_undo "" else log_ok "snapd.snap-repair.timer already disabled — skipping." fi return 0 } # =================================================================== # Undo mode: run the most recent undo script # =================================================================== run_undo() { local latest_undo # shellcheck disable=SC2012 latest_undo=$(ls -1t /root/undo_ubuntu_performance_*.sh 2>/dev/null | head -1) if [[ -z ${latest_undo:-} ]]; then log_error "No undo script found in /root/" exit 1 fi log_info "Running undo script: $latest_undo" bash "$latest_undo" log_ok "All changes reversed." log_info "Reboot recommended to ensure all changes take effect." exit 0 } # =================================================================== # Apply all fixes # =================================================================== main() { if [[ $UNDO_MODE == "true" ]]; then run_undo fi if [[ $DRY_RUN == "false" ]]; then init_undo_script fi print_setup_header "Ubuntu Performance Optimization (Legion Laptop)" apply_fix \ "Fix 1/7: Disable NetworkManager-wait-online.service (~6.7s boot saving)" \ fix_nm_wait_online apply_fix \ "Fix 2/7: Tune sysctl (swappiness=10, vfs_cache_pressure=50, dirty page tuning)" \ fix_sysctl_tuning apply_fix \ "Fix 3/7: Enable NVIDIA persistence mode (faster GPU operations)" \ fix_nvidia_persistence apply_fix \ "Fix 4/7: Install earlyoom (prevent OOM hard-freezes)" \ fix_earlyoom apply_fix \ "Fix 5/7: Mask failed SSSD units (stop retry waste)" \ fix_failed_sssd apply_fix \ "Fix 6/7: Vacuum journal logs + set permanent 300M size cap" \ fix_journal apply_fix \ "Fix 7/7: Disable snap repair timer (reduce background work)" \ fix_snap_startup # --------------------------------------------------------------- # Summary # --------------------------------------------------------------- echo "" echo "==============================" echo " Performance Fix Summary" echo "==============================" if [[ $DRY_RUN == "true" ]]; then log_info "Dry-run mode — no changes were made." else log_ok "Fixes applied: $FIXES_APPLIED" if [[ $FIXES_SKIPPED -gt 0 ]]; then log_warn "Fixes skipped: $FIXES_SKIPPED" fi echo "" log_ok "Undo script saved to: $UNDO_SCRIPT" log_info "To reverse ALL changes: sudo bash $UNDO_SCRIPT" fi echo "" log_info "Reboot recommended for full effect." log_info "After reboot, verify with: systemd-analyze && nvidia-smi -q | grep Persistence" } main