#!/bin/bash # ========================================================== # ProxMenux - Complete Post-Installation Script with Registration # ========================================================== # Author : MacRimi # Copyright : (c) 2024 MacRimi # License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE) # Version : 1.0 # Last Updated: 06/07/2025 # ========================================================== # Description: # # The script performs system optimizations including: # - Repository configuration and system upgrades # - Subscription banner removal and UI enhancements # - Advanced memory management and kernel optimizations # - Network stack tuning and security hardening # - Storage optimizations including log2ram for SSD protection # - System limits increases and entropy generation improvements # - Journald and logrotate optimizations for better log management # - Security enhancements including RPC disabling and time synchronization # - Bash environment customization and system monitoring setup # # Key Features: # - Zero-interaction automation: Runs completely unattended # - Intelligent hardware detection: Automatically detects SSD/NVMe for log2ram # - RAM-aware configurations: Adjusts settings based on available system memory # - Comprehensive error handling: Robust installation with fallback mechanisms # - Registration system: Tracks installed optimizations for easy management # - Reboot management: Intelligently handles reboot requirements # - Translation support: Multi-language compatible through ProxMenux framework # - Rollback compatibility: All optimizations can be reversed using the uninstall script # # This script is based on the post-install script cutotomizable # ========================================================== # Configuration REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main" BASE_DIR="/usr/local/share/proxmenux" UTILS_FILE="$BASE_DIR/utils.sh" VENV_PATH="/opt/googletrans-env" TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json" if [[ -f "$UTILS_FILE" ]]; then source "$UTILS_FILE" fi load_language initialize_cache # Global variables OS_CODENAME="$(grep "VERSION_CODENAME=" /etc/os-release | cut -d"=" -f 2 | xargs)" RAM_SIZE_GB=$(( $(vmstat -s | grep -i "total memory" | xargs | cut -d" " -f 1) / 1024 / 1000)) NECESSARY_REBOOT=0 SCRIPT_TITLE="Customizable post-installation optimization script" # ========================================================== # Tool registration system ensure_tools_json() { [ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON" } register_tool() { local tool="$1" local state="$2" ensure_tools_json jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" } # ========================================================== lvm_repair_check() { msg_info "$(translate "Checking and repairing old LVM PV headers (if needed)...")" pvs_output=$(LC_ALL=C pvs -v 2>&1 | grep "old PV header") if [ -z "$pvs_output" ]; then msg_ok "$(translate "No PVs with old headers found.")" register_tool "lvm_repair" true return fi declare -A vg_map while read -r line; do pv=$(echo "$line" | grep -o '/dev/[^ ]*') vg=$(pvs -o vg_name --noheadings "$pv" | awk '{print $1}') if [ -n "$vg" ]; then vg_map["$vg"]=1 fi done <<< "$pvs_output" for vg in "${!vg_map[@]}"; do msg_warn "$(translate "Old PV header(s) found in VG $vg. Updating metadata...")" vgck --updatemetadata "$vg" vgchange -ay "$vg" if [ $? -ne 0 ]; then msg_warn "$(translate "Metadata update failed for VG $vg. Review manually.")" else msg_ok "$(translate "Metadata updated successfully for VG $vg")" fi done msg_ok "$(translate "LVM PV headers check completed")" } # ========================================================== cleanup_duplicate_repos() { local sources_file="/etc/apt/sources.list" local temp_file=$(mktemp) local cleaned_count=0 declare -A seen_repos while IFS= read -r line || [[ -n "$line" ]]; do if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "$line" ]]; then echo "$line" >> "$temp_file" continue fi if [[ "$line" =~ ^deb ]]; then read -r _ url dist components <<< "$line" local key="${url}_${dist}" if [[ -v "seen_repos[$key]" ]]; then echo "# $line" >> "$temp_file" cleaned_count=$((cleaned_count + 1)) else echo "$line" >> "$temp_file" seen_repos[$key]="$components" fi else echo "$line" >> "$temp_file" fi done < "$sources_file" mv "$temp_file" "$sources_file" chmod 644 "$sources_file" local pve_files=(/etc/apt/sources.list.d/*proxmox*.list /etc/apt/sources.list.d/*pve*.list) local pve_content="deb http://download.proxmox.com/debian/pve ${OS_CODENAME} pve-no-subscription" local pve_public_repo="/etc/apt/sources.list.d/pve-public-repo.list" local pve_public_repo_exists=false if [ -f "$pve_public_repo" ] && grep -q "^deb.*pve-no-subscription" "$pve_public_repo"; then pve_public_repo_exists=true fi for file in "${pve_files[@]}"; do if [ -f "$file" ] && grep -q "^deb.*pve-no-subscription" "$file"; then if ! $pve_public_repo_exists && [[ "$file" == "$pve_public_repo" ]]; then sed -i 's/^# *deb/deb/' "$file" pve_public_repo_exists=true elif [[ "$file" != "$pve_public_repo" ]]; then sed -i 's/^deb/# deb/' "$file" cleaned_count=$((cleaned_count + 1)) fi fi done apt update } apt_upgrade() { NECESSARY_REBOOT=1 if [ -f /etc/apt/sources.list.d/pve-enterprise.list ] && grep -q "^deb" /etc/apt/sources.list.d/pve-enterprise.list; then msg_info "$(translate "Disabling enterprise Proxmox repository...")" sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/pve-enterprise.list msg_ok "$(translate "Enterprise Proxmox repository disabled")" fi if [ -f /etc/apt/sources.list.d/ceph.list ] && grep -q "^deb" /etc/apt/sources.list.d/ceph.list; then msg_info "$(translate "Disabling enterprise Proxmox Ceph repository...")" sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/ceph.list msg_ok "$(translate "Enterprise Proxmox Ceph repository disabled")" fi if [ ! -f /etc/apt/sources.list.d/pve-public-repo.list ] || ! grep -q "pve-no-subscription" /etc/apt/sources.list.d/pve-public-repo.list; then msg_info "$(translate "Enabling free public Proxmox repository...")" echo "deb http://download.proxmox.com/debian/pve ${OS_CODENAME} pve-no-subscription" > /etc/apt/sources.list.d/pve-public-repo.list msg_ok "$(translate "Free public Proxmox repository enabled")" fi sources_file="/etc/apt/sources.list" need_update=false sed -i 's|ftp.es.debian.org|deb.debian.org|g' "$sources_file" if grep -q "^deb http://security.debian.org ${OS_CODENAME}-security main contrib" "$sources_file"; then sed -i "s|^deb http://security.debian.org ${OS_CODENAME}-security main contrib|deb http://security.debian.org/debian-security ${OS_CODENAME}-security main contrib non-free non-free-firmware|" "$sources_file" msg_ok "$(translate "Replaced security repository with full version")" need_update=true fi if ! grep -q "deb http://security.debian.org/debian-security ${OS_CODENAME}-security" "$sources_file"; then echo "deb http://security.debian.org/debian-security ${OS_CODENAME}-security main contrib non-free non-free-firmware" >> "$sources_file" need_update=true fi if ! grep -q "deb http://deb.debian.org/debian ${OS_CODENAME} " "$sources_file"; then echo "deb http://deb.debian.org/debian ${OS_CODENAME} main contrib non-free non-free-firmware" >> "$sources_file" need_update=true fi if ! grep -q "deb http://deb.debian.org/debian ${OS_CODENAME}-updates" "$sources_file"; then echo "deb http://deb.debian.org/debian ${OS_CODENAME}-updates main contrib non-free non-free-firmware" >> "$sources_file" need_update=true fi msg_ok "$(translate "Debian repositories configured correctly")" # =================================================== if [ ! -f /etc/apt/apt.conf.d/no-bookworm-firmware.conf ]; then msg_info "$(translate "Disabling non-free firmware warnings...")" echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' > /etc/apt/apt.conf.d/no-bookworm-firmware.conf msg_ok "$(translate "Non-free firmware warnings disabled")" fi msg_info "$(translate "Updating package lists...")" if apt-get update > /dev/null 2>&1; then msg_ok "$(translate "Package lists updated")" else msg_error "$(translate "Failed to update package lists")" return 1 fi msg_info "$(translate "Removing conflicting utilities...")" if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' purge ntp openntpd systemd-timesyncd > /dev/null 2>&1; then msg_ok "$(translate "Conflicting utilities removed")" else msg_error "$(translate "Failed to remove conflicting utilities")" fi msg_info "$(translate "Performing packages upgrade...")" apt-get install pv -y > /dev/null 2>&1 total_packages=$(apt-get -s dist-upgrade | grep "^Inst" | wc -l) if [ "$total_packages" -eq 0 ]; then total_packages=1 fi msg_ok "$(translate "Packages upgrade successfull")" tput civis tput sc ( /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' dist-upgrade 2>&1 | \ while IFS= read -r line; do if [[ "$line" =~ ^(Setting up|Unpacking|Preparing to unpack|Processing triggers for) ]]; then package_name=$(echo "$line" | sed -E 's/.*(Setting up|Unpacking|Preparing to unpack|Processing triggers for) ([^ ]+).*/\2/') [ -z "$package_name" ] && package_name="$(translate "Unknown")" tput rc tput ed row=$(( $(tput lines) - 6 )) tput cup $row 0; echo "$(translate "Installing packages...")" tput cup $((row + 1)) 0; echo "──────────────────────────────────────────────" tput cup $((row + 2)) 0; echo "Package: $package_name" tput cup $((row + 3)) 0; echo "Progress: [ ] 0%" tput cup $((row + 4)) 0; echo "──────────────────────────────────────────────" for i in $(seq 1 10); do progress=$((i * 10)) tput cup $((row + 3)) 9 printf "[%-50s] %3d%%" "$(printf "#%.0s" $(seq 1 $((progress/2))))" "$progress" done fi done ) if [ $? -eq 0 ]; then tput rc tput ed msg_ok "$(translate "System upgrade completed")" fi msg_info "$(translate "Installing additional Proxmox packages...")" if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install zfsutils-linux proxmox-backup-restore-image chrony > /dev/null 2>&1; then msg_ok "$(translate "Additional Proxmox packages installed")" else msg_error "$(translate "Failed to install additional Proxmox packages")" fi lvm_repair_check cleanup_duplicate_repos msg_ok "$(translate "Proxmox update completed")" } # ========================================================== remove_subscription_banner() { msg_info "$(translate "Removing Proxmox subscription nag banner...")" local JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js" local GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz" local APT_HOOK="/etc/apt/apt.conf.d/no-nag-script" if [[ ! -f "$APT_HOOK" ]]; then cat <<'EOF' > "$APT_HOOK" DPkg::Post-Invoke { "dpkg -V proxmox-widget-toolkit | grep -q '/proxmoxlib\.js$'; if [ $? -eq 1 ]; then { echo 'Removing subscription nag from UI...'; sed -i '/.*data\.status.*{/{s/\!//;s/active/NoMoreNagging/;s/Active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; rm -f /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz; }; fi"; }; EOF fi if [[ -f "$JS_FILE" ]]; then sed -i '/.*data\.status.*{/{s/\!//;s/active/NoMoreNagging/;s/Active/NoMoreNagging/}' "$JS_FILE" [[ -f "$GZ_FILE" ]] && rm -f "$GZ_FILE" touch "$JS_FILE" fi apt --reinstall install proxmox-widget-toolkit -y > /dev/null 2>&1 msg_ok "$(translate "Subscription nag banner removed successfully")" register_tool "subscription_banner" true } # ========================================================== configure_time_sync() { msg_info "$(translate "Configuring system time settings...")" this_ip=$(dig +short myip.opendns.com @resolver1.opendns.com) if [ -z "$this_ip" ]; then msg_warn "$(translate "Failed to obtain public IP address")" timezone="UTC" else timezone=$(curl -s "https://ipapi.co/${this_ip}/timezone") if [ -z "$timezone" ]; then msg_warn "$(translate "Failed to determine timezone from IP address")" timezone="UTC" else msg_ok "$(translate "Found timezone $timezone for IP $this_ip")" fi fi msg_info "$(translate "Enabling automatic time synchronization...")" if timedatectl set-ntp true; then msg_ok "$(translate "Time settings configured - Timezone:") $timezone" register_tool "time_sync" true else msg_error "$(translate "Failed to enable automatic time synchronization")" fi } # ========================================================== skip_apt_languages() { msg_info "$(translate "Configuring APT to skip downloading additional languages...")" local default_locale="" if [ -f /etc/default/locale ]; then default_locale=$(grep '^LANG=' /etc/default/locale | cut -d= -f2 | tr -d '"') elif [ -f /etc/environment ]; then default_locale=$(grep '^LANG=' /etc/environment | cut -d= -f2 | tr -d '"') fi default_locale="${default_locale:-en_US.UTF-8}" local normalized_locale=$(echo "$default_locale" | tr 'A-Z' 'a-z' | sed 's/utf-8/utf8/;s/-/_/') if ! locale -a | grep -qi "^$normalized_locale$"; then if ! grep -qE "^${default_locale}[[:space:]]+UTF-8" /etc/locale.gen; then echo "$default_locale UTF-8" >> /etc/locale.gen fi locale-gen "$default_locale" > /dev/null 2>&1 fi echo 'Acquire::Languages "none";' > /etc/apt/apt.conf.d/99-disable-translations msg_ok "$(translate "APT configured to skip additional languages")" register_tool "apt_languages" true } # ========================================================== optimize_journald() { msg_info "$(translate "Limiting size and optimizing journald...")" NECESSARY_REBOOT=1 cat < /etc/systemd/journald.conf [Journal] Storage=persistent SplitMode=none RateLimitInterval=0 RateLimitIntervalSec=0 RateLimitBurst=0 ForwardToSyslog=no ForwardToWall=yes Seal=no Compress=yes SystemMaxUse=64M RuntimeMaxUse=60M MaxLevelStore=warning MaxLevelSyslog=warning MaxLevelKMsg=warning MaxLevelConsole=notice MaxLevelWall=crit EOF systemctl restart systemd-journald.service > /dev/null 2>&1 journalctl --vacuum-size=64M --vacuum-time=1d > /dev/null 2>&1 journalctl --rotate > /dev/null 2>&1 msg_ok "$(translate "Journald optimized - Max size: 64M")" register_tool "journald" true } # ========================================================== optimize_logrotate() { msg_info "$(translate "Optimizing logrotate configuration...")" local logrotate_conf="/etc/logrotate.conf" local backup_conf="${logrotate_conf}.bak" if ! grep -q "# ProxMenux optimized configuration" "$logrotate_conf"; then cp "$logrotate_conf" "$backup_conf" cat < "$logrotate_conf" # ProxMenux optimized configuration daily su root adm rotate 7 create compress size=10M delaycompress copytruncate include /etc/logrotate.d EOF systemctl restart logrotate > /dev/null 2>&1 fi msg_ok "$(translate "Logrotate optimization completed")" register_tool "logrotate" true } # ========================================================== increase_system_limits() { msg_info "$(translate "Increasing various system limits...")" NECESSARY_REBOOT=1 cat > /etc/sysctl.d/99-maxwatches.conf << EOF # ProxMenux configuration fs.inotify.max_user_watches = 1048576 fs.inotify.max_user_instances = 1048576 fs.inotify.max_queued_events = 1048576 EOF cat > /etc/security/limits.d/99-limits.conf << EOF # ProxMenux configuration * soft nproc 1048576 * hard nproc 1048576 * soft nofile 1048576 * hard nofile 1048576 root soft nproc unlimited root hard nproc unlimited root soft nofile unlimited root hard nofile unlimited EOF cat > /etc/sysctl.d/99-maxkeys.conf << EOF # ProxMenux configuration kernel.keys.root_maxkeys=1000000 kernel.keys.maxkeys=1000000 EOF for file in /etc/systemd/system.conf /etc/systemd/user.conf; do if ! grep -q "^DefaultLimitNOFILE=" "$file"; then echo "DefaultLimitNOFILE=256000" >> "$file" fi done for file in /etc/pam.d/common-session /etc/pam.d/runuser-l; do if ! grep -q "^session required pam_limits.so" "$file"; then echo 'session required pam_limits.so' >> "$file" fi done if ! grep -q "ulimit -n 256000" /root/.profile; then echo "ulimit -n 256000" >> /root/.profile fi cat > /etc/sysctl.d/99-swap.conf << EOF # ProxMenux configuration vm.swappiness = 10 vm.vfs_cache_pressure = 100 EOF cat > /etc/sysctl.d/99-fs.conf << EOF # ProxMenux configuration fs.nr_open = 12000000 fs.file-max = 9223372036854775807 fs.aio-max-nr = 1048576 EOF msg_ok "$(translate "System limits increase completed.")" register_tool "system_limits" true } # ========================================================== configure_entropy() { msg_info "$(translate "Configuring entropy generation to prevent slowdowns...")" /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install haveged > /dev/null 2>&1 cat < /etc/default/haveged # -w sets low entropy watermark (in bits) DAEMON_ARGS="-w 1024" EOF systemctl daemon-reload > /dev/null 2>&1 systemctl enable haveged > /dev/null 2>&1 msg_ok "$(translate "Entropy generation configuration completed")" register_tool "entropy" true } # ========================================================== optimize_memory_settings() { msg_info "$(translate "Optimizing memory settings...")" NECESSARY_REBOOT=1 cat < /etc/sysctl.d/99-memory.conf # Balanced Memory Optimization vm.swappiness = 10 vm.dirty_ratio = 15 vm.dirty_background_ratio = 5 vm.overcommit_memory = 1 vm.max_map_count = 65530 EOF if [ -f /proc/sys/vm/compaction_proactiveness ]; then echo "vm.compaction_proactiveness = 20" >> /etc/sysctl.d/99-memory.conf fi msg_ok "$(translate "Memory optimization completed.")" register_tool "memory_settings" true } # ========================================================== configure_kernel_panic() { msg_info "$(translate "Configuring kernel panic behavior")" NECESSARY_REBOOT=1 cat < /etc/sysctl.d/99-kernelpanic.conf # Enable restart on kernel panic, kernel oops and hardlockup kernel.core_pattern = /var/crash/core.%t.%p kernel.panic = 10 kernel.panic_on_oops = 1 kernel.hardlockup_panic = 1 EOF msg_ok "$(translate "Kernel panic behavior configuration completed")" register_tool "kernel_panic" true } # ========================================================== force_apt_ipv4() { msg_info "$(translate "Configuring APT to use IPv4...")" echo 'Acquire::ForceIPv4 "true";' > /etc/apt/apt.conf.d/99-force-ipv4 msg_ok "$(translate "APT IPv4 configuration completed")" register_tool "apt_ipv4" true } # ========================================================== apply_network_optimizations() { msg_info "$(translate "Optimizing network settings...")" NECESSARY_REBOOT=1 cat < /etc/sysctl.d/99-network.conf net.core.netdev_max_backlog=8192 net.core.optmem_max=8192 net.core.rmem_max=16777216 net.core.somaxconn=8151 net.core.wmem_max=16777216 net.ipv4.conf.all.accept_redirects = 0 net.ipv4.conf.all.accept_source_route = 0 net.ipv4.conf.all.log_martians = 0 net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.all.secure_redirects = 0 net.ipv4.conf.all.send_redirects = 0 net.ipv4.conf.default.accept_redirects = 0 net.ipv4.conf.default.accept_source_route = 0 net.ipv4.conf.default.log_martians = 0 net.ipv4.conf.default.rp_filter = 1 net.ipv4.conf.default.secure_redirects = 0 net.ipv4.conf.default.send_redirects = 0 net.ipv4.icmp_echo_ignore_broadcasts = 1 net.ipv4.icmp_ignore_bogus_error_responses = 1 net.ipv4.ip_local_port_range=1024 65535 net.ipv4.tcp_base_mss = 1024 net.ipv4.tcp_challenge_ack_limit = 999999999 net.ipv4.tcp_fin_timeout=10 net.ipv4.tcp_keepalive_intvl=30 net.ipv4.tcp_keepalive_probes=3 net.ipv4.tcp_keepalive_time=240 net.ipv4.tcp_limit_output_bytes=65536 net.ipv4.tcp_max_syn_backlog=8192 net.ipv4.tcp_max_tw_buckets = 1440000 net.ipv4.tcp_mtu_probing = 1 net.ipv4.tcp_rfc1337=1 net.ipv4.tcp_rmem=8192 87380 16777216 net.ipv4.tcp_sack=1 net.ipv4.tcp_slow_start_after_idle=0 net.ipv4.tcp_syn_retries=3 net.ipv4.tcp_synack_retries = 2 net.ipv4.tcp_tw_recycle = 0 net.ipv4.tcp_tw_reuse = 0 net.ipv4.tcp_wmem=8192 65536 16777216 net.netfilter.nf_conntrack_generic_timeout = 60 net.netfilter.nf_conntrack_helper=0 net.netfilter.nf_conntrack_max = 524288 net.netfilter.nf_conntrack_tcp_timeout_established = 28800 net.unix.max_dgram_qlen = 4096 EOF sysctl --system > /dev/null 2>&1 local interfaces_file="/etc/network/interfaces" if ! grep -q 'source /etc/network/interfaces.d/*' "$interfaces_file"; then echo "source /etc/network/interfaces.d/*" >> "$interfaces_file" fi msg_ok "$(translate "Network optimization completed")" register_tool "network_optimization" true } # ========================================================== disable_rpc() { msg_info "$(translate "Disabling portmapper/rpcbind for security...")" systemctl disable rpcbind > /dev/null 2>&1 systemctl stop rpcbind > /dev/null 2>&1 msg_ok "$(translate "portmapper/rpcbind has been disabled and removed")" register_tool "disable_rpc" true } # ========================================================== customize_bashrc() { msg_info "$(translate "Customizing bashrc for root user...")" local bashrc="/root/.bashrc" local bash_profile="/root/.bash_profile" if [ ! -f "${bashrc}.bak" ]; then cp "$bashrc" "${bashrc}.bak" fi cat >> "$bashrc" << 'EOF' # ProxMenux customizations export HISTTIMEFORMAT="%d/%m/%y %T " export PS1="\[\e[31m\][\[\e[m\]\[\e[38;5;172m\]\u\[\e[m\]@\[\e[38;5;153m\]\h\[\e[m\] \[\e[38;5;214m\]\W\[\e[m\]\[\e[31m\]]\[\e[m\]\\$ " alias l='ls -CF' alias la='ls -A' alias ll='ls -alF' alias ls='ls --color=auto' alias grep='grep --color=auto' alias fgrep='fgrep --color=auto' alias egrep='egrep --color=auto' source /etc/profile.d/bash_completion.sh EOF if ! grep -q "source /root/.bashrc" "$bash_profile"; then echo "source /root/.bashrc" >> "$bash_profile" fi msg_ok "$(translate "Bashrc customization completed")" register_tool "bashrc_custom" true } # ========================================================== install_log2ram_auto() { msg_info "$(translate "Checking if system disk is SSD or M.2...")" ROOT_PART=$(lsblk -no NAME,MOUNTPOINT | grep ' /$' | awk '{print $1}') SYSTEM_DISK=$(lsblk -no PKNAME /dev/$ROOT_PART 2>/dev/null) SYSTEM_DISK=${SYSTEM_DISK:-sda} if [[ "$SYSTEM_DISK" == nvme* || "$(cat /sys/block/$SYSTEM_DISK/queue/rotational 2>/dev/null)" == "0" ]]; then msg_ok "$(translate "System disk ($SYSTEM_DISK) is SSD or M.2. Proceeding with log2ram setup.")" else msg_warn "$(translate "System disk ($SYSTEM_DISK) is not SSD/M.2. Skipping log2ram installation.")" return 0 fi # Clean up previous state rm -rf /tmp/log2ram rm -f /etc/systemd/system/log2ram* rm -f /etc/systemd/system/log2ram-daily.* rm -f /etc/cron.d/log2ram* rm -f /usr/sbin/log2ram rm -f /etc/log2ram.conf rm -f /usr/local/bin/log2ram-check.sh rm -rf /var/log.hdd systemctl daemon-reexec >/dev/null 2>&1 systemctl daemon-reload >/dev/null 2>&1 msg_info "$(translate "Installing log2ram from GitHub...")" git clone https://github.com/azlux/log2ram.git /tmp/log2ram >/dev/null 2>>/tmp/log2ram_install.log cd /tmp/log2ram || return 1 bash install.sh >>/tmp/log2ram_install.log 2>&1 if [[ -f /etc/log2ram.conf ]] && systemctl list-units --all | grep -q log2ram; then msg_ok "$(translate "log2ram installed successfully")" else msg_error "$(translate "Failed to install log2ram. See /tmp/log2ram_install.log")" return 1 fi # Detect RAM RAM_SIZE_GB=$(free -g | awk '/^Mem:/{print $2}') [[ -z "$RAM_SIZE_GB" || "$RAM_SIZE_GB" -eq 0 ]] && RAM_SIZE_GB=4 if (( RAM_SIZE_GB <= 8 )); then LOG2RAM_SIZE="128M" CRON_HOURS=1 elif (( RAM_SIZE_GB <= 16 )); then LOG2RAM_SIZE="256M" CRON_HOURS=3 else LOG2RAM_SIZE="512M" CRON_HOURS=6 fi msg_ok "$(translate "Detected RAM:") $RAM_SIZE_GB GB — $(translate "log2ram size set to:") $LOG2RAM_SIZE" sed -i "s/^SIZE=.*/SIZE=$LOG2RAM_SIZE/" /etc/log2ram.conf rm -f /etc/cron.hourly/log2ram echo "0 */$CRON_HOURS * * * root /usr/sbin/log2ram write" > /etc/cron.d/log2ram msg_ok "$(translate "log2ram write scheduled every") $CRON_HOURS $(translate "hour(s)")" cat << 'EOF' > /usr/local/bin/log2ram-check.sh #!/bin/bash CONF_FILE="/etc/log2ram.conf" LIMIT_KB=$(grep '^SIZE=' "$CONF_FILE" | cut -d'=' -f2 | tr -d 'M')000 USED_KB=$(df /var/log --output=used | tail -1) THRESHOLD=$(( LIMIT_KB * 90 / 100 )) if (( USED_KB > THRESHOLD )); then /usr/sbin/log2ram write fi EOF chmod +x /usr/local/bin/log2ram-check.sh echo "*/5 * * * * root /usr/local/bin/log2ram-check.sh" > /etc/cron.d/log2ram-auto-sync msg_ok "$(translate "Auto-sync enabled when /var/log exceeds 90% of") $LOG2RAM_SIZE" register_tool "log2ram" true } # ========================================================== run_complete_optimization() { clear show_proxmenux_logo msg_title "$(translate "ProxMenux Optimization Post-Installation")" ensure_tools_json apt_upgrade remove_subscription_banner configure_time_sync skip_apt_languages optimize_journald optimize_logrotate increase_system_limits configure_entropy optimize_memory_settings configure_kernel_panic force_apt_ipv4 apply_network_optimizations disable_rpc customize_bashrc install_log2ram_auto echo -e msg_success "$(translate "Complete post-installation optimization finished!")" if [[ "$NECESSARY_REBOOT" -eq 1 ]]; then whiptail --title "Reboot Required" \ --yesno "$(translate "Some changes require a reboot to take effect. Do you want to restart now?")" 10 60 if [[ $? -eq 0 ]]; then msg_info "$(translate "Removing no longer required packages and purging old cached updates...")" apt-get -y autoremove >/dev/null 2>&1 apt-get -y autoclean >/dev/null 2>&1 msg_ok "$(translate "Cleanup finished")" msg_success "$(translate "Press Enter to continue...")" read -r msg_warn "$(translate "Rebooting the system...")" reboot else msg_info "$(translate "Removing no longer required packages and purging old cached updates...")" apt-get -y autoremove >/dev/null 2>&1 apt-get -y autoclean >/dev/null 2>&1 msg_ok "$(translate "Cleanup finished")" msg_info2 "$(translate "You can reboot later manually.")" msg_success "$(translate "Press Enter to continue...")" read -r exit 0 fi fi msg_success "$(translate "All changes applied. No reboot required.")" msg_success "$(translate "Press Enter to return to menu...")" read -r clear } if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then run_complete_optimization fi