From ba87729be7fa270c6331f78c709f4800095cf526 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 17 Sep 2026 13:39:36 +0200 Subject: [PATCH 01/10] sparkyfitness: run the Better Auth migration during the update (#17318) * sparkyfitness: run the Better Auth migration during the update 1.7.1 ships a Better Auth version whose schema has columns 1.6.5 never created, and nothing in the update adds them. The backend starts, then refuses every sign-in: Database schema mismatch Missing columns session.impersonated_by two_factor.verified ... Run `npx auth migrate` to add the missing tables and columns. Run exactly that after the backend dependencies are in place, with the config Better Auth looks for, SparkyFitnessServer/auth.ts, and the database credentials from /etc/sparkyfitness/.env. Users who hit this were left running the server by hand to get the columns created. A failure warns instead of aborting: the rest of the update has already succeeded at that point, and the message names the remaining step. * sparkyfitness: start the server through the entrypoint that migrates The Better Auth CLI added in the previous commit is the wrong tool. It generates sso_provider.user_id as text and cannot reference this schema's uuid user.id: foreign key constraint "sso_provider_user_id_fkey" cannot be implemented [...] incompatible types: text and uuid The real cause is one line up in the unit. It ran tsx SparkyFitnessServer.js which imports the application module directly and never applies the schema migrations. Upstream starts through index.ts - nodemon.json has exec: tsx index.ts - and that file runs applyMigrations() and applyRlsPolicies() before importing anything, with a comment naming this exact failure: Better Auth validates the database schema eagerly, the moment betterAuth() is constructed at auth.ts module scope [...] When migrations ran later (from inside SparkyFitnessServer.ts) that check read the pre-migration schema on the first boot after an upgrade, so every /api/auth request failed until the container was restarted. Point both the install and the update at index.ts and drop the CLI call. index.ts exits non-zero when a migration fails, so systemd surfaces that instead of serving a broken login. --- ct/sparkyfitness.sh | 2 +- install/sparkyfitness-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/sparkyfitness.sh b/ct/sparkyfitness.sh index 0daa9888b..0ce3e2a0f 100644 --- a/ct/sparkyfitness.sh +++ b/ct/sparkyfitness.sh @@ -86,7 +86,7 @@ function update_script() { Type=simple WorkingDirectory=/opt/sparkyfitness/SparkyFitnessServer EnvironmentFile=/etc/sparkyfitness/.env - ExecStart=/opt/sparkyfitness/SparkyFitnessServer/node_modules/.bin/tsx SparkyFitnessServer.js + ExecStart=/opt/sparkyfitness/SparkyFitnessServer/node_modules/.bin/tsx index.ts Restart=always RestartSec=5 diff --git a/install/sparkyfitness-install.sh b/install/sparkyfitness-install.sh index 0d04252de..ffb01de59 100644 --- a/install/sparkyfitness-install.sh +++ b/install/sparkyfitness-install.sh @@ -70,7 +70,7 @@ Requires=postgresql.service Type=simple WorkingDirectory=/opt/sparkyfitness/SparkyFitnessServer EnvironmentFile=/etc/sparkyfitness/.env -ExecStart=/opt/sparkyfitness/SparkyFitnessServer/node_modules/.bin/tsx SparkyFitnessServer.js +ExecStart=/opt/sparkyfitness/SparkyFitnessServer/node_modules/.bin/tsx index.ts Restart=always RestartSec=5 From e55a4f64909f47fa3fcbe3ca21a005eaf73f4dc5 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 11:40:03 +0000 Subject: [PATCH 02/10] Update CHANGELOG.md (#17322) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d7a28f0d..c820efad5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -549,6 +549,7 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit - #### šŸž Bug Fixes + - sparkyfitness: run the Better Auth migration during the update [@MickLesk](https://github.com/MickLesk) ([#17318](https://github.com/community-scripts/ProxmoxVE/pull/17318)) - poznote: follow init.sh into docker/ [@MickLesk](https://github.com/MickLesk) ([#17316](https://github.com/community-scripts/ProxmoxVE/pull/17316)) ## 2026-09-16 From 12afe05341271f5b41e49141d1231c6dfe924331 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 11:42:28 +0000 Subject: [PATCH 03/10] Update CHANGELOG.md (#17323) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c820efad5..e7cb3db87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -552,6 +552,10 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit - sparkyfitness: run the Better Auth migration during the update [@MickLesk](https://github.com/MickLesk) ([#17318](https://github.com/community-scripts/ProxmoxVE/pull/17318)) - poznote: follow init.sh into docker/ [@MickLesk](https://github.com/MickLesk) ([#17316](https://github.com/community-scripts/ProxmoxVE/pull/17316)) +### šŸ’¾ Core + + - Ask before replacing a cached image that no longer matches [@MickLesk](https://github.com/MickLesk) ([core#44](https://github.com/community-scripts/core/pull/44)) + ## 2026-09-16 ### šŸš€ Updated Scripts From 50024dd48d75bdc8c27edafd29ec62d85bebbb17 Mon Sep 17 00:00:00 2001 From: "push-app-to-main[bot]" <203845782+push-app-to-main[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 14:00:21 +0200 Subject: [PATCH 04/10] Allstarlink (#17321) * Add allstarlink-vm (vm) * Add MickLesk as co-author in allstarlink-vm.sh Updated author information to include MickLesk. --------- Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com> Co-authored-by: CanbiZ (MickLesk) <47820557+MickLesk@users.noreply.github.com> --- vm/allstarlink-vm.sh | 203 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 vm/allstarlink-vm.sh diff --git a/vm/allstarlink-vm.sh b/vm/allstarlink-vm.sh new file mode 100644 index 000000000..089134e81 --- /dev/null +++ b/vm/allstarlink-vm.sh @@ -0,0 +1,203 @@ +#!/usr/bin/env bash +# Copyright (c) 2021-2026 community-scripts ORG +# Author: Don Locke (DonLocke) | MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/AllStarLink + +COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main}" +source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func") +load_functions + +GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') +NEXTID=$(pvesh get /cluster/nextid) +RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" +METHOD="" +APP="AllStarLink" +APP_TYPE="vm" +NSAPP="allstarlink-vm" +var_os="debian" +var_version="13" +DISK_SIZE="8G" + +HA=$(echo "\033[1;34m") +THIN="discard=on,ssd=1," + +header_info +echo -e "\n Loading..." +set -e +trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +trap cleanup EXIT +trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT +trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM + +TEMP_DIR=$(mktemp -d) +pushd $TEMP_DIR >/dev/null + +if [[ "${VM_UNATTENDED:-0}" == "1" ]]; then + var_version="${VM_OS_VERSION:-$var_version}" +elif vm_dialog radiolist "DEBIAN BASE" "Choose the Debian release AllStarLink runs on" --cancel-button Exit-Script 11 60 2 \ + "13" "Debian 13 (Trixie)" ON \ + "12" "Debian 12 (Bookworm)" OFF; then + var_version="$VM_DIALOG_RESULT" +else + exit_script +fi + +case "$var_version" in +13) DEBIAN_CODENAME="trixie" ;; +12) DEBIAN_CODENAME="bookworm" ;; +*) + msg_error "AllStarLink only publishes packages for Debian 12 and 13 (got '${var_version}')" + exit 1 + ;; +esac + +function default_settings() { + vm_apply_machine_type "i440fx" + VMID="$NEXTID" + DISK_CACHE="" + HN="allstarlink" + CPU_TYPE="" + CORE_COUNT="2" + RAM_SIZE="2048" + BRG="vmbr0" + MAC="$GEN_MAC" + VLAN="" + MTU="" + START_VM="yes" + METHOD="default" + vm_echo_default_settings +} + +function advanced_settings() { + METHOD="advanced" + vm_prompt_vmid "${VMID:-$(get_valid_nextid)}" + vm_prompt_machine_type "i440fx" + vm_prompt_disk_size "8G" + vm_prompt_disk_cache "none" + vm_prompt_hostname "allstarlink" + vm_prompt_cpu_model "kvm64" + vm_prompt_cpu_cores "2" + vm_prompt_ram "2048" + vm_prompt_bridge "vmbr0" + vm_prompt_mac "$GEN_MAC" + vm_prompt_vlan + vm_prompt_mtu + vm_prompt_verbose "no" + vm_prompt_start_vm "yes" + + if vm_confirm_advanced_settings "Ready to create a AllStarLink VM?"; then + echo -e "${CREATING}${BOLD}${DGN}Creating a AllStarLink VM using the above advanced settings${CL}" + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi +} + + +vm_preflight +vm_start_script "Use Default Settings?" 10 58 +post_to_api_vm + +vm_select_storage "$HN" +msg_info "Retrieving the URL for the Debian ${var_version} Qcow2 Disk Image" +URL="https://cloud.debian.org/images/cloud/${DEBIAN_CODENAME}/latest/debian-${var_version}-nocloud-$(dpkg --print-architecture).qcow2" +sleep 2 +msg_ok "${CL}${BL}${URL}${CL}" +CACHE_FILE="$(vm_image_cache_path "$URL")" +vm_fetch_image "$URL" "$CACHE_FILE" --cache --min-bytes $((100 * 1024 * 1024)) || exit 115 +FILE="$(basename "$CACHE_FILE")" +# Work on a copy: the expand and virt-customize steps below rewrite the image, +# which would poison the cache for every later VM. +cp -f "$CACHE_FILE" "$FILE" + +# qm resize only grows the block device. Without cloud-init nothing grows the +# guest partition, so expand it offline first. +if [ "${CLOUD_INIT:-no}" != "yes" ]; then + msg_info "Expanding the root filesystem to ${DISK_SIZE}" + vm_expand_image "$FILE" "$DISK_SIZE" || true +fi + +STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') +case $STORAGE_TYPE in +nfs | dir) + DISK_EXT=".qcow2" + DISK_REF="$VMID/" + DISK_IMPORT="-format qcow2" + THIN="" + ;; +btrfs) + DISK_EXT=".raw" + DISK_REF="$VMID/" + DISK_IMPORT="-format raw" + FORMAT=",efitype=4m" + THIN="" + ;; +esac +for i in {0,1}; do + disk="DISK$i" + eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-} + eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} +done + +msg_info "Installing Pre-Requisite libguestfs-tools onto Host" +$STD apt-get update +$STD apt-get install -y libguestfs-tools lsb-release +msg_ok "Installed libguestfs-tools successfully" + +msg_info "Adding ASL Package Repository" +virt-customize -q -a "${FILE}" \ + --run-command "curl -fsSL https://repo.allstarlink.org/public/asl-apt-repos.deb${var_version}_all.deb -o /tmp/asl-apt-repos.deb${var_version}_all.deb" \ + --run-command "dpkg -i /tmp/asl-apt-repos.deb${var_version}_all.deb" \ + --update \ + --run-command "rm -f /tmp/asl-apt-repos.deb${var_version}_all.deb" >/dev/null +msg_ok "Added ASL Package Repository" + +msg_info "Installing AllStarLink (patience)" +virt-customize -q -a "${FILE}" \ + --install asl3 \ + --run-command "sed -i \"/secret /s/= .*/= $(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)/\" /etc/asterisk/manager.conf" >/dev/null +msg_ok "Installed AllStarLink" + +vm_prepare_cloud_image "$FILE" "$HN" || true + +if [[ "${VM_UNATTENDED:-0}" == "1" ]]; then + ADD_ALLMON3="${VM_ALLMON3:-no}" +elif vm_dialog yesno "SETTINGS" "Would you like to add Allmon3?" 10 58; then + ADD_ALLMON3="yes" +else + ADD_ALLMON3="no" +fi + +if [[ "$ADD_ALLMON3" == "yes" ]]; then + msg_info "Installing Allmon3" + virt-customize -q -a "${FILE}" \ + --install allmon3 \ + --run-command "sed -i \"s/;pass=.*/;pass=\$(sed -ne 's/^secret = //p' /etc/asterisk/manager.conf)/\" /etc/allmon3/allmon3.ini" >/dev/null + msg_ok "Installed Allmon3" +fi + +msg_info "Creating a AllStarLink VM" +qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ + -name $HN -tags community-script,debian${var_version},radio -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci +pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null +qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null +qm set $VMID \ + -efidisk0 ${DISK0_REF}${FORMAT} \ + -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=2G \ + -boot order=scsi0 \ + -serial0 socket >/dev/null +vm_resize_disk +qm set $VMID --agent enabled=1 >/dev/null + +set_description + +msg_ok "Created a AllStarLink VM ${CL}${BL}(${HN})" +if [ "$START_VM" == "yes" ]; then + msg_info "Starting AllStarLink VM" + $STD qm start $VMID + msg_ok "Started AllStarLink VM" +fi +post_update_to_api "done" "none" +msg_ok "Completed successfully!\n" From d7f036f98af7e7b0f101e51f6780a1a2aa1f585a Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 12:00:49 +0000 Subject: [PATCH 05/10] Update CHANGELOG.md (#17326) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7cb3db87..53525df3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -545,6 +545,10 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit ## 2026-09-17 +### šŸ†• New Scripts + + - Allstarlink ([#17321](https://github.com/community-scripts/ProxmoxVE/pull/17321)) + ### šŸš€ Updated Scripts - #### šŸž Bug Fixes From 17556f06bdf9e373c12f21560986eac2e1d19664 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 14:01:09 +0200 Subject: [PATCH 06/10] Update .app files (#17327) Co-authored-by: GitHub Actions --- vm/headers/allstarlink | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 vm/headers/allstarlink diff --git a/vm/headers/allstarlink b/vm/headers/allstarlink new file mode 100644 index 000000000..8190b681e --- /dev/null +++ b/vm/headers/allstarlink @@ -0,0 +1,6 @@ + ___ _________ __ __ _ __ + / | / / / ___// /_____ ______/ / (_)___ / /__ + / /| | / / /\__ \/ __/ __ `/ ___/ / / / __ \/ //_/ + / ___ |/ / /___/ / /_/ /_/ / / / /___/ / / / / ,< +/_/ |_/_/_//____/\__/\__,_/_/ /_____/_/_/ /_/_/|_| + From e24f6baa0597f0e067691ea92595007f2bd6598f Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 17 Sep 2026 14:15:45 +0200 Subject: [PATCH 07/10] Debian-VM: Refactor | Support Debian 11, 12, 13 (#17325) * Debian-VM: Refactor | Support Debian 11, 12, 13 * remove deprecated debian 13 vm * Update Debian VM script to use architecture variable (arm64 support) --- vm/debian-13-vm.sh | 677 --------------------------------------------- vm/debian-vm.sh | 635 ++++++++---------------------------------- 2 files changed, 119 insertions(+), 1193 deletions(-) delete mode 100644 vm/debian-13-vm.sh diff --git a/vm/debian-13-vm.sh b/vm/debian-13-vm.sh deleted file mode 100644 index 8a756bcb8..000000000 --- a/vm/debian-13-vm.sh +++ /dev/null @@ -1,677 +0,0 @@ -#!/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 - -source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) - -function header_info { - clear - cat <<"EOF" - ____ __ _ ________ - / __ \___ / /_ (_)___ _____ < /__ / - / / / / _ \/ __ \/ / __ `/ __ \ / / /_ < - / /_/ / __/ /_/ / / /_/ / / / / / /___/ / -/_____/\___/_.___/_/\__,_/_/ /_/ /_//____/ - (Trixie) -EOF -} -header_info -echo -e "\n Loading..." -GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') -RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" -METHOD="" -NSAPP="debian-13-vm" -var_os="debian" -var_version="13" - -YW=$(echo "\033[33m") -BL=$(echo "\033[36m") -RD=$(echo "\033[01;31m") -BGN=$(echo "\033[4;92m") -GN=$(echo "\033[1;92m") -DGN=$(echo "\033[32m") -CL=$(echo "\033[m") - -CL=$(echo "\033[m") -BOLD=$(echo "\033[1m") -BFR="\\r\\033[K" -HOLD=" " -TAB=" " - -CM="${TAB}āœ”ļø${TAB}${CL}" -CROSS="${TAB}āœ–ļø${TAB}${CL}" -INFO="${TAB}šŸ’”${TAB}${CL}" -OS="${TAB}šŸ–„ļø${TAB}${CL}" -CONTAINERTYPE="${TAB}šŸ“¦${TAB}${CL}" -DISKSIZE="${TAB}šŸ’¾${TAB}${CL}" -CPUCORE="${TAB}🧠${TAB}${CL}" -RAMSIZE="${TAB}šŸ› ļø${TAB}${CL}" -CONTAINERID="${TAB}šŸ†”${TAB}${CL}" -HOSTNAME="${TAB}šŸ ${TAB}${CL}" -BRIDGE="${TAB}šŸŒ‰${TAB}${CL}" -GATEWAY="${TAB}🌐${TAB}${CL}" -DEFAULT="${TAB}āš™ļø${TAB}${CL}" -MACADDRESS="${TAB}šŸ”—${TAB}${CL}" -VLANTAG="${TAB}šŸ·ļø${TAB}${CL}" -CREATING="${TAB}šŸš€${TAB}${CL}" -ADVANCED="${TAB}🧩${TAB}${CL}" -CLOUD="${TAB}ā˜ļø${TAB}${CL}" - -THIN="discard=on,ssd=1," -set -e -trap 'error_handler $LINENO "$BASH_COMMAND"' ERR -trap cleanup EXIT -trap 'post_update_to_api "failed" "130"' SIGINT -trap 'post_update_to_api "failed" "143"' SIGTERM -trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP -function error_handler() { - local exit_code="$?" - local line_number="$1" - local command="$2" - local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" - post_update_to_api "failed" "${exit_code}" - echo -e "\n$error_message\n" - cleanup_vmid -} - -function get_valid_nextid() { - local try_id - try_id=$(pvesh get /cluster/nextid) - while true; do - if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then - try_id=$((try_id + 1)) - continue - fi - if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then - try_id=$((try_id + 1)) - continue - fi - break - done - echo "$try_id" -} - -function cleanup_vmid() { - if qm status $VMID &>/dev/null; then - qm stop $VMID &>/dev/null - qm destroy $VMID &>/dev/null - fi -} - -function cleanup() { - local exit_code=$? - popd >/dev/null - if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then - if [[ $exit_code -eq 0 ]]; then - post_update_to_api "done" "none" - else - post_update_to_api "failed" "$exit_code" - fi - fi - rm -rf $TEMP_DIR -} - -TEMP_DIR=$(mktemp -d) -pushd $TEMP_DIR >/dev/null -if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Debian 13 VM" --yesno "This will create a New Debian 13 VM. Proceed?" 10 58; then - : -else - header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit -fi - -function msg_info() { - local msg="$1" - echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" -} - -function msg_ok() { - local msg="$1" - echo -e "${BFR}${CM}${GN}${msg}${CL}" -} - -function msg_error() { - local msg="$1" - echo -e "${BFR}${CROSS}${RD}${msg}${CL}" -} - -function check_root() { - if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then - clear - msg_error "Please run this script as root." - echo -e "\nExiting..." - sleep 2 - exit - fi -} - -# 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, 9.0 and 9.2 -pve_check() { - local PVE_VER - PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - - # Check for Proxmox VE 8.x: allow 8.0–8.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.0 and 9.2 - if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then - local MINOR="${BASH_REMATCH[1]}" - if ((MINOR < 0 || MINOR > 2)); then - msg_error "This version of Proxmox VE is not supported." - msg_error "Supported: Proxmox VE version 9.0 – 9.2" - exit 105 - fi - 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.x or 9.0 – 9.2" - exit 105 -} - -function arch_check() { - if [ "$(dpkg --print-architecture)" != "amd64" ]; then - echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" - echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" - echo -e "Exiting..." - sleep 2 - exit - fi -} - -function ssh_check() { - if command -v pveversion >/dev/null 2>&1; then - if [ -n "${SSH_CLIENT:+x}" ]; then - if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then - echo "you've been warned" - else - clear - exit - fi - fi - fi -} - -function exit-script() { - clear - echo -e "\n${CROSS}${RD}User exited script${CL}\n" - exit -} - -function select_cloud_init() { - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "CLOUD-INIT" \ - --yesno "Enable Cloud-Init for VM configuration?\n\nCloud-Init allows automatic configuration of:\n- User accounts and passwords\n- SSH keys\n- Network settings (DHCP/Static)\n- DNS configuration\n\nYou can also configure these settings later in Proxmox UI.\n\nNote: Without Cloud-Init, the nocloud image will be used with console auto-login." --defaultno 18 68); then - CLOUD_INIT="yes" - echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}yes${CL}" - else - CLOUD_INIT="no" - echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}no${CL}" - fi -} - -function default_settings() { - VMID=$(get_valid_nextid) - FORMAT=",efitype=4m" - MACHINE="" - DISK_SIZE="8G" - DISK_CACHE="" - HN="debian" - CPU_TYPE="" - CORE_COUNT="2" - RAM_SIZE="2048" - BRG="vmbr0" - MAC="$GEN_MAC" - VLAN="" - MTU="" - START_VM="yes" - METHOD="default" - echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" - echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" - echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" - select_cloud_init - echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" - echo -e "${CREATING}${BOLD}${DGN}Creating a Debian 13 VM using the above default settings${CL}" -} - -function advanced_settings() { - METHOD="advanced" - [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) - while true; do - if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 "$VMID" --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$VMID" ]; then - VMID=$(get_valid_nextid) - fi - if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then - echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" - sleep 2 - continue - fi - echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" - break - else - exit-script - fi - done - - if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ - "i440fx" "Machine i440fx" ON \ - "q35" "Machine q35" OFF \ - 3>&1 1>&2 2>&3); then - if [ "$MACH" = q35 ]; then - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" - FORMAT="" - MACHINE=" -machine q35" - else - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" - FORMAT=",efitype=4m" - MACHINE="" - fi - else - exit-script - fi - - if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') - if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then - DISK_SIZE="${DISK_SIZE}G" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" - elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" - else - echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" - exit-script - fi - else - exit-script - fi - - if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ - "0" "None (Default)" ON \ - "1" "Write Through" OFF \ - 3>&1 1>&2 2>&3); then - if [ "$DISK_CACHE" = "1" ]; then - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" - DISK_CACHE="cache=writethrough," - else - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" - DISK_CACHE="" - fi - else - exit-script - fi - - if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 debian --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$VM_NAME" ]; then - HN="debian" - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" - else - HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') - if [ "$HN" != "${VM_NAME,,}" ]; then - whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 - fi - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" - fi - else - exit-script - fi - - if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ - "0" "KVM64 (Default)" ON \ - "1" "Host" OFF \ - 3>&1 1>&2 2>&3); then - if [ "$CPU_TYPE1" = "1" ]; then - echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" - CPU_TYPE=" -cpu host" - else - echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" - CPU_TYPE="" - fi - else - exit-script - fi - - while true; do - if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi - if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" - break - fi - whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58 - else - exit-script - fi - done - - while true; do - if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048"; fi - if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" - break - fi - whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58 - else - exit-script - fi - done - - if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$BRG" ]; then - BRG="vmbr0" - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - else - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - fi - else - exit-script - fi - - while true; do - if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 "$GEN_MAC" --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$MAC1" ]; then - MAC="$GEN_MAC" - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" - break - fi - if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then - MAC="$MAC1" - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" - break - fi - whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58 - else - exit-script - fi - done - - while true; do - if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$VLAN1" ]; then - VLAN1="Default" - VLAN="" - echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" - break - fi - if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then - VLAN=",tag=$VLAN1" - echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" - break - fi - whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 - else - exit-script - fi - done - - while true; do - if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$MTU1" ]; then - MTU1="Default" - MTU="" - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" - break - fi - if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then - MTU=",mtu=$MTU1" - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" - break - fi - whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 - else - exit-script - fi - done - - select_cloud_init - - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then - echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" - START_VM="yes" - else - echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" - START_VM="no" - fi - - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Debian 13 VM?" --no-button Do-Over 10 58); then - echo -e "${CREATING}${BOLD}${DGN}Creating a Debian 13 VM using the above advanced settings${CL}" - else - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" - advanced_settings - fi -} - -function start_script() { - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" - default_settings - else - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" - advanced_settings - fi -} - -check_root -arch_check -pve_check -ssh_check -start_script - -post_to_api_vm - -msg_info "Validating Storage" -while read -r line; do - TAG=$(echo "$line" | awk '{print $1}') - TYPE=$(echo "$line" | awk '{printf "%-10s", $2}') - FREE=$(echo "$line" | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') - ITEM=" Type: $TYPE Free: $FREE " - OFFSET=2 - if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then - MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) - fi - STORAGE_MENU+=("$TAG" "$ITEM" "OFF") -done < <(pvesm status -content images | awk 'NR>1') -VALID=$(pvesm status -content images | awk 'NR>1') -if [ -z "$VALID" ]; then - msg_error "Unable to detect a valid storage location." - exit -elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then - STORAGE=${STORAGE_MENU[0]} -else - while [ -z "${STORAGE:+x}" ]; do - STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ - "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ - 16 $(($MSG_MAX_LENGTH + 23)) 6 \ - "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) - done -fi -msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." -msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." - -# ============================================================================== -# PREREQUISITES -# ============================================================================== -if ! command -v virt-customize &>/dev/null; then - msg_info "Installing libguestfs-tools" - apt-get update >/dev/null 2>&1 - apt-get install -y libguestfs-tools >/dev/null 2>&1 - msg_ok "Installed libguestfs-tools" -fi - -msg_info "Retrieving the URL for the Debian 13 Qcow2 Disk Image" -if [ "$CLOUD_INIT" == "yes" ]; then - URL=https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2 -else - URL=https://cloud.debian.org/images/cloud/trixie/latest/debian-13-nocloud-amd64.qcow2 -fi -sleep 2 -msg_ok "${CL}${BL}${URL}${CL}" -curl -f#SL -o "$(basename "$URL")" "$URL" -echo -en "\e[1A\e[0K" -FILE=$(basename $URL) -msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" - -# ============================================================================== -# IMAGE CUSTOMIZATION -# ============================================================================== -msg_info "Customizing ${FILE} image" - -WORK_FILE=$(mktemp --suffix=.qcow2) -cp "$FILE" "$WORK_FILE" - -# Set hostname -virt-customize -q -a "$WORK_FILE" --hostname "${HN}" >/dev/null 2>&1 - -# Prepare for unique machine-id on first boot -virt-customize -q -a "$WORK_FILE" --run-command "truncate -s 0 /etc/machine-id" >/dev/null 2>&1 -virt-customize -q -a "$WORK_FILE" --run-command "rm -f /var/lib/dbus/machine-id" >/dev/null 2>&1 - -# Disable systemd-firstboot to prevent interactive prompts blocking the console -virt-customize -q -a "$WORK_FILE" --run-command "systemctl disable systemd-firstboot.service 2>/dev/null; rm -f /etc/systemd/system/sysinit.target.wants/systemd-firstboot.service; ln -sf /dev/null /etc/systemd/system/systemd-firstboot.service" >/dev/null 2>&1 || true - -# Pre-seed firstboot settings so it won't prompt even if triggered -virt-customize -q -a "$WORK_FILE" --run-command "echo 'Etc/UTC' > /etc/timezone && ln -sf /usr/share/zoneinfo/Etc/UTC /etc/localtime" >/dev/null 2>&1 || true -virt-customize -q -a "$WORK_FILE" --run-command "touch /etc/locale.conf" >/dev/null 2>&1 || true - -if [ "$CLOUD_INIT" == "yes" ]; then - # Cloud-Init handles SSH and login - virt-customize -q -a "$WORK_FILE" --run-command "sed -i 's/^#*PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config" >/dev/null 2>&1 || true - virt-customize -q -a "$WORK_FILE" --run-command "sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config" >/dev/null 2>&1 || true -else - # Configure auto-login on serial console (ttyS0) and virtual console (tty1) - virt-customize -q -a "$WORK_FILE" --run-command "mkdir -p /etc/systemd/system/serial-getty@ttyS0.service.d" >/dev/null 2>&1 || true - virt-customize -q -a "$WORK_FILE" --run-command 'cat > /etc/systemd/system/serial-getty@ttyS0.service.d/autologin.conf << EOF -[Service] -ExecStart= -ExecStart=-/sbin/agetty --autologin root --noclear %I \$TERM -EOF' >/dev/null 2>&1 || true - virt-customize -q -a "$WORK_FILE" --run-command "mkdir -p /etc/systemd/system/getty@tty1.service.d" >/dev/null 2>&1 || true - virt-customize -q -a "$WORK_FILE" --run-command 'cat > /etc/systemd/system/getty@tty1.service.d/autologin.conf << EOF -[Service] -ExecStart= -ExecStart=-/sbin/agetty --autologin root --noclear %I \$TERM -EOF' >/dev/null 2>&1 || true -fi - -msg_ok "Customized image" - -STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}') -case $STORAGE_TYPE in -nfs | dir) - DISK_EXT=".qcow2" - DISK_REF="$VMID/" - DISK_IMPORT="-format qcow2" - THIN="" - ;; -btrfs) - DISK_EXT=".raw" - DISK_REF="$VMID/" - DISK_IMPORT="-format raw" - FORMAT=",efitype=4m" - THIN="" - ;; -*) - DISK_EXT="" - DISK_REF="" - DISK_IMPORT="-format raw" - ;; -esac -for i in {0,1}; do - disk="DISK$i" - eval DISK"${i}"=vm-"${VMID}"-disk-"${i}"${DISK_EXT:-} - eval DISK"${i}"_REF="${STORAGE}":"${DISK_REF:-}"${!disk} -done - -msg_info "Creating a Debian 13 VM" -qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ - -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci -pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null -qm importdisk $VMID ${WORK_FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null -if [ "$CLOUD_INIT" == "yes" ]; then - qm set $VMID \ - -efidisk0 ${DISK0_REF}${FORMAT} \ - -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ - -scsi1 ${STORAGE}:cloudinit \ - -boot order=scsi0 \ - -serial0 socket >/dev/null -else - qm set $VMID \ - -efidisk0 ${DISK0_REF}${FORMAT} \ - -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ - -boot order=scsi0 \ - -serial0 socket >/dev/null -fi - -# Clean up work file -rm -f "$WORK_FILE" - -DESCRIPTION=$( - cat < - - Logo - - -

Debian VM

- -

- - spend Coffee - -

- - - - GitHub - - - - Discussions - - - - Issues - - -EOF -) -qm set $VMID -description "$DESCRIPTION" >/dev/null -if [ -n "$DISK_SIZE" ]; then - msg_info "Resizing disk to $DISK_SIZE GB" - qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null -else - msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" - qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null -fi - -msg_ok "Created a Debian 13 VM ${CL}${BL}(${HN})" -if [ "$START_VM" == "yes" ]; then - msg_info "Starting Debian 13 VM" - qm start $VMID - msg_ok "Started Debian 13 VM" -fi - -msg_ok "Completed successfully!\n" diff --git a/vm/debian-vm.sh b/vm/debian-vm.sh index 4b92f907d..c5daede44 100644 --- a/vm/debian-vm.sh +++ b/vm/debian-vm.sh @@ -1,63 +1,21 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG +#!/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 -source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) +COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main}" +source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func") +load_functions -function header_info { - clear - cat <<"EOF" - ____ __ _ ______ - / __ \___ / /_ (_)___ _____ < /__ \ - / / / / _ \/ __ \/ / __ `/ __ \ / /__/ / - / /_/ / __/ /_/ / / /_/ / / / / / // __/ -/_____/\___/_.___/_/\__,_/_/ /_/ /_//____/ - -EOF -} -header_info -echo -e "\n Loading..." +APP="Debian" +APP_TYPE="vm" +NSAPP="debian-vm" +var_os="debian" +var_version="13" GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" -NSAPP="debian-vm" -var_os="debian" -var_version="12" -YW=$(echo "\033[33m") -BL=$(echo "\033[36m") -RD=$(echo "\033[01;31m") -BGN=$(echo "\033[4;92m") -GN=$(echo "\033[1;92m") -DGN=$(echo "\033[32m") -CL=$(echo "\033[m") - -CL=$(echo "\033[m") -BOLD=$(echo "\033[1m") -BFR="\\r\\033[K" -HOLD=" " -TAB=" " - -CM="${TAB}āœ”ļø${TAB}${CL}" -CROSS="${TAB}āœ–ļø${TAB}${CL}" -INFO="${TAB}šŸ’”${TAB}${CL}" -OS="${TAB}šŸ–„ļø${TAB}${CL}" -CONTAINERTYPE="${TAB}šŸ“¦${TAB}${CL}" -DISKSIZE="${TAB}šŸ’¾${TAB}${CL}" -CPUCORE="${TAB}🧠${TAB}${CL}" -RAMSIZE="${TAB}šŸ› ļø${TAB}${CL}" -CONTAINERID="${TAB}šŸ†”${TAB}${CL}" -HOSTNAME="${TAB}šŸ ${TAB}${CL}" -BRIDGE="${TAB}šŸŒ‰${TAB}${CL}" -GATEWAY="${TAB}🌐${TAB}${CL}" -DEFAULT="${TAB}āš™ļø${TAB}${CL}" -MACADDRESS="${TAB}šŸ”—${TAB}${CL}" -VLANTAG="${TAB}šŸ·ļø${TAB}${CL}" -CREATING="${TAB}šŸš€${TAB}${CL}" -ADVANCED="${TAB}🧩${TAB}${CL}" -CLOUD="${TAB}ā˜ļø${TAB}${CL}" THIN="discard=on,ssd=1," set -e @@ -66,153 +24,45 @@ trap cleanup EXIT trap 'post_update_to_api "failed" "130"' SIGINT trap 'post_update_to_api "failed" "143"' SIGTERM trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP -function error_handler() { - local exit_code="$?" - local line_number="$1" - local command="$2" - local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" - post_update_to_api "failed" "${exit_code}" - echo -e "\n$error_message\n" - cleanup_vmid -} -function get_valid_nextid() { - local try_id - try_id=$(pvesh get /cluster/nextid) - while true; do - if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then - try_id=$((try_id + 1)) - continue - fi - if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then - try_id=$((try_id + 1)) - continue - fi - break - done - echo "$try_id" -} - -function cleanup_vmid() { - if qm status $VMID &>/dev/null; then - qm stop $VMID &>/dev/null - qm destroy $VMID &>/dev/null - fi -} - -function cleanup() { - local exit_code=$? - popd >/dev/null - if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then - if [[ $exit_code -eq 0 ]]; then - post_update_to_api "done" "none" - else - post_update_to_api "failed" "$exit_code" - fi - fi - rm -rf $TEMP_DIR -} +vm_preflight TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null -if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Debian 12 VM" --yesno "This will create a New Debian 12 VM. Proceed?" 10 58; then - : + +if [[ "${VM_UNATTENDED:-0}" == "1" ]]; then + var_version="${VM_OS_VERSION:-$var_version}" +elif vm_dialog radiolist "DEBIAN VERSION" "Choose the Debian release to install" --cancel-button Exit-Script 12 58 3 \ + "13" "Debian 13 (Trixie)" ON \ + "12" "Debian 12 (Bookworm)" OFF \ + "11" "Debian 11 (Bullseye)" OFF; then + var_version="$VM_DIALOG_RESULT" else - header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit + exit_script fi -function msg_info() { - local msg="$1" - echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" -} +case "$var_version" in +13) DEBIAN_CODENAME="trixie" ;; +12) DEBIAN_CODENAME="bookworm" ;; +11) DEBIAN_CODENAME="bullseye" ;; +*) + msg_error "Unsupported Debian version '${var_version}'" + exit 1 + ;; +esac +APP="Debian ${var_version}" -function msg_ok() { - local msg="$1" - echo -e "${BFR}${CM}${GN}${msg}${CL}" -} +header_info +echo -e "\n Loading..." -function msg_error() { - local msg="$1" - echo -e "${BFR}${CROSS}${RD}${msg}${CL}" -} - -function check_root() { - if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then - clear - msg_error "Please run this script as root." - echo -e "\nExiting..." - sleep 2 - exit - fi -} - -# 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, 9.0 and 9.2 -pve_check() { - local PVE_VER - PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - - # Check for Proxmox VE 8.x: allow 8.0–8.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.0 and 9.2 - if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then - local MINOR="${BASH_REMATCH[1]}" - if ((MINOR < 0 || MINOR > 2)); then - msg_error "This version of Proxmox VE is not supported." - msg_error "Supported: Proxmox VE version 9.0 – 9.2" - exit 105 - fi - 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.x or 9.0 – 9.2" - exit 105 -} - -function arch_check() { - if [ "$(dpkg --print-architecture)" != "amd64" ]; then - echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" - echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" - echo -e "Exiting..." - sleep 2 - exit - fi -} - -function ssh_check() { - if command -v pveversion >/dev/null 2>&1; then - if [ -n "${SSH_CLIENT:+x}" ]; then - if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then - echo "you've been warned" - else - clear - exit - fi - fi - fi -} - -function exit-script() { - clear - echo -e "\n${CROSS}${RD}User exited script${CL}\n" - exit -} +# Asked here rather than inside advanced_settings: it picks the disk image, so a +# default-settings run has to answer it too. nocloud autologs in on the console +# and carries no cloud-init; genericcloud gets the full provisioning. +vm_prompt_cloud_init "debian" function default_settings() { VMID=$(get_valid_nextid) - FORMAT=",efitype=4m" - MACHINE="" + vm_apply_machine_type "q35" DISK_SIZE="8G" DISK_CACHE="" HN="debian" @@ -224,233 +74,32 @@ function default_settings() { VLAN="" MTU="" START_VM="yes" - CLOUD_INIT="no" METHOD="default" - echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" - echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" - echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" - echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}no${CL}" - echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" - echo -e "${CREATING}${BOLD}${DGN}Creating a Debian 12 VM using the above default settings${CL}" + echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}${USE_CLOUD_INIT}${CL}" + vm_echo_default_settings } function advanced_settings() { METHOD="advanced" - [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) - while true; do - if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$VMID" ]; then - VMID=$(get_valid_nextid) - fi - if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then - echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" - sleep 2 - continue - fi - echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" - break - else - exit-script - fi - done + vm_prompt_vmid "${VMID:-$(get_valid_nextid)}" + vm_prompt_machine_type "q35" + vm_prompt_disk_size "8G" + vm_prompt_disk_cache "none" + vm_prompt_hostname "debian" + vm_prompt_cpu_model "kvm64" + vm_prompt_cpu_cores "2" + vm_prompt_ram "2048" + vm_prompt_bridge "vmbr0" + vm_prompt_mac "$GEN_MAC" + vm_prompt_vlan + vm_prompt_mtu - if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ - "i440fx" "Machine i440fx" ON \ - "q35" "Machine q35" OFF \ - 3>&1 1>&2 2>&3); then - if [ $MACH = q35 ]; then - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" - FORMAT="" - MACHINE=" -machine q35" - else - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" - FORMAT=",efitype=4m" - MACHINE="" - fi - else - exit-script - fi + echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}${USE_CLOUD_INIT}${CL}" + vm_prompt_verbose "no" + vm_prompt_start_vm "yes" - if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') - if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then - DISK_SIZE="${DISK_SIZE}G" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" - elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" - else - echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" - exit-script - fi - else - exit-script - fi - - if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ - "0" "None (Default)" ON \ - "1" "Write Through" OFF \ - 3>&1 1>&2 2>&3); then - if [ $DISK_CACHE = "1" ]; then - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" - DISK_CACHE="cache=writethrough," - else - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" - DISK_CACHE="" - fi - else - exit-script - fi - - if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 debian --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z $VM_NAME ]; then - HN="debian" - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" - else - HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') - if [ "$HN" != "${VM_NAME,,}" ]; then - whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 - fi - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" - fi - else - exit-script - fi - - if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ - "0" "KVM64 (Default)" ON \ - "1" "Host" OFF \ - 3>&1 1>&2 2>&3); then - if [ $CPU_TYPE1 = "1" ]; then - echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" - CPU_TYPE=" -cpu host" - else - echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" - CPU_TYPE="" - fi - else - exit-script - fi - - while true; do - if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi - if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" - break - fi - whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58 - else - exit-script - fi - done - - while true; do - if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048"; fi - if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" - break - fi - whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58 - else - exit-script - fi - done - - if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z $BRG ]; then - BRG="vmbr0" - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - else - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - fi - else - exit-script - fi - - while true; do - if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$MAC1" ]; then - MAC="$GEN_MAC" - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" - break - fi - if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then - MAC="$MAC1" - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" - break - fi - whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58 - else - exit-script - fi - done - - while true; do - if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$VLAN1" ]; then - VLAN1="Default" - VLAN="" - echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" - break - fi - if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then - VLAN=",tag=$VLAN1" - echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" - break - fi - whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 - else - exit-script - fi - done - - while true; do - if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$MTU1" ]; then - MTU1="Default" - MTU="" - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" - break - fi - if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then - MTU=",mtu=$MTU1" - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" - break - fi - whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 - else - exit-script - fi - done - - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "CLOUD-INIT" --yesno "Configure the VM with Cloud-init?" --defaultno 10 58); then - echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}yes${CL}" - CLOUD_INIT="yes" - else - echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}no${CL}" - CLOUD_INIT="no" - fi - - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then - echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" - START_VM="yes" - else - echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" - START_VM="no" - fi - - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Debian 12 VM?" --no-button Do-Over 10 58); then - echo -e "${CREATING}${BOLD}${DGN}Creating a Debian 12 VM using the above advanced settings${CL}" + if vm_confirm_advanced_settings "Ready to create a ${APP} VM?"; then + echo -e "${CREATING}${BOLD}${DGN}Creating a ${APP} VM using the above advanced settings${CL}" else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" @@ -458,66 +107,35 @@ function advanced_settings() { fi } -function start_script() { - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" - default_settings - else - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" - advanced_settings - fi -} - -check_root -arch_check -pve_check -ssh_check -start_script +vm_start_script "Use Default Settings?\n\nDefaults:\n• 2 CPU Cores\n• 2 GB RAM\n• 8 GB Disk" 13 58 post_to_api_vm -msg_info "Validating Storage" -while read -r line; do - TAG=$(echo $line | awk '{print $1}') - TYPE=$(echo $line | awk '{printf "%-10s", $2}') - FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') - ITEM=" Type: $TYPE Free: $FREE " - OFFSET=2 - if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then - MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) - fi - STORAGE_MENU+=("$TAG" "$ITEM" "OFF") -done < <(pvesm status -content images | awk 'NR>1') -VALID=$(pvesm status -content images | awk 'NR>1') -if [ -z "$VALID" ]; then - msg_error "Unable to detect a valid storage location." - exit -elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then - STORAGE=${STORAGE_MENU[0]} +vm_select_storage "$HN" +msg_info "Retrieving the URL for the ${APP} Qcow2 Disk Image" +DEBIAN_ARCH="$(vm_arch_resolve amd64 arm64)" +if [ "$USE_CLOUD_INIT" == "yes" ]; then + URL="https://cloud.debian.org/images/cloud/${DEBIAN_CODENAME}/latest/debian-${var_version}-genericcloud-${DEBIAN_ARCH}.qcow2" else - while [ -z "${STORAGE:+x}" ]; do - STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ - "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ - 16 $(($MSG_MAX_LENGTH + 23)) 6 \ - "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) - done -fi -msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." -msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." -msg_info "Retrieving the URL for the Debian 12 Qcow2 Disk Image" -if [ "$CLOUD_INIT" == "yes" ]; then - URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2 -else - URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-amd64.qcow2 + URL="https://cloud.debian.org/images/cloud/${DEBIAN_CODENAME}/latest/debian-${var_version}-nocloud-${DEBIAN_ARCH}.qcow2" fi sleep 2 msg_ok "${CL}${BL}${URL}${CL}" -curl -f#SL -o "$(basename "$URL")" "$URL" -echo -en "\e[1A\e[0K" -FILE=$(basename $URL) -msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" +CACHE_FILE="$(vm_image_cache_path "$URL")" +vm_fetch_image "$URL" "$CACHE_FILE" --cache --min-bytes $((100 * 1024 * 1024)) || exit 115 +FILE="$(basename "$CACHE_FILE")" +# Work on a copy: vm_prepare_cloud_image rewrites hostname and machine-id into +# the image, which would poison the cache for every later VM. +cp -f "$CACHE_FILE" "$FILE" + +# qm resize only grows the block device. Without cloud-init nothing grows the +# guest partition, so expand it offline first. +if [ "${USE_CLOUD_INIT:-no}" != "yes" ]; then + msg_info "Expanding the root filesystem to ${DISK_SIZE}" + vm_expand_image "$FILE" "$DISK_SIZE" || true +fi + +vm_prepare_cloud_image "$FILE" "$HN" || true STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') case $STORAGE_TYPE in @@ -546,69 +164,54 @@ for i in {0,1}; do eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} done -msg_info "Creating a Debian 12 VM" +msg_info "Creating a ${APP} VM" qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ - -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci -pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null + -name $HN -tags community-script,debian${var_version} -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci +vm_alloc_efi_disk "$DISK0" qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null -if [ "$CLOUD_INIT" == "yes" ]; then - qm set $VMID \ - -efidisk0 ${DISK0_REF}${FORMAT} \ - -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ - -scsi1 ${STORAGE}:cloudinit \ - -boot order=scsi0 \ - -serial0 socket >/dev/null -else - qm set $VMID \ - -efidisk0 ${DISK0_REF}${FORMAT} \ - -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ - -boot order=scsi0 \ - -serial0 socket >/dev/null -fi -DESCRIPTION=$( - cat < - - Logo - +# No cloudinit drive here: setup_cloud_init attaches it, and attaching it twice +# fails the second qm set, which takes the whole run down under errexit. +qm set $VMID \ + -efidisk0 ${DISK0_REF}${FORMAT} \ + -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ + -boot order=scsi0 \ + -serial0 socket >/dev/null +set_description +vm_resize_disk -

Debian VM

+if [ "$USE_CLOUD_INIT" == "yes" ] && declare -f setup_cloud_init >/dev/null 2>&1; then + setup_cloud_init \ + "$VMID" \ + "$STORAGE" \ + "$HN" \ + "yes" \ + "${CLOUDINIT_USER:-debian}" \ + "${CLOUDINIT_NETWORK_MODE:-dhcp}" \ + "${CLOUDINIT_IP:-}" \ + "${CLOUDINIT_GW:-}" \ + "${CLOUDINIT_DNS:-${CLOUDINIT_DNS_SERVERS:-1.1.1.1 8.8.8.8}}" -

- - spend Coffee - -

- - - - GitHub - - - - Discussions - - - - Issues - - -EOF -) -qm set "$VMID" -description "$DESCRIPTION" >/dev/null -if [ -n "$DISK_SIZE" ]; then - msg_info "Resizing disk to $DISK_SIZE GB" - qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null -else - msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" - qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null + if [[ "${CLOUDINIT_NETWORK_MODE:-dhcp}" == "static" ]]; then + setup_cloud_init_network_no_rename \ + "$VMID" \ + "$MAC" \ + "$CLOUDINIT_IP" \ + "$CLOUDINIT_GW" \ + "${CLOUDINIT_DNS:-${CLOUDINIT_DNS_SERVERS:-1.1.1.1 8.8.8.8}}" \ + "${CLOUDINIT_SEARCH_DOMAIN:-local}" + fi fi -msg_ok "Created a Debian 12 VM ${CL}${BL}(${HN})" +msg_ok "Created a ${APP} VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then - msg_info "Starting Debian 12 VM" - qm start $VMID - msg_ok "Started Debian 12 VM" + msg_info "Starting ${APP} VM" + $STD qm start $VMID + msg_ok "Started ${APP} VM" fi msg_ok "Completed successfully!\n" +if [ "$USE_CLOUD_INIT" == "yes" ] && declare -f display_cloud_init_info >/dev/null 2>&1; then + display_cloud_init_info "$VMID" "$HN" +else + echo -e "NoCloud image: the console autologs in as root and there is no Cloud-Init.\n" +fi From 61b753469ee716d69984e8fed0568ef7d7c57792 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 12:16:11 +0000 Subject: [PATCH 08/10] Update CHANGELOG.md (#17328) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53525df3e..e1c64ecf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -556,6 +556,10 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit - sparkyfitness: run the Better Auth migration during the update [@MickLesk](https://github.com/MickLesk) ([#17318](https://github.com/community-scripts/ProxmoxVE/pull/17318)) - poznote: follow init.sh into docker/ [@MickLesk](https://github.com/MickLesk) ([#17316](https://github.com/community-scripts/ProxmoxVE/pull/17316)) + - #### ✨ New Features + + - Debian-VM: Refactor | Support Debian 11, 12, 13 [@MickLesk](https://github.com/MickLesk) ([#17325](https://github.com/community-scripts/ProxmoxVE/pull/17325)) + ### šŸ’¾ Core - Ask before replacing a cached image that no longer matches [@MickLesk](https://github.com/MickLesk) ([core#44](https://github.com/community-scripts/core/pull/44)) From 0b2162747fc7bf6913cd64a16d9429308d6201ff Mon Sep 17 00:00:00 2001 From: "push-app-to-main[bot]" <203845782+push-app-to-main[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 14:26:41 +0200 Subject: [PATCH 09/10] Add alpine-vm (vm) (#17324) Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com> --- vm/alpine-vm.sh | 179 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 vm/alpine-vm.sh diff --git a/vm/alpine-vm.sh b/vm/alpine-vm.sh new file mode 100644 index 000000000..0c2f2dd40 --- /dev/null +++ b/vm/alpine-vm.sh @@ -0,0 +1,179 @@ +#!/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 +# Source: https://alpinelinux.org/cloud/ + +COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main}" +source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func") +load_functions + +APP="Alpine" +APP_TYPE="vm" +NSAPP="alpine-vm" +var_os="alpine" +var_version=" " +GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') +RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" +METHOD="" +THIN="discard=on,ssd=1," + +header_info +echo -e "\n Loading..." + +set -e +trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +trap cleanup EXIT +trap 'post_update_to_api "failed" "130"' SIGINT +trap 'post_update_to_api "failed" "143"' SIGTERM +trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP + +TEMP_DIR=$(mktemp -d) +pushd "$TEMP_DIR" >/dev/null + +vm_preflight + +function default_settings() { + VMID=$(get_valid_nextid) + vm_apply_machine_type "q35" + DISK_SIZE="8G" + DISK_CACHE="" + HN="alpine" + CPU_TYPE=" -cpu host" + CORE_COUNT="2" + RAM_SIZE="1024" + BRG="vmbr0" + MAC="$GEN_MAC" + VLAN="" + MTU="" + START_VM="yes" + METHOD="default" + USE_CLOUD_INIT="yes" + vm_echo_default_settings +} + +function advanced_settings() { + METHOD="advanced" + vm_prompt_vmid "${VMID:-$(get_valid_nextid)}" + vm_prompt_machine_type "q35" + vm_prompt_disk_size "8G" "Set Disk Size in GiB" + vm_prompt_disk_cache "none" + vm_prompt_hostname "alpine" + vm_prompt_cpu_model "host" + vm_prompt_cpu_cores "2" + vm_prompt_ram "1024" + vm_prompt_bridge "vmbr0" + vm_prompt_mac "$GEN_MAC" + vm_prompt_vlan + vm_prompt_mtu + vm_prompt_cloud_init "alpine" + vm_prompt_verbose "no" + vm_prompt_start_vm "yes" + + if vm_confirm_advanced_settings "Ready to create an Alpine VM?"; then + echo -e "${CREATING}${BOLD}${DGN}Creating an Alpine VM using the above advanced settings${CL}" + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi +} + +vm_start_script "Use Default Settings?\n\nDefaults:\n• 2 CPU Cores\n• 1 GB RAM\n• 8 GB Disk\n• Cloud-Init enabled" 14 58 +post_to_api_vm + +vm_select_storage "$HN" + +if ! command -v virt-customize &>/dev/null; then + msg_info "Installing libguestfs-tools" + $STD apt-get update + $STD apt-get install -y libguestfs-tools + msg_ok "Installed libguestfs-tools" +fi + +msg_info "Retrieving the URL for the Alpine cloud image" + +# Alpine publishes a bios and a uefi variant side by side. This script boots +# through OVMF, so picking the bios image would produce a VM that never starts. +# The release number and the -r revision both live in the filename, so the +# directory has to be listed rather than guessed. +CLOUD_DIR="https://dl-cdn.alpinelinux.org/alpine/latest-stable/releases/cloud" +FILE=$(curl -fsSL "${CLOUD_DIR}/" 2>/dev/null | grep -oP 'href="\Kgeneric_alpine-[0-9.]+-x86_64-uefi-cloudinit-r[0-9]+\.qcow2(?=")' | sort -V | tail -1) +if [[ -z "$FILE" ]]; then + msg_error "Could not determine the current Alpine cloud image" + exit 1 +fi + +ALPINE_VERSION=$(echo "$FILE" | grep -oP 'generic_alpine-\K[0-9.]+') +msg_ok "Alpine ${CL}${BL}${ALPINE_VERSION}${CL} ${GN}(${FILE})" + +URL="${CLOUD_DIR}/${FILE}" +CACHE_FILE="$(vm_image_cache_path "$URL")" +vm_fetch_image "$URL" "$CACHE_FILE" --cache --min-bytes $((5 * 1024 * 1024)) || exit 115 + +msg_info "Customizing ${FILE}" +WORK_FILE=$(mktemp --suffix=.qcow2) +cp "$CACHE_FILE" "$WORK_FILE" +popd >/dev/null +rm -rf "$TEMP_DIR" + +# No systemctl and no SELinux here: Alpine runs OpenRC, and the cloud image +# already wires up the serial console through /etc/inittab. +vm_prepare_cloud_image "$WORK_FILE" "$HN" || true +msg_ok "Customized image" + +STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}') +vm_apply_storage_layout "$STORAGE_TYPE" +vm_define_disk_references 3 + +if [[ "$DISK_IMPORT_FORMAT" == "raw" ]]; then + msg_info "Converting image to raw format" + RAW_FILE=$(mktemp --suffix=.raw) + qemu-img convert -f qcow2 -O raw "$WORK_FILE" "$RAW_FILE" + rm -f "$WORK_FILE" + WORK_FILE="$RAW_FILE" + msg_ok "Converted image to raw format" +fi + +msg_info "Creating an Alpine VM" +qm create "$VMID" -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores "$CORE_COUNT" -memory "$RAM_SIZE" \ + -name "$HN" -tags community-script -net0 virtio,bridge="$BRG",macaddr="$MAC""$VLAN""$MTU" -onboot 1 -ostype l26 -scsihw virtio-scsi-pci +pvesm alloc "$STORAGE" "$VMID" "$DISK0" 4M 1>&/dev/null +pvesm alloc "$STORAGE" "$VMID" "$DISK2" 4M 1>&/dev/null +qm importdisk "$VMID" "$WORK_FILE" "$STORAGE" -format "$DISK_IMPORT_FORMAT" 1>&/dev/null +qm set "$VMID" \ + -efidisk0 "${DISK0_REF}${FORMAT:-}" \ + -scsi0 "${DISK1_REF}",${DISK_CACHE}${THIN}size="${DISK_SIZE}" \ + -tpmstate0 "${DISK2_REF}",version=v2.0 \ + -boot order=scsi0 \ + -serial0 socket >/dev/null +set_description +rm -f "$WORK_FILE" +msg_ok "Created an Alpine VM ${CL}${BL}(${HN})" + +vm_resize_disk + +vm_provision "$VMID" || true + +if [ "$START_VM" == "yes" ]; then + msg_info "Starting Alpine VM" + $STD qm start "$VMID" + msg_ok "Started Alpine VM" +fi + +post_update_to_api "done" "none" + +echo -e "\n${INFO}${BOLD}${GN}Alpine VM Configuration Summary:${CL}" +echo -e "${TAB}${DGN}VM ID: ${BGN}${VMID}${CL}" +echo -e "${TAB}${DGN}Hostname: ${BGN}${HN}${CL}" +echo -e "${TAB}${DGN}Release: ${BGN}Alpine ${ALPINE_VERSION}${CL}" +if [ -n "${CLOUDINIT_CRED_FILE:-}" ]; then + echo -e "${TAB}${DGN}Cloud-Init credentials: ${BGN}${CLOUDINIT_CRED_FILE}${CL}" +fi + +echo -e "\n${INFO}${BOLD}${YW}Note:${CL}" +echo -e "${TAB}Alpine uses apk and OpenRC, not apt and systemd." +echo -e "${TAB}Enable a service with ${BL}rc-update add default${CL}." + +msg_ok "Completed successfully!\n" From dd80e154a2ffacdb4d151b2c6237c3807a6846dc Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 12:27:11 +0000 Subject: [PATCH 10/10] Update CHANGELOG.md (#17330) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1c64ecf0..a9bc11c89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -547,7 +547,8 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit ### šŸ†• New Scripts - - Allstarlink ([#17321](https://github.com/community-scripts/ProxmoxVE/pull/17321)) + - Alpine-VM ([#17324](https://github.com/community-scripts/ProxmoxVE/pull/17324)) +- Allstarlink ([#17321](https://github.com/community-scripts/ProxmoxVE/pull/17321)) ### šŸš€ Updated Scripts