This commit is contained in:
apulkin
2026-05-28 11:58:28 +03:00
commit 366ccb7b9a
42 changed files with 8475 additions and 0 deletions
+72
View File
@@ -0,0 +1,72 @@
Регулятор масштабирования ЦП определяет, как регулируется частота ЦП в зависимости от рабочей нагрузки, с целью либо экономии энергии, либо повышения производительности. Изменяя частоту в большую или меньшую сторону, операционная система может оптимизировать использование ЦП и, по возможности, экономить энергию. Типичные регуляторы масштабирования
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/scaling-governor.sh)"
#!/usr/bin/env bash
# Copyright (c) 2021-2026 tteck
# Author: tteck (tteckster)
# License: MIT
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
set -e
# Telemetry
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "scaling-governor" "pve"
header_info() {
clear
cat <<EOF
________ __ __ _____
/ ___/ _ \/ / / / / ___/__ _ _____ _______ ___ _______
/ /__/ ___/ /_/ / / (_ / _ \ |/ / -_) __/ _ \/ _ \/ __(_-<
\___/_/ \____/ \___/\___/___/\__/_/ /_//_/\___/_/ /___/
EOF
}
header_info
whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU Scaling Governors" --yesno "View/Change CPU Scaling Governors. Proceed?" 10 58
current_governor=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor)
GOVERNORS_MENU=()
MSG_MAX_LENGTH=0
while read -r TAG ITEM; do
OFFSET=2
((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET
GOVERNORS_MENU+=("$TAG" "$ITEM " "OFF")
done < <(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors | tr ' ' '\n' | grep -v "$current_governor")
scaling_governor=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Current CPU Scaling Governor is set to $current_governor" --checklist "\nSelect the Scaling Governor to use:\n" 16 $((MSG_MAX_LENGTH + 58)) 6 "${GOVERNORS_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"')
[ -z "$scaling_governor" ] && {
whiptail --backtitle "Proxmox VE Helper Scripts" --title "No CPU Scaling Governor Selected" --msgbox "It appears that no CPU Scaling Governor was selected" 10 68
clear
exit
}
echo "${scaling_governor}" | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor >/dev/null
current_governor=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor)
whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Current CPU Scaling Governor" "\nCurrent CPU Scaling Governor has been set to $current_governor\n" 10 60
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU Scaling Governor" --menu "This will establish a crontab to maintain the CPU Scaling Governor configuration across reboots.\n \nSetup a crontab?" 14 68 2 \
"yes" " " \
"no" " " 3>&2 2>&1 1>&3)
case $CHOICE in
yes)
set +e
NEW_CRONTAB_COMMAND="(sleep 60 && echo \"$current_governor\" | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor)"
EXISTING_CRONTAB=$(crontab -l 2>/dev/null)
if [[ -n "$EXISTING_CRONTAB" ]]; then
TEMP_CRONTAB_FILE=$(mktemp)
echo "$EXISTING_CRONTAB" | grep -v "@reboot (sleep 60 && echo*" >"$TEMP_CRONTAB_FILE"
crontab "$TEMP_CRONTAB_FILE"
rm "$TEMP_CRONTAB_FILE"
fi
(
crontab -l 2>/dev/null
echo "@reboot $NEW_CRONTAB_COMMAND"
) | crontab -
echo -e "\nCrontab Set (use 'crontab -e' to check)"
;;
no)
echo -e "\n\033[31mNOTE: Settings return to default after reboot\033[m\n"
;;
esac
echo -e "Current CPU Scaling Governor is set to \033[36m$current_governor\033[m\n"
+99
View File
@@ -0,0 +1,99 @@
# Этот скрипт помогает пользователям Proxmox выявлять и удалять "осиротевшие" тома LVM,
# которые больше не связаны ни с одной виртуальной машиной или контейнером LXC.
# Он сканирует все тома LVM, обнаруживает неиспользуемые и предоставляет интерактивное приглашение для их безопасного
# удаления. Критически важные для системы тома, такие как корневой раздел, раздел подкачки и раздел
# данных, исключаются, чтобы предотвратить случайное удаление.
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/clean-orphaned-lvm.sh)"
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
function header_info {
clear
cat <<"EOF"
____ ________ ____ __ __ __ _ ____ ___
/ __ \_________ _ ______ ___ ____ _ __ / ____/ /__ ____ _____ / __ \_________ / /_ ____ _____ ___ ____/ / / /| | / / |/ /____
/ /_/ / ___/ __ \| |/_/ __ `__ \/ __ \| |/_/ / / / / _ \/ __ `/ __ \ / / / / ___/ __ \/ __ \/ __ `/ __ \/ _ \/ __ / / / | | / / /|_/ / ___/
/ ____/ / / /_/ /> </ / / / / / /_/ /> < / /___/ / __/ /_/ / / / / / /_/ / / / /_/ / / / / /_/ / / / / __/ /_/ / / /__| |/ / / / (__ )
/_/ /_/ \____/_/|_/_/ /_/ /_/\____/_/|_| \____/_/\___/\__,_/_/ /_/ \____/_/ / .___/_/ /_/\__,_/_/ /_/\___/\__,_/ /_____/___/_/ /_/____/
/_/
EOF
}
# Telemetry
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "clean-orphaned-lvm" "pve"
# Function to check for orphaned LVM volumes
function find_orphaned_lvm {
echo -e "\n🔍 Scanning for orphaned LVM volumes...\n"
orphaned_volumes=()
while read -r lv vg size seg_type; do
# Exclude system-critical LVs and Ceph OSDs
if [[ "$lv" == "data" || "$lv" == "root" || "$lv" == "swap" || "$lv" =~ ^osd-block- ]]; then
continue
fi
# Exclude thin pools (any name)
if [[ "$seg_type" == "thin-pool" ]]; then
continue
fi
container_id=$(echo "$lv" | grep -oE "[0-9]+" | head -1)
# Check if the ID exists as a VM or LXC container on any cluster node
if compgen -G "/etc/pve/nodes/*/lxc/${container_id}.conf" >/dev/null 2>&1 ||
compgen -G "/etc/pve/nodes/*/qemu-server/${container_id}.conf" >/dev/null 2>&1; then
continue
fi
orphaned_volumes+=("$lv" "$vg" "$size")
done < <(lvs --noheadings -o lv_name,vg_name,lv_size,seg_type --separator ' ' 2>/dev/null | awk '{print $1, $2, $3, $4}')
# Display orphaned volumes
echo -e "❗ The following orphaned LVM volumes were found:\n"
printf "%-25s %-10s %-10s\n" "LV Name" "VG" "Size"
printf "%-25s %-10s %-10s\n" "-------------------------" "----------" "----------"
for ((i = 0; i < ${#orphaned_volumes[@]}; i += 3)); do
printf "%-25s %-10s %-10s\n" "${orphaned_volumes[i]}" "${orphaned_volumes[i + 1]}" "${orphaned_volumes[i + 2]}"
done
echo ""
}
# Function to delete selected volumes
function delete_orphaned_lvm {
for ((i = 0; i < ${#orphaned_volumes[@]}; i += 3)); do
lv="${orphaned_volumes[i]}"
vg="${orphaned_volumes[i + 1]}"
size="${orphaned_volumes[i + 2]}"
read -p "❓ Do you want to delete $lv (VG: $vg, Size: $size)? [y/N]: " confirm
if [[ "$confirm" =~ ^[Yy]$ ]]; then
echo -e "🗑️ Deleting $lv from $vg..."
lvremove -f "$vg/$lv"
if [ $? -eq 0 ]; then
echo -e "✅ Successfully deleted $lv.\n"
else
echo -e "❌ Failed to delete $lv.\n"
fi
else
echo -e "⚠️ Skipping $lv.\n"
fi
done
}
# Run script
header_info
find_orphaned_lvm
delete_orphaned_lvm
echo -e "✅ Cleanup process completed!\n"
+355
View File
@@ -0,0 +1,355 @@
# Этот скрипт добавит/удалит расписание crontab, которое обновляет операционную систему всех LXC каждое воскресенье в полночь.
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/cron-update-lxcs.sh)"
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
#
# This script manages a local cron job for automatic LXC container OS updates.
# The update script is downloaded once, displayed for review, and installed
# locally. Cron runs the local copy — no remote code execution at runtime.
#
# bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/cron-update-lxcs.sh)"
set -euo pipefail
REPO_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main"
SCRIPT_URL="${REPO_URL}/tools/pve/update-lxcs-cron.sh"
LOCAL_SCRIPT="/usr/local/bin/update-lxcs.sh"
CONF_FILE="/etc/update-lxcs.conf"
LOG_FILE="/var/log/update-lxcs-cron.log"
CRON_ENTRY="0 0 * * 0 ${LOCAL_SCRIPT} >>${LOG_FILE} 2>&1"
clear
cat <<"EOF"
______ __ __ __ __ __ _ ________
/ ____/________ ____ / / / /___ ____/ /___ _/ /____ / / | |/ / ____/____
/ / / ___/ __ \/ __ \ / / / / __ \/ __ / __ `/ __/ _ \ / / | / / / ___/
/ /___/ / / /_/ / / / / / /_/ / /_/ / /_/ / /_/ / /_/ __/ / /___/ / /___(__ )
\____/_/ \____/_/ /_/ \____/ .___/\__,_/\__,_/\__/\___/ /_____/_/|_\____/____/
/_/
EOF
info() { echo -e "\n \e[36m[Info]\e[0m $1"; }
ok() { echo -e " \e[32m[OK]\e[0m $1"; }
err() { echo -e " \e[31m[Error]\e[0m $1" >&2; }
confirm() {
local prompt="${1:-Proceed?}"
while true; do
read -rp " ${prompt} (y/n): " yn
case $yn in
[Yy]*) return 0 ;;
[Nn]*) return 1 ;;
*) echo " Please answer yes or no." ;;
esac
done
}
download_script() {
local tmp
tmp=$(mktemp)
if ! curl -fsSL -o "$tmp" "$SCRIPT_URL"; then
err "Failed to download script from:\n ${SCRIPT_URL}"
rm -f "$tmp"
return 1
fi
echo "$tmp"
}
review_script() {
local file="$1"
local hash
hash=$(sha256sum "$file" | awk '{print $1}')
echo ""
echo -e " \e[1;33m─── Script Content ───────────────────────────────────────────\e[0m"
cat "$file"
echo -e " \e[1;33m──────────────────────────────────────────────────────────────\e[0m"
echo -e " \e[36mSHA256:\e[0m ${hash}"
echo -e " \e[36mSource:\e[0m ${SCRIPT_URL}"
echo ""
}
remove_legacy_cron() {
if crontab -l -u root 2>/dev/null | grep -q "update-lxcs-cron.sh"; then
(crontab -l -u root 2>/dev/null | grep -v "update-lxcs-cron.sh") | crontab -u root -
ok "Removed legacy curl-based cron entry"
fi
}
add() {
info "Downloading update script..."
local tmp
tmp=$(download_script) || exit 1
local hash
hash=$(sha256sum "$tmp" | awk '{print $1}')
echo ""
echo -e " \e[1;33m─── Installation Summary ─────────────────────────────────────\e[0m"
echo -e " \e[36mSource:\e[0m ${SCRIPT_URL}"
echo -e " \e[36mSHA256:\e[0m ${hash}"
echo -e " \e[36mInstall to:\e[0m ${LOCAL_SCRIPT}"
echo -e " \e[36mConfig:\e[0m ${CONF_FILE}"
echo -e " \e[36mLog file:\e[0m ${LOG_FILE}"
echo -e " \e[36mCron schedule:\e[0m Every Sunday at midnight (0 0 * * 0)"
echo -e " \e[1;33m──────────────────────────────────────────────────────────────\e[0m"
echo ""
if confirm "Review script content before installing?"; then
review_script "$tmp"
fi
if ! confirm "Install this script and activate cron schedule?"; then
rm -f "$tmp"
echo " Aborted."
exit 0
fi
remove_legacy_cron
install -m 0755 "$tmp" "$LOCAL_SCRIPT"
rm -f "$tmp"
ok "Installed script to ${LOCAL_SCRIPT}"
if [[ ! -f "$CONF_FILE" ]]; then
cat >"$CONF_FILE" <<'CONF'
# Configuration for automatic LXC container OS updates.
# Add container IDs to exclude from updates (comma-separated):
# EXCLUDE=100,101,102
EXCLUDE=
CONF
ok "Created config ${CONF_FILE}"
fi
(
crontab -l -u root 2>/dev/null | grep -v "${LOCAL_SCRIPT}" || true
echo "${CRON_ENTRY}"
) | crontab -u root -
ok "Added cron schedule: Every Sunday at midnight"
echo ""
echo -e " \e[36mLocal script:\e[0m ${LOCAL_SCRIPT}"
echo -e " \e[36mConfig:\e[0m ${CONF_FILE}"
echo -e " \e[36mLog file:\e[0m ${LOG_FILE}"
echo ""
}
remove() {
if crontab -l -u root 2>/dev/null | grep -q "${LOCAL_SCRIPT}"; then
(crontab -l -u root 2>/dev/null | grep -v "${LOCAL_SCRIPT}") | crontab -u root -
ok "Removed cron schedule"
fi
remove_legacy_cron
[[ -f "$LOCAL_SCRIPT" ]] && rm -f "$LOCAL_SCRIPT" && ok "Removed ${LOCAL_SCRIPT}"
[[ -f "$LOG_FILE" ]] && rm -f "$LOG_FILE" && ok "Removed ${LOG_FILE}"
echo -e "\n Cron Update LXCs has been fully removed."
echo -e " \e[90mNote: ${CONF_FILE} was kept (remove manually if desired).\e[0m"
}
update_script() {
if [[ ! -f "$LOCAL_SCRIPT" ]]; then
err "No local script found at ${LOCAL_SCRIPT}. Use 'Add' first."
exit 1
fi
info "Downloading latest version..."
local tmp
tmp=$(download_script) || exit 1
if command -v diff &>/dev/null; then
local changes
changes=$(diff --color=auto "$LOCAL_SCRIPT" "$tmp" 2>/dev/null || true)
if [[ -z "$changes" ]]; then
ok "Script is already up-to-date (no changes)."
rm -f "$tmp"
return
fi
echo ""
echo -e " \e[1;33m─── Changes ──────────────────────────────────────────────────\e[0m"
echo "$changes"
echo -e " \e[1;33m──────────────────────────────────────────────────────────────\e[0m"
else
review_script "$tmp"
fi
local new_hash old_hash
new_hash=$(sha256sum "$tmp" | awk '{print $1}')
old_hash=$(sha256sum "$LOCAL_SCRIPT" | awk '{print $1}')
echo -e " \e[36mCurrent SHA256:\e[0m ${old_hash}"
echo -e " \e[36mNew SHA256:\e[0m ${new_hash}"
echo ""
if ! confirm "Apply update?"; then
rm -f "$tmp"
echo " Aborted."
return
fi
install -m 0755 "$tmp" "$LOCAL_SCRIPT"
rm -f "$tmp"
ok "Updated ${LOCAL_SCRIPT}"
}
view_script() {
if [[ ! -f "$LOCAL_SCRIPT" ]]; then
err "No local script found at ${LOCAL_SCRIPT}. Use 'Add' first."
exit 1
fi
local view_choice
view_choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "View Script" --menu "What do you want to view?" 12 60 3 \
"Worker" "Installed update script (${LOCAL_SCRIPT##*/})" \
"Cron" "Cron schedule & configuration" \
"Both" "Show everything" \
3>&1 1>&2 2>&3) || return 0
case "$view_choice" in
"Worker") view_worker_script ;;
"Cron") view_cron_config ;;
"Both") view_cron_config && echo "" && view_worker_script ;;
esac
}
view_worker_script() {
local hash
hash=$(sha256sum "$LOCAL_SCRIPT" | awk '{print $1}')
echo ""
echo -e " \e[1;33m─── ${LOCAL_SCRIPT} ───\e[0m"
cat "$LOCAL_SCRIPT"
echo -e " \e[1;33m──────────────────────────────────────────────────────────────\e[0m"
echo -e " \e[36mSHA256:\e[0m ${hash}"
echo -e " \e[36mInstalled:\e[0m $(stat -c '%y' "$LOCAL_SCRIPT" 2>/dev/null | cut -d. -f1)"
echo ""
}
view_cron_config() {
echo ""
echo -e " \e[1;33m─── Cron Configuration ───────────────────────────────────────\e[0m"
if crontab -l -u root 2>/dev/null | grep -q "${LOCAL_SCRIPT}"; then
local entry
entry=$(crontab -l -u root 2>/dev/null | grep "${LOCAL_SCRIPT}")
echo -e " \e[36mCron entry:\e[0m ${entry}"
local schedule
schedule=$(echo "$entry" | awk '{print $1,$2,$3,$4,$5}')
echo -e " \e[36mSchedule:\e[0m ${schedule} ($(cron_to_human "$schedule"))"
else
echo -e " \e[31mCron:\e[0m Not configured"
fi
if [[ -f "$CONF_FILE" ]]; then
echo -e " \e[36mConfig file:\e[0m ${CONF_FILE}"
local excludes
excludes=$(grep -oP '^\s*EXCLUDE\s*=\s*\K.*' "$CONF_FILE" 2>/dev/null || true)
echo -e " \e[36mExcluded:\e[0m ${excludes:-(none)}"
echo ""
echo -e " \e[90m--- ${CONF_FILE} ---\e[0m"
cat "$CONF_FILE"
else
echo -e " \e[36mConfig file:\e[0m (not created yet)"
fi
if [[ -f "$LOG_FILE" ]]; then
local log_size
log_size=$(du -h "$LOG_FILE" | awk '{print $1}')
echo -e " \e[36mLog file:\e[0m ${LOG_FILE} (${log_size})"
fi
echo -e " \e[1;33m──────────────────────────────────────────────────────────────\e[0m"
echo ""
}
cron_to_human() {
local schedule="$1"
case "$schedule" in
"0 0 * * 0") echo "Every Sunday at midnight" ;;
"0 0 * * *") echo "Daily at midnight" ;;
"0 * * * *") echo "Every hour" ;;
*) echo "Custom schedule" ;;
esac
}
show_status() {
echo ""
if [[ -f "$LOCAL_SCRIPT" ]]; then
local hash
hash=$(sha256sum "$LOCAL_SCRIPT" | awk '{print $1}')
ok "Script installed: ${LOCAL_SCRIPT}"
echo -e " \e[36mSHA256:\e[0m ${hash}"
echo -e " \e[36mInstalled:\e[0m $(stat -c '%y' "$LOCAL_SCRIPT" 2>/dev/null | cut -d. -f1)"
else
err "Script not installed"
fi
if crontab -l -u root 2>/dev/null | grep -q "${LOCAL_SCRIPT}"; then
local schedule
schedule=$(crontab -l -u root 2>/dev/null | grep "${LOCAL_SCRIPT}" | awk '{print $1,$2,$3,$4,$5}')
ok "Cron active: ${schedule}"
else
err "Cron not configured"
fi
if [[ -f "$CONF_FILE" ]]; then
local excludes
excludes=$(grep -oP '^\s*EXCLUDE\s*=\s*\K.*' "$CONF_FILE" 2>/dev/null || echo "(none)")
echo -e " \e[36mExcluded:\e[0m ${excludes:-"(none)"}"
fi
if [[ -f "$LOG_FILE" ]]; then
local log_size last_run
log_size=$(du -h "$LOG_FILE" | awk '{print $1}')
last_run=$(grep -oP '^\s+\K\w.*' "$LOG_FILE" | tail -1)
echo -e " \e[36mLog file:\e[0m ${LOG_FILE} (${log_size})"
[[ -n "${last_run:-}" ]] && echo -e " \e[36mLast run:\e[0m ${last_run}"
else
echo -e " \e[36mLog file:\e[0m (no runs yet)"
fi
echo ""
}
run_now() {
if [[ ! -f "$LOCAL_SCRIPT" ]]; then
err "No local script found at ${LOCAL_SCRIPT}. Use 'Add' first."
exit 1
fi
info "Running update script now..."
bash "$LOCAL_SCRIPT" | tee -a "$LOG_FILE"
ok "Run completed. Log appended to ${LOG_FILE}"
}
rotate_log() {
if [[ ! -f "$LOG_FILE" ]]; then
info "No log file to rotate."
return
fi
local log_size
log_size=$(stat -c '%s' "$LOG_FILE" 2>/dev/null || echo 0)
local log_size_h
log_size_h=$(du -h "$LOG_FILE" | awk '{print $1}')
if confirm "Rotate log file? (current size: ${log_size_h})"; then
mv "$LOG_FILE" "${LOG_FILE}.old"
ok "Rotated: ${LOG_FILE}${LOG_FILE}.old"
fi
}
OPTIONS=(
Add "Download, review & install cron schedule"
Remove "Remove cron schedule & local script"
Update "Update local script from repository"
Status "Show installation status & last run"
Run "Run update script now (manual trigger)"
View "View cron config & installed script"
Rotate "Rotate log file"
)
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Cron Update LXCs" --menu "Select an option:" 16 68 7 \
"${OPTIONS[@]}" 3>&1 1>&2 2>&3) || exit 0
case $CHOICE in
"Add") add ;;
"Remove") remove ;;
"Update") update_script ;;
"Status") show_status ;;
"Run") run_now ;;
"View") view_script ;;
"Rotate") rotate_log ;;
esac
+93
View File
@@ -0,0 +1,93 @@
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/host-backup.sh)"
#!/usr/bin/env bash
# Copyright (c) 2021-2026 tteck
# Author: tteck (tteckster)
# License: MIT
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
function header_info {
clear
cat <<"EOF"
__ __ __ ___ __
/ // /__ ___ / /_ / _ )___ _____/ /____ _____
/ _ / _ \(_-</ __/ / _ / _ `/ __/ '_/ // / _ \
/_//_/\___/___/\__/ /____/\_,_/\__/_/\_\\_,_/ .__/
/_/
EOF
}
# Telemetry
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "host-backup" "pve"
# Function to perform backup
function perform_backup {
local BACKUP_PATH
local DIR
local DIR_DASH
local BACKUP_FILE
local selected_directories=()
# Get backup path from user
BACKUP_PATH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "\nDefaults to /root/\ne.g. /mnt/backups/" 11 68 --title "Directory to backup to:" 3>&1 1>&2 2>&3) || return
# Default to /root/ if no input
BACKUP_PATH="${BACKUP_PATH:-/root/}"
# Get directory to work in from user
DIR=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "\nDefaults to /etc/\ne.g. /root/, /var/lib/pve-cluster/ etc." 11 68 --title "Directory to work in:" 3>&1 1>&2 2>&3) || return
# Default to /etc/ if no input
DIR="${DIR:-/etc/}"
DIR_DASH=$(echo "$DIR" | tr '/' '-')
BACKUP_FILE="$(hostname)${DIR_DASH}backup"
# Build a list of directories for backup
local CTID_MENU=()
CTID_MENU=("ALL" "Backup all folders" "OFF")
while read -r dir; do
CTID_MENU+=("$(basename "$dir")" "$dir " "OFF")
done < <(ls -d "${DIR}"*)
# Allow the user to select directories
local HOST_BACKUP
while [ -z "${HOST_BACKUP:+x}" ]; do
HOST_BACKUP=$(whiptail --backtitle "Proxmox VE Host Backup" --title "Working in the ${DIR} directory " --checklist \
"\nSelect what files/directories to backup:\n" 16 $(((${#DIRNAME} + 2) + 88)) 6 "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) || return
for selected_dir in ${HOST_BACKUP//\"/}; do
if [[ "$selected_dir" == "ALL" ]]; then
# if ALL was chosen, secure all folders
selected_directories=("${DIR}"*/)
break
else
selected_directories+=("${DIR}$selected_dir")
fi
done
done
# Perform the backup
header_info
echo -e "This will create a backup in\e[1;33m $BACKUP_PATH \e[0mfor these files and directories\e[1;33m ${selected_directories[*]} \e[0m"
read -p "Press ENTER to continue..."
header_info
echo "Working..."
tar -czf "$BACKUP_PATH$BACKUP_FILE-$(date +%Y_%m_%dT%H_%M).tar.gz" --absolute-names "${selected_directories[@]}"
header_info
echo -e "\nFinished"
echo -e "\e[1;33m \nA backup is rendered ineffective when it remains stored on the host.\n \e[0m"
sleep 2
}
# Main script execution loop
while true; do
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE Host Backup" --yesno "This will create backups for particular files and directories located within a designated directory. Proceed?" 10 88); then
perform_backup
else
break
fi
done
+122
View File
@@ -0,0 +1,122 @@
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/kernel-clean.sh)"
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk
# License: MIT
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
function header_info {
clear
cat <<"EOF"
__ __ __ ________
/ //_/__ _________ ___ / / / ____/ /__ ____ _____
/ ,< / _ \/ ___/ __ \/ _ \/ / / / / / _ \/ __ `/ __ \
/ /| / __/ / / / / / __/ / / /___/ / __/ /_/ / / / /
/_/ |_\___/_/ /_/ /_/\___/_/ \____/_/\___/\__,_/_/ /_/
EOF
}
# Color variables
YW="\033[33m"
GN="\033[1;92m"
RD="\033[01;31m"
CL="\033[m"
# Telemetry
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "kernel-clean" "pve"
# Detect current kernel
current_kernel=$(uname -r)
# Only list fully-installed (ii) versioned kernel packages; the pattern
# proxmox-kernel-X.Y.Z matches versioned kernels while excluding the
# two-segment meta-packages (proxmox-kernel-X.Y) and proxmox-kernel-helper.
available_kernels=$(dpkg --list |
awk '/^ii/ {print $2}' |
grep -E '^proxmox-kernel-[0-9]+\.[0-9]+\.[0-9]' |
grep -v "$current_kernel" |
sort -V)
header_info
if [ -z "$available_kernels" ]; then
echo -e "${GN}No old kernels detected. Current kernel: ${current_kernel}${CL}"
exit 0
fi
echo -e "${GN}Currently running kernel: ${current_kernel}${CL}"
echo -e "${YW}Available kernels for removal:${CL}"
echo "$available_kernels" | nl -w 2 -s '. '
echo -e "\n${YW}Select kernels to remove (e.g. 1,3 or 1-5 or 1-3,7):${CL}"
read -r selected
# Parse selection: supports single indices, ranges (e.g., 1-5), and combinations (e.g., 1,3-5,7)
selected_indices=()
IFS=',' read -r -a tokens <<<"$selected"
for token in "${tokens[@]}"; do
if [[ "$token" =~ ^([0-9]+)-([0-9]+)$ ]]; then
for ((i = BASH_REMATCH[1]; i <= BASH_REMATCH[2]; i++)); do
selected_indices+=("$i")
done
else
selected_indices+=("$token")
fi
done
kernels_to_remove=()
for index in "${selected_indices[@]}"; do
kernel=$(echo "$available_kernels" | sed -n "${index}p")
if [ -n "$kernel" ]; then
kernels_to_remove+=("$kernel")
fi
done
if [ ${#kernels_to_remove[@]} -eq 0 ]; then
echo -e "${RD}No valid selection made. Exiting.${CL}"
exit 0
fi
# Confirm removal
echo -e "${YW}Kernels to be removed:${CL}"
printf "%s\n" "${kernels_to_remove[@]}"
read -rp "Proceed with removal? (y/n): " confirm
if [[ "$confirm" != "y" ]]; then
echo -e "${RD}Aborted.${CL}"
exit 0
fi
# Remove kernels
for kernel in "${kernels_to_remove[@]}"; do
echo -e "${YW}Removing $kernel...${CL}"
# Derive the major.minor meta-package name (e.g. proxmox-kernel-6.14)
# from a versioned name like proxmox-kernel-6.14.11-9-pve-signed.
minor_version=$(echo "$kernel" | sed -E 's/^proxmox-kernel-([0-9]+\.[0-9]+)\..*/\1/')
meta="proxmox-kernel-${minor_version}"
pkgs_to_remove=("$kernel")
# Include the meta-package in the purge when it is installed and when
# no other versioned kernel of the same minor version will remain
# (the running kernel keeps it alive if it shares the same minor).
if dpkg -l "$meta" 2>/dev/null | grep -q '^ii'; then
remaining=$(dpkg --list |
awk '/^ii/ {print $2}' |
grep -E "^proxmox-kernel-${minor_version}\." |
grep -v "^${kernel}$" |
wc -l)
if [ "$remaining" -eq 0 ]; then
pkgs_to_remove+=("$meta")
fi
fi
if apt-get purge -y "${pkgs_to_remove[@]}" >/dev/null 2>&1; then
echo -e "${GN}Successfully removed: ${pkgs_to_remove[*]}${CL}"
else
echo -e "${RD}Failed to remove: ${pkgs_to_remove[*]}. Check dependencies.${CL}"
fi
done
# Clean up and update GRUB
echo -e "${YW}Cleaning up...${CL}"
apt-get autoremove -y >/dev/null 2>&1 && update-grub >/dev/null 2>&1
echo -e "${GN}Cleanup and GRUB update complete.${CL}"
+680
View File
@@ -0,0 +1,680 @@
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/update-apps.sh)"
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: BvdBerg01 | Co-Author: remz1337
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/refs/heads/main/misc/core.func)
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "update-apps" "pve"
# =============================================================================
# CONFIGURATION VARIABLES
# Set these variables to skip interactive prompts (Whiptail dialogs)
# =============================================================================
# var_backup: Enable/disable backup before update
# Options: "yes" | "no" | "" (empty = interactive prompt)
var_backup="${var_backup:-}"
# var_backup_storage: Storage location for backups (only used if var_backup=yes)
# Options: Storage name from /etc/pve/storage.cfg (e.g., "local", "nas-backup")
# Leave empty for interactive selection
var_backup_storage="${var_backup_storage:-}"
# var_container: Which containers to update
# Options:
# - "all" : All containers with community-scripts tags
# - "all_running" : Only running containers with community-scripts tags
# - "all_stopped" : Only stopped containers with community-scripts tags
# - "101,102,109" : Comma-separated list of specific container IDs
# - "" : Interactive selection via Whiptail
var_container="${var_container:-}"
# var_unattended: Run updates without user interaction inside containers
# Options: "yes" | "no" | "" (empty = interactive prompt)
var_unattended="${var_unattended:-}"
# var_skip_confirm: Skip initial confirmation dialog
# Options: "yes" | "no" (default: no)
var_skip_confirm="${var_skip_confirm:-no}"
# var_auto_reboot: Automatically reboot containers that require it after update
# Options: "yes" | "no" | "" (empty = interactive prompt)
var_auto_reboot="${var_auto_reboot:-}"
# var_continue_on_error: Continue updating remaining containers if one update fails
# Options: "yes" | "no" (default: no = stop on first error)
# Note: containers with backups always attempt restore on failure regardless of this setting
var_continue_on_error="${var_continue_on_error:-no}"
# var_dry_run: Check for available updates without applying them
# Options: "yes" | "no" (default: no)
# Output: lists each container with current vs. latest version
# Note: requires the container to be running; does not modify any container
var_dry_run="${var_dry_run:-no}"
# var_tags: Optionally override the tags used for auto-detection
# Options: "community-script|proxmox-helper-scripts" (default)
var_tags="${var_tags:-community-script|proxmox-helper-scripts}"
# =============================================================================
# JSON CONFIG EXPORT
# Run with --export-config to output current configuration as JSON
# =============================================================================
function export_config_json() {
cat <<EOF
{
"var_backup": "${var_backup}",
"var_backup_storage": "${var_backup_storage}",
"var_container": "${var_container}",
"var_unattended": "${var_unattended}",
"var_skip_confirm": "${var_skip_confirm}",
"var_auto_reboot": "${var_auto_reboot}",
"var_continue_on_error": "${var_continue_on_error}",
"var_dry_run": "${var_dry_run}",
"var_tags": "${var_tags}"
}
EOF
}
function print_usage() {
cat <<EOF
Usage: $(basename "$0") [OPTIONS]
Update LXC containers created with community-scripts.
Options:
--help Show this help message
--export-config Export current configuration as JSON
Environment Variables:
var_backup Enable backup before update (yes/no)
var_backup_storage Storage location for backups
var_container Container selection (all/all_running/all_stopped/101,102,...)
var_unattended Run updates unattended (yes/no)
var_skip_confirm Skip initial confirmation (yes/no)
var_auto_reboot Auto-reboot containers if required (yes/no)
var_continue_on_error Continue to next container on update failure (yes/no)
var_dry_run Check for updates without applying them (yes/no)
var_tags Optionally override auto-detection tags ("prod|smb|community-script")
Examples:
# Run interactively
$(basename "$0")
# Update all running containers unattended with backup
var_backup=yes var_backup_storage=local var_container=all_running var_unattended=yes var_skip_confirm=yes $(basename "$0")
# Update specific containers without backup
var_backup=no var_container=101,102,105 var_unattended=yes var_skip_confirm=yes $(basename "$0")
# Unattended cron-style: skip confirm, continue on error, no backup
var_backup=no var_container=all_running var_unattended=yes var_skip_confirm=yes var_continue_on_error=yes $(basename "$0")
# Dry-run: show available updates for all running containers without applying
var_container=all_running var_skip_confirm=yes var_dry_run=yes $(basename "$0")
# Export current configuration
$(basename "$0") --export-config
EOF
}
# Handle command line arguments
case "${1:-}" in
--help | -h)
print_usage
exit 0
;;
--export-config)
export_config_json
exit 0
;;
esac
# =============================================================================
function header_info {
clear
cat <<"EOF"
__ _ ________ __ __ __ __
/ / | |/ / ____/ / / / /___ ____/ /___ _/ /____
/ / | / / / / / / __ \/ __ / __ `/ __/ _ \
/ /___/ / /___ / /_/ / /_/ / /_/ / /_/ / /_/ __/
/_____/_/|_\____/ \____/ .___/\__,_/\__,_/\__/\___/
/_/
EOF
}
function detect_service() {
pushd $(mktemp -d) >/dev/null
pct pull "$1" /usr/bin/update update 2>/dev/null
service=$(cat update | sed 's|.*/ct/||g' | sed 's|\.sh).*||g')
popd >/dev/null
}
function dry_run_container() {
local container="$1"
local service="$2"
# Extract app name and source repo directly from check_for_gh_release call in the ct script
# Pattern: check_for_gh_release "appname" "owner/repo"
local check_line app_name app_lc source_repo
check_line=$(echo "$script" | grep -m1 'check_for_gh_release')
if [[ -z "$check_line" ]]; then
echo -e "${YW}[DRY-RUN]${CL} Container $container ($service): no check_for_gh_release found — skipping"
DRY_RUN_RESULT="no check_for_gh_release found — skipping"
return
fi
app_name=$(echo "$check_line" | cut -d'"' -f2)
source_repo=$(echo "$check_line" | cut -d'"' -f4)
app_lc=$(echo "${app_name,,}" | tr -d ' ')
if [[ -z "$source_repo" || "$source_repo" != *"/"* ]]; then
echo -e "${YW}[DRY-RUN]${CL} Container $container ($service): cannot parse source repo — skipping"
DRY_RUN_RESULT="cannot parse source repo — skipping"
return
fi
# Read installed version from container (stored by check_for_gh_release as ~/.<appname>)
local current_version
current_version=$(pct exec "$container" -- bash -c "cat \$HOME/.${app_lc} 2>/dev/null" 2>/dev/null || true)
current_version="${current_version#v}"
# Query latest release from GitHub API
local latest_version
latest_version=$(curl -sSL --max-time 10 \
-H 'Accept: application/vnd.github+json' \
-H 'X-GitHub-Api-Version: 2022-11-28' \
"https://api.github.com/repos/${source_repo}/releases/latest" 2>/dev/null |
grep '"tag_name"' | head -1 | cut -d'"' -f4 | sed 's/^v//')
if [[ -z "$latest_version" ]]; then
echo -e "${YW}[DRY-RUN]${CL} Container $container ($service): cannot fetch latest version from $source_repo"
DRY_RUN_RESULT="cannot fetch latest version from $source_repo"
return
fi
if [[ -z "$current_version" ]]; then
echo -e "${BL}[DRY-RUN]${CL} Container $container ($service): installed version unknown, latest: ${latest_version} (${source_repo})"
DRY_RUN_RESULT="version unknown — latest: ${latest_version}"
elif [[ "$current_version" == "$latest_version" ]]; then
echo -e "${GN}[DRY-RUN]${CL} Container $container ($service): up to date (${current_version})"
DRY_RUN_RESULT="up to date (${current_version})"
else
echo -e "${YW}[DRY-RUN]${CL} Container $container ($service): update available ${current_version}${latest_version}"
DRY_RUN_RESULT="update available ${current_version}${latest_version}"
fi
}
function backup_container() {
msg_info "Creating backup for container $1"
vzdump $1 --compress zstd --storage $STORAGE_CHOICE -notes-template "{{guestname}} - community-scripts backup updater" >/dev/null 2>&1
status=$?
if [ $status -eq 0 ]; then
msg_ok "Backup created"
else
msg_error "Backup failed for container $1"
exit 235
fi
}
function get_backup_storages() {
STORAGES=$(awk '
/^[a-z]+:/ {
if (name != "") {
if (has_backup || (!has_content && type == "dir")) print name
}
split($0, a, ":")
type = a[1]
name = a[2]
gsub(/^[ \t]+|[ \t]+$/, "", name)
has_content = 0
has_backup = 0
}
/^[ \t]*content/ {
has_content = 1
if ($0 ~ /backup/) has_backup = 1
}
END {
if (name != "") {
if (has_backup || (!has_content && type == "dir")) print name
}
}
' /etc/pve/storage.cfg)
}
# Structured result tracking for the final summary report
# Each entry: "CTID|service|STATUS|details"
declare -a UPDATE_RESULTS=()
function log_result() {
# log_result <ctid> <service> <STATUS> <details>
UPDATE_RESULTS+=("${1}|${2}|${3}|${4}")
}
header_info
# =============================================================================
# LOGGING SETUP
# Key events are written directly to a timestamped log file under
# /usr/local/community-scripts/update_apps/ — this avoids any stdout
# redirection that would break interactive spinners or whiptail dialogs.
# The full summary table is appended at the end of the run.
# =============================================================================
LOG_DIR="/usr/local/community-scripts/update_apps"
mkdir -p "$LOG_DIR"
LOG_FILE="${LOG_DIR}/$(date '+%Y%m%d_%H%M%S').log"
echo "Update started: $(date '+%Y-%m-%d %H:%M:%S')" >"$LOG_FILE"
function log_write() {
echo "[$(date '+%H:%M:%S')] $*" >>"$LOG_FILE"
}
# Skip confirmation if var_skip_confirm is set to yes
if [[ "$var_skip_confirm" != "yes" ]]; then
whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC App Update" --yesno "This will update apps in LXCs installed by Helper-Scripts. Proceed?" 10 58 || exit
fi
tags_formatted="${var_tags//|/, }"
msg_info "Loading all possible LXC containers from Proxmox VE with tags: ${tags_formatted}. This may take a few seconds..."
NODE=$(hostname)
containers=$(pct list | tail -n +2 | awk '{print $0 " " $4}')
if [ -z "$containers" ]; then
whiptail --title "LXC Container Update" --msgbox "No LXC containers available!" 10 60
exit 234
fi
menu_items=()
FORMAT="%-10s %-15s %-10s"
TAGS="${var_tags:-community-script|proxmox-helper-scripts}"
while read -r container; do
container_id=$(echo $container | awk '{print $1}')
container_name=$(echo $container | awk '{print $2}')
container_status=$(echo $container | awk '{print $3}')
formatted_line=$(printf "$FORMAT" "$container_name" "$container_status")
if pct config "$container_id" | grep -qE "[^-][; ](${TAGS}).*"; then
menu_items+=("$container_id" "$formatted_line" "OFF")
fi
done <<<"$containers"
msg_ok "Loaded $((${#menu_items[@]} / 3)) containers"
# Determine container selection based on var_container
if [[ -n "$var_container" ]]; then
case "$var_container" in
all)
# Select all containers with matching tags
CHOICE=""
for ((i = 0; i < ${#menu_items[@]}; i += 3)); do
CHOICE="$CHOICE ${menu_items[$i]}"
done
CHOICE=$(echo "$CHOICE" | xargs)
;;
all_running)
# Select only running containers with matching tags
CHOICE=""
for ((i = 0; i < ${#menu_items[@]}; i += 3)); do
cid="${menu_items[$i]}"
if pct status "$cid" 2>/dev/null | grep -q "running"; then
CHOICE="$CHOICE $cid"
fi
done
CHOICE=$(echo "$CHOICE" | xargs)
;;
all_stopped)
# Select only stopped containers with matching tags
CHOICE=""
for ((i = 0; i < ${#menu_items[@]}; i += 3)); do
cid="${menu_items[$i]}"
if pct status "$cid" 2>/dev/null | grep -q "stopped"; then
CHOICE="$CHOICE $cid"
fi
done
CHOICE=$(echo "$CHOICE" | xargs)
;;
*)
# Assume comma-separated list of container IDs
CHOICE=$(echo "$var_container" | tr ',' ' ')
;;
esac
if [[ -z "$CHOICE" ]]; then
msg_error "No containers matched the selection criteria: $var_container ${var_tags:-community-script|proxmox-helper-scripts}"
exit 234
fi
msg_ok "Selected containers: $CHOICE"
else
CHOICE=$(whiptail --title "LXC Container Update" \
--checklist "Select LXC containers to update:" 25 60 13 \
"${menu_items[@]}" 3>&2 2>&1 1>&3 | tr -d '"')
if [ -z "$CHOICE" ]; then
whiptail --title "LXC Container Update" \
--msgbox "No containers selected!" 10 60
exit 0
fi
fi
header_info
# Determine backup choice based on var_backup
# Dry-run never needs a backup — skip the prompt entirely
if [[ "$var_dry_run" == "yes" ]]; then
BACKUP_CHOICE="no"
elif [[ -n "$var_backup" ]]; then
BACKUP_CHOICE="$var_backup"
else
BACKUP_CHOICE="no"
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "Do you want to backup your containers before update?" 10 58); then
BACKUP_CHOICE="yes"
fi
fi
# Determine unattended update based on var_unattended
# Dry-run never executes updates — skip the prompt entirely
if [[ "$var_dry_run" == "yes" ]]; then
UNATTENDED_UPDATE="no"
elif [[ -n "$var_unattended" ]]; then
UNATTENDED_UPDATE="$var_unattended"
else
UNATTENDED_UPDATE="no"
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "Run updates unattended?" 10 58); then
UNATTENDED_UPDATE="yes"
fi
fi
if [ "$BACKUP_CHOICE" == "yes" ]; then
get_backup_storages
if [ -z "$STORAGES" ]; then
msg_error "No storage with 'backup' support found!"
exit 119
fi
# Determine storage based on var_backup_storage
if [[ -n "$var_backup_storage" ]]; then
# Validate that the specified storage exists and supports backups
if echo "$STORAGES" | grep -qw "$var_backup_storage"; then
STORAGE_CHOICE="$var_backup_storage"
msg_ok "Using backup storage: $STORAGE_CHOICE"
else
msg_error "Specified backup storage '$var_backup_storage' not found or doesn't support backups!"
msg_info "Available storages: $(echo $STORAGES | tr '\n' ' ')"
exit 119
fi
else
MENU_ITEMS=()
for STORAGE in $STORAGES; do
MENU_ITEMS+=("$STORAGE" "")
done
STORAGE_CHOICE=$(whiptail --title "Select storage device" --menu "Select a storage device (Only storage devices with 'backup' support are listed):" 15 50 5 "${MENU_ITEMS[@]}" 3>&1 1>&2 2>&3)
if [ -z "$STORAGE_CHOICE" ]; then
msg_error "No storage selected!"
exit 0
fi
fi
fi
UPDATE_CMD="update;"
if [ "$UNATTENDED_UPDATE" == "yes" ]; then
UPDATE_CMD="export PHS_SILENT=1;update;"
fi
containers_needing_reboot=()
for container in $CHOICE; do
echo -e "${BL}[INFO]${CL} Updating container $container"
log_write "Container $container: starting"
if [ "$BACKUP_CHOICE" == "yes" ]; then
backup_container $container
fi
os=$(pct config "$container" | awk '/^ostype/ {print $2}')
status=$(pct status $container)
template=$(pct config $container | grep -q "template:" && echo "true" || echo "false")
if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then
echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL} \n"
pct start $container
echo -e "${BL}[Info]${GN} Waiting For${BL} $container${CL}${GN} To Start ${CL} \n"
sleep 5
fi
#1) Detect service using the service name in the update command
detect_service $container
#1.1) If update script not detected, return
if [ -z "${service}" ]; then
echo -e "${YW}[WARN]${CL} Update script not found. Skipping to next container"
log_result "$container" "(unknown)" "SKIPPED" "No update script found in container"
log_write "Container $container: SKIPPED — no update script found"
continue
else
echo -e "${BL}[INFO]${CL} Detected service: ${GN}${service}${CL}"
log_write "Container $container: detected service '$service'"
fi
#2) Extract service build/update resource requirements from config/installation file
script=$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${service}.sh)
#2.1) Check if the script downloaded successfully
if [ $? -ne 0 ]; then
echo -e "${RD}[ERROR]${CL} Issue while downloading install script."
echo -e "${YW}[WARN]${CL} Unable to assess build resource requirements. Proceeding with current resources."
fi
config=$(pct config "$container")
build_cpu=$(echo "$script" | { grep -m 1 "var_cpu" || test $? = 1; } | sed 's|.*=||g' | sed 's|"||g' | sed 's|.*var_cpu:-||g' | sed 's|}||g')
build_ram=$(echo "$script" | { grep -m 1 "var_ram" || test $? = 1; } | sed 's|.*=||g' | sed 's|"||g' | sed 's|.*var_ram:-||g' | sed 's|}||g')
run_cpu=$(echo "$script" | { grep -m 1 "pct set \$CTID -cores" || test $? = 1; } | sed 's|.*cores ||g')
run_ram=$(echo "$script" | { grep -m 1 "pct set \$CTID -memory" || test $? = 1; } | sed 's|.*memory ||g')
current_cpu=$(echo "$config" | grep -m 1 "cores:" | sed 's|cores: ||g')
current_ram=$(echo "$config" | grep -m 1 "memory:" | sed 's|memory: ||g')
#Test if all values are valid (>0)
if [ -z "${run_cpu}" ] || [ "$run_cpu" -le 0 ]; then
#echo "No valid value found for run_cpu. Assuming same as current configuration."
run_cpu=$current_cpu
fi
if [ -z "${run_ram}" ] || [ "$run_ram" -le 0 ]; then
#echo "No valid value found for run_ram. Assuming same as current configuration."
run_ram=$current_ram
fi
if [ -z "${build_cpu}" ] || [ "$build_cpu" -le 0 ]; then
#echo "No valid value found for build_cpu. Assuming same as current configuration."
build_cpu=$current_cpu
fi
if [ -z "${build_ram}" ] || [ "$build_ram" -le 0 ]; then
#echo "No valid value found for build_ram. Assuming same as current configuration."
build_ram=$current_ram
fi
UPDATE_BUILD_RESOURCES=0
if [ "$build_cpu" -gt "$run_cpu" ] || [ "$build_ram" -gt "$run_ram" ]; then
UPDATE_BUILD_RESOURCES=1
fi
#3) if build resources are different than run resources, then:
if [ "$UPDATE_BUILD_RESOURCES" -eq "1" ] && [[ "$var_dry_run" != "yes" ]]; then
pct set "$container" --cores "$build_cpu" --memory "$build_ram"
fi
#3.5) Dry-run: report update availability without applying
if [[ "$var_dry_run" == "yes" ]]; then
DRY_RUN_RESULT=""
dry_run_container "$container" "$service"
log_result "$container" "$service" "DRY-RUN" "${DRY_RUN_RESULT:-version check only}"
log_write "Container $container ($service): DRY-RUN — ${DRY_RUN_RESULT:-version check only}"
continue
fi
#4) Update service, using the update command
# Prepend a no-op 'clear' wrapper to PATH so update scripts calling clear
# don't fail without a TTY — works for all shells incl. ash (no export -f)
SETUP_CMD="mkdir -p /tmp/.nc; printf '#!/bin/sh\n:\n' > /tmp/.nc/clear; chmod +x /tmp/.nc/clear; export PATH=/tmp/.nc:\$PATH; export TERM=dumb; "
case "$os" in
alpine) pct exec "$container" -- ash -c "${SETUP_CMD}${UPDATE_CMD}" ;;
archlinux) pct exec "$container" -- bash -c "${SETUP_CMD}${UPDATE_CMD}" ;;
fedora | rocky | centos | alma) pct exec "$container" -- bash -c "${SETUP_CMD}${UPDATE_CMD}" ;;
ubuntu | debian | devuan) pct exec "$container" -- bash -c "${SETUP_CMD}${UPDATE_CMD}" ;;
opensuse) pct exec "$container" -- bash -c "${SETUP_CMD}${UPDATE_CMD}" ;;
esac
exit_code=$?
#5) if build resources are different than run resources, then:
if [ "$UPDATE_BUILD_RESOURCES" -eq "1" ]; then
pct set "$container" --cores "$run_cpu" --memory "$run_ram"
fi
if pct exec "$container" -- [ -e "/var/run/reboot-required" ]; then
# Get the container's hostname and add it to the list
container_hostname=$(pct exec "$container" hostname)
containers_needing_reboot+=("$container ($container_hostname)")
fi
if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then
echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL} \n"
pct shutdown $container &>/dev/null &
fi
if [ $exit_code -eq 0 ]; then
msg_ok "Updated container $container"
log_result "$container" "$service" "OK" "Updated successfully"
log_write "Container $container ($service): OK"
elif [ $exit_code -eq 75 ]; then
echo -e "${YW}[WARN]${CL} Container $container skipped (requires interactive mode)"
log_result "$container" "$service" "SKIPPED" "Requires interactive mode (exit 75)"
log_write "Container $container ($service): SKIPPED — requires interactive mode"
elif [ $exit_code -eq 113 ]; then
echo -e "${YW}[WARN]${CL} Container $container skipped (under-provisioned: increase CPU/RAM to match template)"
log_result "$container" "$service" "SKIPPED" "Under-provisioned — increase CPU/RAM to match template"
log_write "Container $container ($service): SKIPPED — under-provisioned"
elif [ $exit_code -eq 114 ]; then
echo -e "${YW}[WARN]${CL} Container $container skipped (storage critically low on /boot)"
log_result "$container" "$service" "SKIPPED" "Storage critically low on /boot (>80%)"
log_write "Container $container ($service): SKIPPED — storage critically low on /boot"
elif [ "$BACKUP_CHOICE" == "yes" ]; then
msg_error "Update failed for container $container (exit code: $exit_code) — attempting restore"
log_write "Container $container ($service): FAILED (exit $exit_code) — attempting restore"
msg_info "Restoring LXC $container from backup ($STORAGE_CHOICE)"
pct stop $container
LXC_STORAGE=$(pct config $container | awk -F '[:,]' '/rootfs/ {print $2}')
BACKUP_ENTRY=$(pvesm list "$STORAGE_CHOICE" 2>/dev/null | awk -v ctid="$container" '$1 ~ "vzdump-lxc-"ctid"-" || $1 ~ "/ct/"ctid"/" {print $1}' | sort -r | head -n1)
if [ -z "$BACKUP_ENTRY" ]; then
msg_error "No backup found in storage $STORAGE_CHOICE for container $container"
log_result "$container" "$service" "FAILED" "Update failed (exit $exit_code) — no backup found for restore"
log_write "Container $container ($service): FAILED — no backup found for restore"
exit 235
fi
msg_info "Restoring from: $BACKUP_ENTRY"
pct restore $container "$BACKUP_ENTRY" --storage $LXC_STORAGE --force >/dev/null 2>&1
restorestatus=$?
if [ $restorestatus -eq 0 ]; then
pct start $container
msg_ok "Container $container successfully restored from backup"
log_result "$container" "$service" "RESTORED" "Update failed (exit $exit_code) — restored from backup"
log_write "Container $container ($service): RESTORED from $BACKUP_ENTRY"
else
msg_error "Restore failed for container $container"
log_result "$container" "$service" "FAILED" "Update failed (exit $exit_code) — restore also failed"
log_write "Container $container ($service): FAILED — restore also failed"
exit 235
fi
else
msg_error "Update failed for container $container (exit code: $exit_code)"
log_result "$container" "$service" "FAILED" "Exit code $exit_code"
log_write "Container $container ($service): FAILED (exit $exit_code)"
if [[ "$var_continue_on_error" == "yes" ]]; then
echo -e "${YW}[WARN]${CL} Continuing to next container (var_continue_on_error=yes)"
continue
else
exit "$exit_code"
fi
fi
done
wait
header_info
if [[ "$var_dry_run" == "yes" ]]; then
echo -e "${GN}Dry-run complete. No containers were modified.${CL}\n"
else
echo -e "${GN}The process is complete, and the containers have been successfully updated.${CL}\n"
fi
# =============================================================================
# SUMMARY REPORT
# =============================================================================
if [ "${#UPDATE_RESULTS[@]}" -gt 0 ]; then
SEPARATOR="━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
HEADER=$(printf " %-8s %-22s %-10s %s" "CTID" "Service" "Status" "Details")
# terminal output (with colours)
echo ""
echo "$SEPARATOR"
echo "$HEADER"
echo "$SEPARATOR"
for entry in "${UPDATE_RESULTS[@]}"; do
IFS='|' read -r _ctid _svc _status _details <<<"$entry"
case "$_status" in
OK) _color="${GN}" ;;
FAILED) _color="${RD}" ;;
RESTORED) _color="${YW}" ;;
*) _color="${YW}" ;;
esac
printf " %-8s %-22s ${_color}%-10s${CL} %s\n" "$_ctid" "$_svc" "$_status" "$_details"
done
echo "$SEPARATOR"
echo ""
echo "Full log: $LOG_FILE"
echo ""
# append plain-text summary to log file
{
echo ""
echo "Update finished: $(date '+%Y-%m-%d %H:%M:%S')"
echo "$SEPARATOR"
echo "$HEADER"
echo "$SEPARATOR"
for entry in "${UPDATE_RESULTS[@]}"; do
IFS='|' read -r _ctid _svc _status _details <<<"$entry"
printf " %-8s %-22s %-10s %s\n" "$_ctid" "$_svc" "$_status" "$_details"
done
echo "$SEPARATOR"
} >>"$LOG_FILE"
fi
if [ "${#containers_needing_reboot[@]}" -gt 0 ]; then
echo -e "${RD}The following containers require a reboot:${CL}"
for container_name in "${containers_needing_reboot[@]}"; do
echo "$container_name"
done
# Determine reboot choice based on var_auto_reboot
REBOOT_CHOICE="no"
if [[ -n "$var_auto_reboot" ]]; then
REBOOT_CHOICE="$var_auto_reboot"
else
echo -ne "${INFO} Do you wish to reboot these containers? <yes/No> "
read -r prompt
if [[ ${prompt,,} =~ ^(yes)$ ]]; then
REBOOT_CHOICE="yes"
fi
fi
if [[ "$REBOOT_CHOICE" == "yes" ]]; then
echo -e "${CROSS}${HOLD} ${YWB}Rebooting containers.${CL}"
for container_name in "${containers_needing_reboot[@]}"; do
container=$(echo $container_name | cut -d " " -f 1)
pct reboot ${container}
done
fi
fi
+124
View File
@@ -0,0 +1,124 @@
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/clean-lxcs.sh)"
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# License: MIT
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
function header_info() {
clear
cat <<"EOF"
________ __ _ ________
/ ____/ /__ ____ _____ / / | |/ / ____/
/ / / / _ \/ __ `/ __ \ / / | / /
/ /___/ / __/ /_/ / / / / / /___/ / /___
\____/_/\___/\__,_/_/ /_/ /_____/_/|_\____/
EOF
}
set -eEuo pipefail
BL="\033[36m"
RD="\033[01;31m"
CM='\xE2\x9C\x94\033'
GN="\033[1;92m"
CL="\033[m"
# Telemetry
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "clean-lxcs" "pve"
header_info
echo "Loading..."
whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Updater" --yesno "This will clean logs, cache and update package lists on selected LXC Containers. Proceed?" 10 58
NODE=$(hostname)
EXCLUDE_MENU=()
MSG_MAX_LENGTH=0
while read -r TAG ITEM; do
OFFSET=2
((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET
EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF")
done < <(pct list | awk 'NR>1')
excluded_containers=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --checklist "\nSelect containers to skip from cleaning:\n" \
16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"')
if [ $? -ne 0 ]; then
exit
fi
function run_lxc_clean() {
local container=$1
header_info
name=$(pct exec "$container" hostname)
pct exec "$container" -- bash -c '
BL="\033[36m"; GN="\033[1;92m"; CL="\033[m"
name=$(hostname)
if [ -e /etc/alpine-release ]; then
echo -e "${BL}[Info]${GN} Cleaning $name (Alpine)${CL}\n"
apk cache clean
find /var/log -type f -delete 2>/dev/null
find /tmp -mindepth 1 -delete 2>/dev/null
apk update
elif [ -e /etc/redhat-release ]; then
echo -e "${BL}[Info]${GN} Cleaning $name (CentOS)${CL}\n"
yum clean all
find /var/log -type f -delete 2>/dev/null
find /tmp -mindepth 1 -delete 2>/dev/null
yum update
yum upgrade -y
else
echo -e "${BL}[Info]${GN} Cleaning $name (Debian/Ubuntu)${CL}\n"
find /var/cache -type f -delete 2>/dev/null
find /var/log -type f -delete 2>/dev/null
find /tmp -mindepth 1 -delete 2>/dev/null
apt -y --purge autoremove
apt -y autoclean
rm -rf /var/lib/apt/lists/*
apt update
fi
'
}
for container in $(pct list | awk '{if(NR>1) print $1}'); do
if [[ " ${excluded_containers[@]} " =~ " $container " ]]; then
header_info
echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}"
sleep 1
continue
fi
os=$(pct config "$container" | awk '/^ostype/ {print $2}')
# Supported: debian, ubuntu, alpine, centos
if [ "$os" != "debian" ] && [ "$os" != "ubuntu" ] && [ "$os" != "alpine" ] && [ "$os" != "centos" ]; then
header_info
echo -e "${BL}[Info]${GN} Skipping ${RD}$container is not Debian, Ubuntu, Alpine or Red Hat Compatible${CL} \n"
sleep 1
continue
fi
status=$(pct status "$container")
template=$(pct config "$container" | grep -q "template:" && echo "true" || echo "false")
if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then
echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL} \n"
pct start "$container"
echo -e "${BL}[Info]${GN} Waiting For${BL} $container${CL}${GN} To Start ${CL} \n"
sleep 5
run_lxc_clean "$container"
echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL} \n"
pct shutdown "$container" &
elif [ "$status" == "status: running" ]; then
run_lxc_clean "$container"
fi
done
wait
header_info
echo -e "${GN} Finished, Selected Containers Cleaned. ${CL} \n"
+94
View File
@@ -0,0 +1,94 @@
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/execute.sh)"
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: jeroenzwart
# License: MIT
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
function header_info() {
clear
cat <<"EOF"
______ __ __ _ ________
/ ____/ _____ _______ __/ /____ / / | |/ / ____/
/ __/ | |/_/ _ \/ ___/ / / / __/ _ \ / / | / /
/ /____> </ __/ /__/ /_/ / /_/ __/ / /___/ / /___
/_____/_/|_|\___/\___/\__,_/\__/\___/ /_____/_/|_\____/
EOF
}
set -eEuo pipefail
BL=$(echo "\033[36m")
RD=$(echo "\033[01;31m")
CM='\xE2\x9C\x94\033'
GN=$(echo "\033[1;92m")
CL=$(echo "\033[m")
# Telemetry
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "execute-lxcs" "pve"
header_info
echo "Loading..."
whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Execute" --yesno "This will execute a command inside selected LXC Containers. Proceed?" 10 58
NODE=$(hostname)
EXCLUDE_MENU=()
MSG_MAX_LENGTH=0
while read -r TAG ITEM; do
OFFSET=2
((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET
EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF")
done < <(pct list | awk 'NR>1')
excluded_containers=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --checklist "\nSelect containers to skip from executing:\n" \
16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"')
if [ $? -ne 0 ]; then
exit
fi
read -r -p "Enter here command for inside the containers: " custom_command
header_info
echo "One moment please...\n"
function execute_in() {
container=$1
name=$(pct exec "$container" hostname)
echo -e "${BL}[Info]${GN} Execute inside${BL} ${name}${GN} with output: ${CL}"
if ! pct exec "$container" -- bash -c "command ${custom_command} >/dev/null 2>&1"; then
echo -e "${BL}[Info]${GN} Skipping ${name} ${RD}$container has no command: ${custom_command}"
else
pct exec "$container" -- bash -c "${custom_command}" | tee
fi
}
for container in $(pct list | awk '{if(NR>1) print $1}'); do
if [[ " ${excluded_containers[@]} " =~ " $container " ]]; then
echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}"
else
os=$(pct config "$container" | awk '/^ostype/ {print $2}')
if [ "$os" != "debian" ] && [ "$os" != "ubuntu" ]; then
echo -e "${BL}[Info]${GN} Skipping ${name} ${RD}$container is not Debian or Ubuntu ${CL}"
continue
fi
status=$(pct status "$container")
template=$(pct config "$container" | grep -q "template:" && echo "true" || echo "false")
if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then
echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL}"
pct start "$container"
echo -e "${BL}[Info]${GN} Waiting For${BL} $container${CL}${GN} To Start ${CL}"
sleep 5
execute_in "$container"
echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL}"
pct shutdown "$container" &
elif [ "$status" == "status: running" ]; then
execute_in "$container"
fi
fi
done
wait
echo -e "${GN} Finished, execute command inside selected containers. ${CL} \n"
+197
View File
@@ -0,0 +1,197 @@
# Это позволяет поддерживать производительность SSD за счет управления неиспользуемыми блоками. Системы хранения с тонким выделением ресурсов также требуют управления для предотвращения ненужного использования хранилища. Виртуальные машины автоматизируют fstrim, в то время как контейнеры LXC требуют ручных или автоматизированных процессов fstrim для оптимальной производительности.
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/fstrim.sh)"
#!/usr/bin/env bash
set -eEuo pipefail
function header_info() {
clear
cat <<"EOF"
_______ __ __ ______ _
/ ____(_) /__ _______ _______/ /____ ____ ___ /_ __/____(_)___ ___
/ /_ / / / _ \/ ___/ / / / ___/ __/ _ \/ __ `__ \ / / / ___/ / __ `__ \
/ __/ / / / __(__ ) /_/ (__ ) /_/ __/ / / / / / / / / / / / / / / / /
/_/ /_/_/\___/____/\__, /____/\__/\___/_/ /_/ /_/ /_/ /_/ /_/_/ /_/ /_/
/____/
EOF
}
BL="\033[36m"
RD="\033[01;31m"
GN="\033[1;92m"
CL="\033[m"
# Telemetry
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "fstrim" "pve"
LOGFILE="/var/log/fstrim.log"
touch "$LOGFILE"
chmod 600 "$LOGFILE"
echo -e "\n----- $(date '+%Y-%m-%d %H:%M:%S') | fstrim Run by $(whoami) on $(hostname) -----" >>"$LOGFILE"
header_info
echo "Loading..."
whiptail --backtitle "Proxmox VE Helper Scripts" \
--title "About fstrim (LXC)" \
--msgbox "The 'fstrim' command releases unused blocks back to the storage device. This only makes sense for containers on SSD, NVMe, Thin-LVM, or storage with discard/TRIM support.\n\nIf your root filesystem or container disks are on classic HDDs, thick LVM, or unsupported storage types, running fstrim will have no effect.\n\nRecommended:\n- Use fstrim only on SSD, NVMe, or thin-provisioned storage with discard enabled.\n- For ZFS, ensure 'autotrim=on' is set on your pool.\n" 16 88
ROOT_FS=$(df -Th "/" | awk 'NR==2 {print $2}')
if [ "$ROOT_FS" != "ext4" ]; then
whiptail --backtitle "Proxmox VE Helper Scripts" \
--title "Warning" \
--yesno "Root filesystem is not ext4 ($ROOT_FS).\nContinue anyway?" 12 80 || exit 0
fi
NODE=$(hostname)
EXCLUDE_MENU=()
STOPPED_MENU=()
MAX_NAME_LEN=0
MAX_STAT_LEN=0
# Build arrays with one pct list
mapfile -t CTLINES < <(pct list | awk 'NR>1')
for LINE in "${CTLINES[@]}"; do
CTID=$(awk '{print $1}' <<<"$LINE")
STATUS=$(awk '{print $2}' <<<"$LINE")
NAME=$(awk '{print $3}' <<<"$LINE")
((${#NAME} > MAX_NAME_LEN)) && MAX_NAME_LEN=${#NAME}
((${#STATUS} > MAX_STAT_LEN)) && MAX_STAT_LEN=${#STATUS}
done
FMT="%-${MAX_NAME_LEN}s | %-${MAX_STAT_LEN}s"
for LINE in "${CTLINES[@]}"; do
CTID=$(awk '{print $1}' <<<"$LINE")
STATUS=$(awk '{print $2}' <<<"$LINE")
NAME=$(awk '{print $3}' <<<"$LINE")
DESC=$(printf "$FMT" "$NAME" "$STATUS")
EXCLUDE_MENU+=("$CTID" "$DESC" "OFF")
if [[ "$STATUS" == "stopped" ]]; then
STOPPED_MENU+=("$CTID" "$DESC" "OFF")
fi
done
excluded_containers_raw=$(whiptail --backtitle "Proxmox VE Helper Scripts" \
--title "Containers on $NODE" \
--checklist "\nSelect containers to skip from trimming:\n" \
20 $((MAX_NAME_LEN + MAX_STAT_LEN + 20)) 12 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3)
[ $? -ne 0 ] && exit
read -ra EXCLUDED <<<$(echo "$excluded_containers_raw" | tr -d '"')
TO_START=()
if [ ${#STOPPED_MENU[@]} -gt 0 ]; then
for ((i = 0; i < ${#STOPPED_MENU[@]}; i += 3)); do
CTID="${STOPPED_MENU[i]}"
DESC="${STOPPED_MENU[i + 1]}"
if [[ " ${EXCLUDED[*]} " =~ " $CTID " ]]; then
continue
fi
header_info
echo -e "${BL}[Info]${GN} Container $CTID ($DESC) is currently stopped.${CL}"
read -rp "Temporarily start for fstrim? [y/N]: " answer
if [[ "$answer" =~ ^[Yy]$ ]]; then
TO_START+=("$CTID")
fi
done
fi
declare -A WAS_STOPPED
for ct in "${TO_START[@]}"; do
WAS_STOPPED["$ct"]=1
done
function trim_container() {
local container="$1"
local name="$2"
header_info
echo -e "${BL}[Info]${GN} Trimming ${BL}$container${CL} \n"
local before_trim after_trim
local lv_name="vm-${container}-disk-0"
if lvs --noheadings -o lv_name 2>/dev/null | grep -qw "$lv_name"; then
before_trim=$(lvs --noheadings -o lv_name,data_percent 2>/dev/null | awk -v ctid="$lv_name" '$1 == ctid {gsub(/%/, "", $2); print $2}')
[[ -n "$before_trim" ]] && echo -e "${RD}Data before trim $before_trim%${CL}" || echo -e "${RD}Data before trim: not available${CL}"
else
before_trim=""
echo -e "${RD}Data before trim: not available (non-LVM storage)${CL}"
fi
local fstrim_output
fstrim_output=$(pct fstrim "$container" 2>&1)
if echo "$fstrim_output" | grep -qi "not supported"; then
echo -e "${RD}fstrim isnt supported on this storage!${CL}"
elif echo "$fstrim_output" | grep -Eq '([0-9]+(\.[0-9]+)?\s*[KMGT]?B)'; then
echo -e "${GN}fstrim result: $fstrim_output${CL}"
else
echo -e "${RD}fstrim result: $fstrim_output${CL}"
fi
if lvs --noheadings -o lv_name 2>/dev/null | grep -qw "$lv_name"; then
after_trim=$(lvs --noheadings -o lv_name,data_percent 2>/dev/null | awk -v ctid="$lv_name" '$1 == ctid {gsub(/%/, "", $2); print $2}')
[[ -n "$after_trim" ]] && echo -e "${GN}Data after trim $after_trim%${CL}" || echo -e "${GN}Data after trim: not available${CL}"
else
after_trim=""
echo -e "${GN}Data after trim: not available (non-LVM storage)${CL}"
fi
# Logging
echo "$(date '+%Y-%m-%d %H:%M:%S') | CTID=$container | Name=$name | Before=${before_trim:-N/A}% | After=${after_trim:-N/A}% | fstrim: $fstrim_output" >>"$LOGFILE"
sleep 0.5
}
for LINE in "${CTLINES[@]}"; do
CTID=$(awk '{print $1}' <<<"$LINE")
STATUS=$(awk '{print $2}' <<<"$LINE")
NAME=$(awk '{print $3}' <<<"$LINE")
if [[ " ${EXCLUDED[*]} " =~ " $CTID " ]]; then
header_info
echo -e "${BL}[Info]${GN} Skipping $CTID ($NAME, excluded)${CL}"
sleep 0.5
continue
fi
if pct config "$CTID" | grep -q "template:"; then
header_info
echo -e "${BL}[Info]${GN} Skipping $CTID ($NAME, template)${CL}\n"
sleep 0.5
continue
fi
if [[ "$STATUS" != "running" ]]; then
if [[ -n "${WAS_STOPPED[$CTID]:-}" ]]; then
header_info
echo -e "${BL}[Info]${GN} Starting $CTID ($NAME) for trim...${CL}"
pct start "$CTID"
sleep 2
else
header_info
echo -e "${BL}[Info]${GN} Skipping $CTID ($NAME, not running, not selected)${CL}"
sleep 0.5
continue
fi
fi
trim_container "$CTID" "$NAME"
if [[ -n "${WAS_STOPPED[$CTID]:-}" ]]; then
read -rp "Stop LXC $CTID ($NAME) again after trim? [Y/n]: " answer
if [[ ! "$answer" =~ ^[Nn]$ ]]; then
header_info
echo -e "${BL}[Info]${GN} Stopping $CTID ($NAME) again...${CL}"
pct stop "$CTID"
sleep 1
else
header_info
echo -e "${BL}[Info]${GN} Leaving $CTID ($NAME) running as requested.${CL}"
sleep 1
fi
fi
done
header_info
echo -e "${GN}Finished, LXC Containers Trimmed.${CL} \n"
echo -e "${BL}If you want to see the complete log: cat $LOGFILE${CL}"
exit 0
File diff suppressed because it is too large Load Diff
+171
View File
@@ -0,0 +1,171 @@
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/netdata.sh)"
#!/usr/bin/env bash
# Copyright (c) 2021-2026 tteck
# Author: tteck (tteckster)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://www.netdata.cloud/ | Github: https://github.com/netdata/netdata
function header_info {
clear
cat <<"EOF"
_ __ __ ____ __
/ | / /__ / /_/ __ \____ _/ /_____ _
/ |/ / _ \/ __/ / / / __ `/ __/ __ `/
/ /| / __/ /_/ /_/ / /_/ / /_/ /_/ /
/_/ |_/\___/\__/_____/\__,_/\__/\__,_/
EOF
}
YW=$(echo "\033[33m")
BL=$(echo "\033[36m")
RD=$(echo "\033[01;31m")
GN=$(echo "\033[1;92m")
CL=$(echo "\033[m")
BFR="\\r\\033[K"
HOLD="-"
CM="${GN}${CL}"
silent() { "$@" >/dev/null 2>&1; }
# Telemetry
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "netdata" "addon"
set -e
header_info
echo "Loading..."
function msg_info() {
local msg="$1"
echo -ne " ${HOLD} ${YW}${msg}..."
}
function msg_ok() {
local msg="$1"
echo -e "${BFR} ${CM} ${GN}${msg}${CL}"
}
function msg_error() { echo -e "${RD}$1${CL}"; }
# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported.
# Supported: Proxmox VE 8.0.x 8.9.x and 9.09.x
pve_check() {
local PVE_VER
PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')"
# Check for Proxmox VE 8.x: allow 8.08.9
if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then
local MINOR="${BASH_REMATCH[1]}"
if ((MINOR < 0 || MINOR > 9)); then
msg_error "This version of Proxmox VE is not supported."
msg_error "Supported: Proxmox VE version 8.0 8.9"
exit 105
fi
return 0
fi
# Check for Proxmox VE 9.x: allow 9.09.x
if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then
return 0
fi
# All other unsupported versions
msg_error "This version of Proxmox VE is not supported."
msg_error "Supported versions: Proxmox VE 8.0 8.9 or 9.09.x"
exit 105
}
detect_codename() {
source /etc/os-release
if [[ "$ID" != "debian" ]]; then
msg_error "Unsupported base OS: $ID (only Proxmox VE / Debian supported)."
exit 238
fi
CODENAME="${VERSION_CODENAME:-}"
if [[ -z "$CODENAME" ]]; then
msg_error "Could not detect Debian codename."
exit 71
fi
echo "$CODENAME"
}
get_latest_repo_pkg() {
local REPO_URL=$1
curl -fsSL "$REPO_URL" |
grep -oP 'netdata-repo_[^"]+all\.deb' |
sort -V |
tail -n1
}
install() {
header_info
while true; do
read -p "Are you sure you want to install NetData on Proxmox VE host. Proceed(y/n)? " yn
case $yn in
[Yy]*) break ;;
[Nn]*) exit ;;
*) echo "Please answer yes or no." ;;
esac
done
read -r -p "Verbose mode? <y/N> " prompt
[[ ${prompt,,} =~ ^(y|yes)$ ]] && STD="" || STD="silent"
CODENAME=$(detect_codename)
REPO_URL="https://repo.netdata.cloud/repos/repoconfig/debian/${CODENAME}/"
msg_info "Setting up repository"
$STD apt-get install -y debian-keyring
PKG=$(get_latest_repo_pkg "$REPO_URL")
if [[ -z "$PKG" ]]; then
msg_error "Could not find netdata-repo package for Debian $CODENAME"
exit 237
fi
curl -fsSL "${REPO_URL}${PKG}" -o "$PKG"
$STD dpkg -i "$PKG"
rm -f "$PKG"
msg_ok "Set up repository"
msg_info "Installing Netdata"
$STD apt-get update
$STD apt-get install -y netdata
msg_ok "Installed Netdata"
msg_ok "Completed successfully!\n"
echo -e "\n Netdata should be reachable at${BL} http://$(hostname -I | awk '{print $1}'):19999 ${CL}\n"
}
uninstall() {
header_info
read -r -p "Verbose mode? <y/N> " prompt
[[ ${prompt,,} =~ ^(y|yes)$ ]] && STD="" || STD="silent"
msg_info "Uninstalling Netdata"
systemctl stop netdata || true
rm -rf /var/log/netdata /var/lib/netdata /var/cache/netdata /etc/netdata/go.d
rm -rf /etc/apt/trusted.gpg.d/netdata-archive-keyring.gpg /etc/apt/sources.list.d/netdata.list
$STD apt-get remove --purge -y netdata netdata-repo
systemctl daemon-reload
$STD apt autoremove -y
$STD userdel netdata || true
msg_ok "Uninstalled Netdata"
msg_ok "Completed successfully!\n"
}
header_info
pve_check
OPTIONS=(Install "Install NetData on Proxmox VE"
Uninstall "Uninstall NetData from Proxmox VE")
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "NetData" \
--menu "Select an option:" 10 58 2 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
case $CHOICE in
"Install") install ;;
"Uninstall") uninstall ;;
*)
echo "Exiting..."
exit 0
;;
esac
+72
View File
@@ -0,0 +1,72 @@
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/update-repo.sh)"
#!/usr/bin/env bash
# Copyright (c) 2021-2026 tteck
# Author: MickLesk
# License: MIT
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
function header_info {
clear
cat <<"EOF"
__ __ __ __ ____
/ / / /___ ____/ /___ _/ /____ / __ \___ ____ ____
/ / / / __ \/ __ / __ `/ __/ _ \ / /_/ / _ \/ __ \/ __ \
/ /_/ / /_/ / /_/ / /_/ / /_/ __/ / _, _/ __/ /_/ / /_/ /
\____/ .___/\__,_/\__,_/\__/\___/ /_/ |_|\___/ .___/\____/
/_/ /_/
EOF
}
set -eEuo pipefail
BL=$(echo "\033[36m")
RD=$(echo "\033[01;31m")
GN=$(echo "\033[1;92m")
CL=$(echo "\033[m")
# Telemetry
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "update-repo" "pve"
header_info
echo "Loading..."
NODE=$(hostname)
function update_container() {
container=$1
os=$(pct config "$container" | awk '/^ostype/ {print $2}')
if [[ "$os" == "ubuntu" || "$os" == "debian" ]]; then
echo -e "${BL}[Info]${GN} Checking /usr/bin/update in ${BL}$container${CL} (OS: ${GN}$os${CL})"
if pct exec "$container" -- [ -e /usr/bin/update ]; then
if pct exec "$container" -- grep -q "community-scripts/ProxmoxVE" /usr/bin/update; then
echo -e "${RD}[No Change]${CL} /usr/bin/update is already up to date in ${BL}$container${CL}.\n"
elif pct exec "$container" -- grep -q -v "tteck" /usr/bin/update; then
echo -e "${RD}[Warning]${CL} /usr/bin/update in ${BL}$container${CL} contains a different entry (${RD}tteck${CL}). No changes made.\n"
else
pct exec "$container" -- bash -c "sed -i 's/tteck\\/Proxmox/community-scripts\\/ProxmoxVE/g' /usr/bin/update"
if pct exec "$container" -- grep -q "community-scripts/ProxmoxVE" /usr/bin/update; then
echo -e "${GN}[Success]${CL} /usr/bin/update updated in ${BL}$container${CL}.\n"
else
echo -e "${RD}[Error]${CL} /usr/bin/update in ${BL}$container${CL} could not be updated properly.\n"
fi
fi
else
echo -e "${RD}[Error]${CL} /usr/bin/update not found in container ${BL}$container${CL}.\n"
fi
else
echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL} (not Debian/Ubuntu)\n"
fi
}
header_info
for container in $(pct list | awk '{if(NR>1) print $1}'); do
update_container "$container"
done
header_info
echo -e "${GN}The process is complete. The repositories have been switched to community-scripts/ProxmoxVE.${CL}\n"
+188
View File
@@ -0,0 +1,188 @@
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/pbs-microcode.sh)"
# Микрокод процессора — это низкоуровневый программный слой, работающий на процессоре и предоставляющий исправления или обновления для его микропрограммы. Обновления микрокода могут исправлять аппаратные ошибки,
# повышать производительность и улучшать функции безопасности процессора. Этот скрипт адаптирован для среды Proxmox Backup Server и будет работать только на физических системах. При запуске в виртуализированной среде скрипт завершит работу.
# Обратите внимание, что механизмы обновления микропрограммы, такие как Intel Management Engine (ME) или AMD Platform Security Processor (PSP), могут различаться в зависимости от вашего процессора и его реализации. Пожалуйста, обратитесь к
# документации вашего процессора, чтобы проверить, можно ли применить обновления микропрограммы через операционную систему.
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: DonPablo1010 | Co-Author: tteck (tteckster)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
function header_info {
clear
cat <<"EOF"
____ __ ____ __
/ __ \_________ ________ ______________ _____ / |/ (_)_____________ _________ ____/ /__
/ /_/ / ___/ __ \/ ___/ _ \/ ___/ ___/ __ \/ ___/ / /|_/ / / ___/ ___/ __ \/ ___/ __ \/ __ / _ \
/ ____/ / / /_/ / /__/ __(__ |__ ) /_/ / / / / / / / /__/ / / /_/ / /__/ /_/ / /_/ / __/
/_/ /_/ \____/\___/\___/____/____/\____/_/ /_/ /_/_/\___/_/ \____/\___/\____/\__,_/\___/
Proxmox Backup Server Processor Microcode Updater
EOF
}
# Color definitions
RD=$(echo "\033[01;31m")
YW=$(echo "\033[33m")
GN=$(echo "\033[1;92m")
CL=$(echo "\033[m")
BFR="\\r\\033[K"
HOLD="-"
CM="${GN}${CL}"
CROSS="${RD}${CL}"
msg_info() { echo -ne " ${HOLD} ${YW}$1..."; }
# Telemetry
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "pbs-microcode" "pve"
msg_ok() { echo -e "${BFR} ${CM} ${GN}$1${CL}"; }
msg_error() { echo -e "${BFR} ${CROSS} ${RD}$1${CL}"; }
header_info
# Check if running on bare metal using systemd-detect-virt.
virt=$(systemd-detect-virt)
if [ "$virt" != "none" ]; then
msg_error "This script must be run on bare metal. Detected virtual environment: $virt"
exit 232
fi
# Attempt to obtain the current loaded microcode revision
current_microcode=$(journalctl -k | grep -i 'microcode: Current revision:' | grep -oP 'Current revision: \K0x[0-9a-f]+')
[ -z "$current_microcode" ] && current_microcode="Not found."
intel() {
if ! dpkg -s iucode-tool >/dev/null 2>&1; then
msg_info "Installing iucode-tool (Intel microcode updater)"
apt-get install -y iucode-tool &>/dev/null
msg_ok "Installed iucode-tool"
else
msg_ok "Intel iucode-tool is already installed"
sleep 1
fi
intel_microcode=$(curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/i/intel-microcode/" | grep -o 'href="[^"]*amd64.deb"' | sed 's/href="//;s/"//')
[ -z "$intel_microcode" ] && {
whiptail --backtitle "Proxmox Backup Server Helper Scripts" --title "No Microcode Found" --msgbox "No microcode packages were found.\nTry again later." 10 68
msg_info "Exiting"
sleep 1
msg_ok "Done"
exit
}
MICROCODE_MENU=()
MSG_MAX_LENGTH=0
while read -r TAG ITEM; do
OFFSET=2
((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=$((${#ITEM} + OFFSET))
MICROCODE_MENU+=("$TAG" "$ITEM " "OFF")
done < <(echo "$intel_microcode")
microcode=$(whiptail --backtitle "Proxmox Backup Server Helper Scripts" \
--title "Current Microcode Revision: ${current_microcode}" \
--radiolist "\nSelect a microcode package to install:\n" \
16 $((MSG_MAX_LENGTH + 58)) 6 "${MICROCODE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"')
[ -z "$microcode" ] && {
whiptail --backtitle "Proxmox Backup Server Helper Scripts" --title "No Microcode Selected" --msgbox "No microcode package was selected." 10 68
msg_info "Exiting"
sleep 1
msg_ok "Done"
exit
}
msg_info "Downloading Intel processor microcode package $microcode"
curl -fsSL "http://ftp.debian.org/debian/pool/non-free-firmware/i/intel-microcode/$microcode" -o $(basename "http://ftp.debian.org/debian/pool/non-free-firmware/i/intel-microcode/$microcode")
msg_ok "Downloaded Intel processor microcode package $microcode"
msg_info "Installing $microcode (this might take a while)"
dpkg -i $microcode &>/dev/null
msg_ok "Installed $microcode"
msg_info "Cleaning up"
rm $microcode
msg_ok "Clean up complete"
echo -e "\nA system reboot is required to apply the changes.\n"
}
amd() {
amd_microcode=$(curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/" | grep -o 'href="[^"]*amd64.deb"' | sed 's/href="//;s/"//')
[ -z "$amd_microcode" ] && {
whiptail --backtitle "Proxmox Backup Server Helper Scripts" --title "No Microcode Found" --msgbox "No microcode packages were found.\nTry again later." 10 68
msg_info "Exiting"
sleep 1
msg_ok "Done"
exit
}
MICROCODE_MENU=()
MSG_MAX_LENGTH=0
while read -r TAG ITEM; do
OFFSET=2
((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=$((${#ITEM} + OFFSET))
MICROCODE_MENU+=("$TAG" "$ITEM " "OFF")
done < <(echo "$amd_microcode")
microcode=$(whiptail --backtitle "Proxmox Backup Server Helper Scripts" \
--title "Current Microcode Revision: ${current_microcode}" \
--radiolist "\nSelect a microcode package to install:\n" \
16 $((MSG_MAX_LENGTH + 58)) 6 "${MICROCODE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"')
[ -z "$microcode" ] && {
whiptail --backtitle "Proxmox Backup Server Helper Scripts" --title "No Microcode Selected" --msgbox "No microcode package was selected." 10 68
msg_info "Exiting"
sleep 1
msg_ok "Done"
exit
}
msg_info "Downloading AMD processor microcode package $microcode"
curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/$microcode" -o $(basename "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/$microcode")
msg_ok "Downloaded AMD processor microcode package $microcode"
msg_info "Installing $microcode (this might take a while)"
dpkg -i $microcode &>/dev/null
msg_ok "Installed $microcode"
msg_info "Cleaning up"
rm $microcode
msg_ok "Clean up complete"
echo -e "\nA system reboot is required to apply the changes.\n"
}
# Check if this is a Proxmox Backup Server by verifying the presence of the datastore config.
if [ ! -f /etc/proxmox-backup/user.cfg ]; then
header_info
msg_error "Proxmox Backup Server not detected!"
exit
fi
whiptail --backtitle "Proxmox Backup Server Helper Scripts" \
--title "Proxmox Backup Server Processor Microcode" \
--yesno "This script searches for CPU microcode packages and offers the option to install them.\nProceed?" 10 68
msg_info "Checking CPU vendor"
cpu=$(lscpu | grep -oP 'Vendor ID:\s*\K\S+' | head -n 1)
if [ "$cpu" == "GenuineIntel" ]; then
msg_ok "${cpu} detected"
sleep 1
intel
elif [ "$cpu" == "AuthenticAMD" ]; then
msg_ok "${cpu} detected"
sleep 1
amd
else
msg_error "CPU vendor ${cpu} is not supported"
exit
fi
+341
View File
@@ -0,0 +1,341 @@
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/post-pbs-install.sh)"
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: tteck (tteckster) | MickLesk (CanbiZ) | thost96
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
header_info() {
clear
cat <<"EOF"
____ ____ _____ ____ __ ____ __ ____
/ __ \/ __ ) ___/ / __ \____ _____/ /_ / _/___ _____/ /_____ _/ / /
/ /_/ / __ \__ \ / /_/ / __ \/ ___/ __/ / // __ \/ ___/ __/ __ `/ / /
/ ____/ /_/ /__/ / / ____/ /_/ (__ ) /_ _/ // / / (__ ) /_/ /_/ / / /
/_/ /_____/____/ /_/ \____/____/\__/ /___/_/ /_/____/\__/\__,_/_/_/
EOF
}
RD=$(echo "\033[01;31m")
YW=$(echo "\033[33m")
GN=$(echo "\033[1;92m")
CL=$(echo "\033[m")
BFR="\\r\\033[K"
HOLD="-"
CM="${GN}${CL}"
CROSS="${RD}${CL}"
set -euo pipefail
shopt -s inherit_errexit nullglob
msg_info() { echo -ne " ${HOLD} ${YW}$1..."; }
msg_ok() { echo -e "${BFR} ${CM} ${GN}$1${CL}"; }
msg_error() { echo -e "${BFR} ${CROSS} ${RD}$1${CL}"; }
# Telemetry
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "post-pbs-install" "pve"
# ---- helpers ----
get_pbs_codename() {
awk -F'=' '/^VERSION_CODENAME=/{print $2}' /etc/os-release
}
repo_state_list() {
local repo="$1"
local file=""
local state="missing"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.list; do
[[ -f "$f" ]] || continue
if grep -q "$repo" "$f"; then
file="$f"
if grep -qE "^[^#].*${repo}" "$f"; then
state="active"
elif grep -qE "^#.*${repo}" "$f"; then
state="disabled"
fi
break
fi
done
echo "$state $file"
}
component_exists_in_sources() {
local component="$1"
grep -h -E "^[^#]*Components:[^#]*\b${component}\b" /etc/apt/sources.list.d/*.sources 2>/dev/null | grep -q .
}
require_whiptail() {
if ! command -v whiptail >/dev/null 2>&1; then
msg_error "Missing dependency: whiptail"
echo -e "Install it first (e.g. apt update && apt install -y whiptail), then re-run this script."
exit 127
fi
}
# ---- main ----
main() {
header_info
echo -e "\nThis script will Perform Post Install Routines.\n"
while true; do
read -rp "Start the Proxmox Backup Server Post Install Script (y/n)? " yn
case $yn in
[Yy]*) break ;;
[Nn]*)
clear
exit
;;
*) echo "Please answer yes or no." ;;
esac
done
if command -v pveversion >/dev/null 2>&1; then
echo -e "\n🛑 PVE Detected, Wrong Script!\n"
exit 232
fi
local CODENAME
CODENAME="$(get_pbs_codename)"
case "$CODENAME" in
bookworm)
require_whiptail
start_routines_3
;;
trixie)
require_whiptail
start_routines_4
;;
*)
msg_error "Unsupported Debian codename: $CODENAME"
echo -e "Supported: bookworm (PBS 3.x) and trixie (PBS 4.x)"
exit 105
;;
esac
}
# ---- PBS 3.x (Bookworm) ----
start_routines_3() {
header_info
local VERSION="bookworm"
# --- Debian sources ---
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PBS SOURCES" --menu \
"Correct Debian sources for Proxmox Backup Server 3.x?" 14 58 2 "yes" " " "no" " " 3>&2 2>&1 1>&3)
case $CHOICE in
yes)
msg_info "Correcting Debian Sources"
cat <<EOF >/etc/apt/sources.list
deb http://deb.debian.org/debian ${VERSION} main contrib
deb http://deb.debian.org/debian ${VERSION}-updates main contrib
deb http://security.debian.org/debian-security ${VERSION}-security main contrib
EOF
msg_ok "Corrected Debian Sources"
;;
no) msg_error "Selected no to Correcting Debian Sources" ;;
esac
# --- Enterprise repo ---
read -r state file <<<"$(repo_state_list pbs-enterprise)"
case $state in
active)
sed -i "s/^[^#].*pbs-enterprise/# &/" "$file"
msg_ok "Disabled 'pbs-enterprise' repository"
;;
disabled) msg_ok "'pbs-enterprise' already disabled" ;;
missing)
cat >/etc/apt/sources.list.d/pbs-enterprise.list <<EOF
# deb https://enterprise.proxmox.com/debian/pbs ${VERSION} pbs-enterprise
EOF
msg_ok "Added 'pbs-enterprise' repository (disabled)"
;;
esac
# --- No-subscription repo ---
read -r state file <<<"$(repo_state_list pbs-no-subscription)"
if [[ "$state" == "missing" ]]; then
cat >/etc/apt/sources.list.d/pbs-install-repo.list <<EOF
deb http://download.proxmox.com/debian/pbs ${VERSION} pbs-no-subscription
EOF
msg_ok "Enabled 'pbs-no-subscription' repository"
else
msg_ok "'pbs-no-subscription' repository already present"
fi
# --- Test repo (legacy name pbstest) ---
read -r state file <<<"$(repo_state_list pbstest)"
if [[ "$state" == "missing" ]]; then
cat >/etc/apt/sources.list.d/pbstest-for-beta.list <<EOF
# deb http://download.proxmox.com/debian/pbs ${VERSION} pbstest
EOF
msg_ok "Added 'pbstest' repository (disabled)"
else
msg_ok "'pbstest' repository already exists"
fi
post_routines_common
}
# ---- PBS 4.x (Trixie, deb822) ----
start_routines_4() {
header_info
local VERSION="trixie"
# --- Debian sources (deb822) ---
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PBS SOURCES" --menu \
"Correct Debian sources for Proxmox Backup Server 4.x (deb822)?" 14 58 2 "yes" " " "no" " " 3>&2 2>&1 1>&3)
case $CHOICE in
yes)
msg_info "Correcting Debian Sources (deb822)"
rm -f /etc/apt/sources.list.d/*.list
if [ -f /etc/apt/sources.list ]; then
sed -i '/proxmox/d;/bookworm/d' /etc/apt/sources.list
fi
cat >/etc/apt/sources.list.d/debian.sources <<EOF
Types: deb
URIs: http://deb.debian.org/debian/
Suites: trixie trixie-updates
Components: main contrib non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
Types: deb
URIs: http://security.debian.org/debian-security/
Suites: trixie-security
Components: main contrib non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
EOF
msg_ok "Corrected Debian Sources"
;;
no) msg_error "Selected no to Correcting Debian Sources" ;;
esac
# --- Enterprise repo ---
if component_exists_in_sources "pbs-enterprise"; then
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PBS Enterprise Repository" --menu \
"Enterprise repository detected.
You normally need a valid subscription for this.
Disable it (recommended)?" 14 58 2 "yes" " " "no" " " 3>&2 2>&1 1>&3)
case $CHOICE in
yes)
msg_info "Disabling 'pbs-enterprise' repository"
# Use Enabled: false instead of commenting to avoid malformed entry
if grep -q "^Enabled:" /etc/apt/sources.list.d/pbs-enterprise.sources 2>/dev/null; then
sed -i 's/^Enabled:.*/Enabled: false/' /etc/apt/sources.list.d/pbs-enterprise.sources
else
echo "Enabled: false" >>/etc/apt/sources.list.d/pbs-enterprise.sources
fi
msg_ok "Disabled 'pbs-enterprise' repository"
;;
no)
msg_error "Keeping 'pbs-enterprise' active (subscription required!)"
;;
esac
else
cat >/etc/apt/sources.list.d/pbs-enterprise.sources <<EOF
Types: deb
URIs: https://enterprise.proxmox.com/debian/pbs
Suites: trixie
Components: pbs-enterprise
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
Enabled: false
EOF
msg_ok "Added 'pbs-enterprise' repository (disabled)"
fi
# --- No-subscription repo ---
if ! component_exists_in_sources "pbs-no-subscription"; then
cat >/etc/apt/sources.list.d/proxmox.sources <<EOF
Types: deb
URIs: http://download.proxmox.com/debian/pbs
Suites: trixie
Components: pbs-no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
msg_ok "Added 'pbs-no-subscription' repository"
else
msg_ok "'pbs-no-subscription' repository already present"
fi
# --- Test repo (pbs-test, renamed) ---
if ! component_exists_in_sources "pbs-test"; then
cat >/etc/apt/sources.list.d/pbs-test.sources <<EOF
Types: deb
URIs: http://download.proxmox.com/debian/pbs
Suites: trixie
Components: pbs-test
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
Enabled: false
EOF
msg_ok "Added 'pbs-test' repository (disabled)"
else
msg_ok "'pbs-test' repository already present"
fi
post_routines_common
}
# ---- Shared routines ----
post_routines_common() {
# Subscription nag
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SUBSCRIPTION NAG" --menu \
"Disable subscription nag in PBS UI?" 14 58 2 "yes" " " "no" " " 3>&2 2>&1 1>&3)
case $CHOICE in
yes)
whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox \
"Supporting the software's development team is essential.\nPlease consider buying a subscription." 10 58
msg_info "Disabling subscription nag"
echo "DPkg::Post-Invoke { \"if [ -s /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js ] && ! grep -q -F 'NoMoreNagging' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; then sed -i '/data\\.status/{s/\\!//;s/active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; fi\" };" >/etc/apt/apt.conf.d/no-nag-script
msg_ok "Disabled subscription nag (clear browser cache!)"
;;
no)
msg_error "Selected no to Disabling subscription nag"
rm -f /etc/apt/apt.conf.d/no-nag-script 2>/dev/null
;;
esac
apt --reinstall install proxmox-widget-toolkit &>/dev/null || msg_error "Widget toolkit reinstall failed"
# Update
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "UPDATE" --menu \
"Update Proxmox Backup Server now?" 11 58 2 "yes" " " "no" " " 3>&2 2>&1 1>&3)
case $CHOICE in
yes)
msg_info "Updating Proxmox Backup Server (Patience)"
apt update &>/dev/null || msg_error "apt update failed"
apt -y dist-upgrade &>/dev/null || msg_error "apt dist-upgrade failed"
msg_ok "Updated Proxmox Backup Server"
;;
no) msg_error "Selected no to updating Proxmox Backup Server" ;;
esac
# Reminder
whiptail --backtitle "Proxmox VE Helper Scripts" --title "Post-Install Reminder" --msgbox \
"IMPORTANT:
Please run this script on every PBS node individually if you have multiple nodes.
After completing these steps, it is strongly recommended to REBOOT your node.
After the upgrade or post-install routines, always clear your browser cache or perform a hard reload (Ctrl+Shift+R) before using the PBS Web UI to avoid UI display issues." 20 80
# Reboot
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "REBOOT" --menu \
"Reboot Proxmox Backup Server now? (recommended)" 11 58 2 "yes" " " "no" " " 3>&2 2>&1 1>&3)
case $CHOICE in
yes)
msg_info "Rebooting PBS"
sleep 2
msg_ok "Completed Post Install Routines"
reboot
;;
no)
msg_error "Selected no to Reboot (Reboot recommended)"
msg_ok "Completed Post Install Routines"
;;
esac
}
main
+49
View File
@@ -0,0 +1,49 @@
#pve
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/proxmox-backup-server.sh)"
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 tteck
# Author: tteck (tteckster)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://www.proxmox.com/en/proxmox-backup-server
APP="Proxmox-Backup-Server"
var_tags="${var_tags:-backup}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-2048}"
var_disk="${var_disk:-10}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_arm64="${var_arm64:-no}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -e /usr/sbin/proxmox-backup-manager ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
msg_info "Updating $APP LXC"
$STD apt update
$STD apt -y upgrade
msg_ok "Updated $APP LXC"
msg_ok "Updated successfully!"
exit
}
start
build_container
description
msg_ok "Completed successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8007${CL}"
+191
View File
@@ -0,0 +1,191 @@
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/pbs4-upgrade.sh)"
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
header_info() {
clear
cat <<"EOF"
____ ____ _____ __ __ __ __ __
/ __ \/ __ ) ___// // / / / / /___ ____ __________ _____/ /__
/ /_/ / __ \__ \/ // /_ / / / / __ \/ __ `/ ___/ __ `/ __ / _ \
/ ____/ /_/ /__/ /__ __/ / /_/ / /_/ / /_/ / / / /_/ / /_/ / __/
/_/ /_____/____/ /_/ \____/ .___/\__, /_/ \__,_/\__,_/\___/
/_/ /____/
EOF
}
RD=$(echo "\033[01;31m")
YW=$(echo "\033[33m")
GN=$(echo "\033[1;92m")
CL=$(echo "\033[m")
BFR="\\r\\033[K"
HOLD="-"
CM="${GN}${CL}"
CROSS="${RD}${CL}"
set -euo pipefail
shopt -s inherit_errexit nullglob
msg_info() { echo -ne " ${HOLD} ${YW}$1..."; }
msg_ok() { echo -e "${BFR} ${CM} ${GN}$1${CL}"; }
msg_error() { echo -e "${BFR} ${CROSS} ${RD}$1${CL}"; }
# Telemetry
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "pbs4-upgrade" "pve"
start_routines() {
header_info
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PBS 3 BACKUP" --menu \
"\nMake a backup of /etc/proxmox-backup to ensure recovery in worst case?" 14 58 2 \
"yes" " " "no" " " 3>&2 2>&1 1>&3)
case $CHOICE in
yes)
msg_info "Backing up Proxmox Backup Server 3"
tar czf "pbs3-etc-backup-$(date -I).tar.gz" -C "/etc" "proxmox-backup"
msg_ok "Backed up Proxmox Backup Server 3"
;;
no) msg_error "Selected no to Backup" ;;
esac
# --- Debian 13 Sources ---
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PBS 4 SOURCES" --menu \
"Switch to Debian 13 (Trixie) sources for PBS 4?" 14 58 2 "yes" " " "no" " " 3>&2 2>&1 1>&3)
case $CHOICE in
yes)
msg_info "Switching to Debian 13 (Trixie) Sources"
rm -f /etc/apt/sources.list.d/*.list
if [ -f /etc/apt/sources.list ]; then
sed -i '/proxmox/d;/bookworm/d' /etc/apt/sources.list
fi
cat >/etc/apt/sources.list.d/debian.sources <<EOF
Types: deb
URIs: http://deb.debian.org/debian
Suites: trixie
Components: main contrib
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
Types: deb
URIs: http://security.debian.org/debian-security
Suites: trixie-security
Components: main contrib
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
Types: deb
URIs: http://deb.debian.org/debian
Suites: trixie-updates
Components: main contrib
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
EOF
msg_ok "Configured Debian 13 (Trixie) Sources"
;;
no) msg_error "Selected no to Sources update" ;;
esac
# --- Enterprise Repo ---
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PBS4-ENTERPRISE" --menu \
"Add 'pbs-enterprise' repository (for subscription users)?" 14 58 2 "yes" " " "no" " " \
3>&2 2>&1 1>&3)
case $CHOICE in
yes)
msg_info "Adding 'pbs-enterprise' repository"
cat >/etc/apt/sources.list.d/pbs-enterprise.sources <<EOF
Types: deb
URIs: https://enterprise.proxmox.com/debian/pbs
Suites: trixie
Components: pbs-enterprise
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
msg_ok "Added 'pbs-enterprise' repository"
;;
no) msg_error "Skipped enterprise repo" ;;
esac
# --- No-Subscription Repo ---
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PBS4-NO-SUBSCRIPTION" --menu \
"Enable 'pbs-no-subscription' repository?" 14 58 2 "yes" " " "no" " " \
3>&2 2>&1 1>&3)
case $CHOICE in
yes)
msg_info "Adding 'pbs-no-subscription' repository"
cat >/etc/apt/sources.list.d/proxmox.sources <<EOF
Types: deb
URIs: http://download.proxmox.com/debian/pbs
Suites: trixie
Components: pbs-no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
msg_ok "Added 'pbs-no-subscription' repository"
;;
no) msg_error "Skipped no-subscription repo" ;;
esac
# --- Test Repo ---
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PBS4 TEST" --menu \
"Add 'pbs-test' repository (disabled by default)?" 14 58 2 "yes" " " "no" " " \
3>&2 2>&1 1>&3)
case $CHOICE in
yes)
msg_info "Adding 'pbs-test' repository (disabled)"
cat >/etc/apt/sources.list.d/pbs-test.sources <<EOF
# Types: deb
# URIs: http://download.proxmox.com/debian/pbs
# Suites: trixie
# Components: pbs-test
# Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
msg_ok "Added 'pbs-test' repository"
;;
no) msg_error "Skipped test repo" ;;
esac
# --- Upgrade ---
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PBS 4 UPGRADE" --menu \
"\nUpgrade to Proxmox Backup Server 4 now?" 11 58 2 "yes" " " "no" " " \
3>&2 2>&1 1>&3)
case $CHOICE in
yes)
msg_info "Upgrading to Proxmox Backup Server 4 (Patience)"
apt update
DEBIAN_FRONTEND=noninteractive apt -o Dpkg::Options::="--force-confold" dist-upgrade -y
msg_ok "System upgraded to PBS 4"
;;
no) msg_error "Selected no to upgrade" ;;
esac
# --- Reboot ---
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "REBOOT" --menu \
"\nReboot Proxmox Backup Server 4 now? (recommended)" 11 58 2 "yes" " " "no" " " \
3>&2 2>&1 1>&3)
case $CHOICE in
yes)
msg_info "Rebooting PBS 4"
sleep 2
msg_ok "Upgrade Complete"
reboot
;;
no)
msg_error "Selected no to Reboot (Reboot recommended)"
msg_ok "Upgrade Complete"
;;
esac
}
header_info
while true; do
read -rp "Start the Upgrade to Proxmox Backup Server 4 Script (y/n)? " yn
case $yn in
[Yy]*) break ;;
[Nn]*)
clear
exit
;;
*) echo "Please answer yes or no." ;;
esac
done
start_routines