From 98e1dd01331b844a7056d08160f473d7c15c1f04 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Tue, 5 May 2026 14:40:57 +0200 Subject: [PATCH 01/18] Increase default disk size from 10GB to 16GB --- ct/authentik.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/authentik.sh b/ct/authentik.sh index 0d049c47..2879eed6 100644 --- a/ct/authentik.sh +++ b/ct/authentik.sh @@ -9,7 +9,7 @@ APP="authentik" var_tags="${var_tags:-auth}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" -var_disk="${var_disk:-10}" +var_disk="${var_disk:-16}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" From bc51257aa7b3b5ba8dbc2885499a3b1371c85b0d Mon Sep 17 00:00:00 2001 From: MickLesk Date: Wed, 6 May 2026 07:09:31 +0200 Subject: [PATCH 02/18] Refactor build process in Puter installation and update JSON files to ensure proper formatting --- ct/puter.sh | 3 --- install/puter-install.sh | 11 +++-------- json/blinko.json | 2 +- json/invidious.json | 2 +- json/paperclip.json | 2 +- 5 files changed, 6 insertions(+), 14 deletions(-) diff --git a/ct/puter.sh b/ct/puter.sh index 870a21ec..53030db3 100644 --- a/ct/puter.sh +++ b/ct/puter.sh @@ -39,10 +39,7 @@ function update_script() { msg_info "Building Application" cd /opt/puter $STD npm ci - cd /opt/puter/src/gui $STD npm run build - cd /opt/puter - cp -r src/gui/dist dist msg_ok "Built Application" msg_info "Starting Service" diff --git a/install/puter-install.sh b/install/puter-install.sh index 6c6d2af5..1ab020dd 100644 --- a/install/puter-install.sh +++ b/install/puter-install.sh @@ -26,13 +26,8 @@ fetch_and_deploy_gh_release "puter" "HeyPuter/puter" "tarball" msg_info "Building Application" cd /opt/puter -node -e "const f=require('fs'),p=JSON.parse(f.readFileSync('package.json'));p.overrides={'better-sqlite3':'>=12.0.0'};f.writeFileSync('package.json',JSON.stringify(p,null,2))" -rm -f package-lock.json -$STD npm install -cd /opt/puter/src/gui +$STD npm ci $STD npm run build -cd /opt/puter -cp -r src/gui/dist dist msg_ok "Built Application" msg_info "Creating Directories" @@ -67,8 +62,8 @@ After=network.target Type=simple User=root WorkingDirectory=/opt/puter -Environment=CONFIG_PATH=/etc/puter -ExecStart=/usr/bin/npm start +Environment=PUTER_CONFIG_PATH=/etc/puter/config.json +ExecStart=/usr/bin/node --enable-source-maps -r /opt/puter/dist/src/backend/telemetry.js /opt/puter/dist/src/backend/index.js Restart=on-failure RestartSec=5 diff --git a/json/blinko.json b/json/blinko.json index 70f3508f..b7b3a570 100644 --- a/json/blinko.json +++ b/json/blinko.json @@ -41,4 +41,4 @@ "type": "warning" } ] -} +} \ No newline at end of file diff --git a/json/invidious.json b/json/invidious.json index 5a8837e1..17c914a3 100644 --- a/json/invidious.json +++ b/json/invidious.json @@ -37,4 +37,4 @@ "type": "info" } ] -} +} \ No newline at end of file diff --git a/json/paperclip.json b/json/paperclip.json index 187f8a67..b60c621d 100644 --- a/json/paperclip.json +++ b/json/paperclip.json @@ -45,4 +45,4 @@ "type": "info" } ] -} +} \ No newline at end of file From 7b5bb6dfd9312bb1153a9e303c0b574ddf6d6db4 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Wed, 6 May 2026 07:36:01 +0200 Subject: [PATCH 03/18] Update domain configuration to use nip.io for subdomain routing and enhance documentation --- ct/puter.sh | 2 +- install/puter-install.sh | 4 ++-- json/puter.json | 12 ++++++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/ct/puter.sh b/ct/puter.sh index 53030db3..491d6aae 100644 --- a/ct/puter.sh +++ b/ct/puter.sh @@ -57,4 +57,4 @@ 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}http://${IP}:4100${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}.nip.io:4100${CL}" diff --git a/install/puter-install.sh b/install/puter-install.sh index 1ab020dd..685b4dba 100644 --- a/install/puter-install.sh +++ b/install/puter-install.sh @@ -38,10 +38,10 @@ msg_info "Configuring Application" cat </etc/puter/config.json { "config_name": "proxmox", - "domain": "${LOCAL_IP}", + "domain": "${LOCAL_IP}.nip.io", "protocol": "http", "http_port": 4100, - "experimental_no_subdomain": true, + "allow_nipio_domains": true, "services": { "database": { "engine": "sqlite", diff --git a/json/puter.json b/json/puter.json index 5c3adee5..9028c933 100644 --- a/json/puter.json +++ b/json/puter.json @@ -33,8 +33,16 @@ }, "notes": [ { - "text": "Configuration is stored in /etc/puter and data in /var/puter.", + "text": "Access via http://<IP>.nip.io:4100 (not the raw IP). nip.io is required for Puter's subdomain routing.", + "type": "info" + }, + { + "text": "Camera, microphone, and other media capture features require HTTPS (a secure context). They will not work on the default HTTP setup.", + "type": "warning" + }, + { + "text": "Configuration is stored in /etc/puter/config.json and data in /var/puter.", "type": "info" } ] -} +} \ No newline at end of file From 1580612098f7be364460b0bf851772ccbdc66f37 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Wed, 6 May 2026 08:34:10 +0200 Subject: [PATCH 04/18] finalize hoodik Co-authored-by: Copilot --- ct/hoodik.sh | 44 ++++++++++----------------------------- install/hoodik-install.sh | 8 ++++--- 2 files changed, 16 insertions(+), 36 deletions(-) diff --git a/ct/hoodik.sh b/ct/hoodik.sh index dadbc842..c63a175e 100644 --- a/ct/hoodik.sh +++ b/ct/hoodik.sh @@ -8,9 +8,9 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV APP="Hoodik" var_tags="${var_tags:-cloud;storage}" -var_cpu="${var_cpu:-4}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-20}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" @@ -25,53 +25,31 @@ function update_script() { check_container_storage check_container_resources - if [[ ! -f /usr/local/bin/hoodik ]]; then + if [[ ! -f /opt/hoodik/hoodik ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "hoodik" "hudikhq/hoodik"; then - msg_info "Stopping Services" + msg_info "Stopping Service" systemctl stop hoodik - msg_ok "Stopped Services" + msg_ok "Stopped Service" msg_info "Backing up Configuration" cp /opt/hoodik/.env /opt/hoodik.env.bak msg_ok "Backed up Configuration" - msg_info "Updating Hoodik (Patience - this takes 15-20 minutes)" - source ~/.cargo/env - rm -rf /opt/hoodik - fetch_and_deploy_gh_release "hoodik" "hudikhq/hoodik" "tarball" "latest" "/opt/hoodik" - cd /opt/hoodik - - msg_info "Building Frontend" - $STD yarn install --frozen-lockfile - $STD yarn wasm-pack - $STD yarn web:build - msg_ok "Built Frontend" - - msg_info "Building Backend" - $STD cargo build --release - cp /opt/hoodik/target/release/hoodik /usr/local/bin/hoodik - chmod +x /usr/local/bin/hoodik - msg_ok "Updated Hoodik" + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "hoodik" "hudikhq/hoodik" "prebuild" "latest" "/opt/hoodik" "*x86_64.tar.gz" msg_info "Restoring Configuration" cp /opt/hoodik.env.bak /opt/hoodik/.env rm -f /opt/hoodik.env.bak msg_ok "Restored Configuration" - msg_info "Cleaning Up" - rm -rf /opt/hoodik/target - rm -rf /root/.cargo/registry - rm -rf /opt/hoodik/node_modules - msg_ok "Cleaned" - - msg_info "Starting Services" + msg_info "Starting Service" systemctl start hoodik - msg_ok "Started Services" - msg_ok "Updated Successfully" + msg_ok "Started Service" + msg_ok "Updated successfully!" fi exit } @@ -83,4 +61,4 @@ 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}http://${IP}:5443${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5443/auth/register${CL}" diff --git a/install/hoodik-install.sh b/install/hoodik-install.sh index 8edc7e63..1138e4c4 100644 --- a/install/hoodik-install.sh +++ b/install/hoodik-install.sh @@ -23,8 +23,10 @@ DATA_DIR=/opt/hoodik_data HTTP_PORT=5443 HTTP_ADDRESS=0.0.0.0 JWT_SECRET=${JWT_SECRET} -APP_URL=http://127.0.0.1:5443 +APP_URL=http://${LOCAL_IP}:5443 SSL_DISABLED=true +COOKIE_SECURE=false +COOKIE_HTTP_ONLY=false MAILER_TYPE=none RUST_LOG=hoodik=info,error=info EOF @@ -41,14 +43,14 @@ Type=simple User=root WorkingDirectory=/opt/hoodik_data EnvironmentFile=/opt/hoodik/.env -ExecStart=/opt/hoodik +ExecStart=/opt/hoodik/hoodik Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now hoodik.service +systemctl enable -q --now hoodik msg_ok "Created Service" motd_ssh From 012fa20a03167e25176f4ef0ed5d46143f79062d Mon Sep 17 00:00:00 2001 From: MickLesk Date: Wed, 6 May 2026 08:35:07 +0200 Subject: [PATCH 05/18] dfv --- json/hoodik.json | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/json/hoodik.json b/json/hoodik.json index 20fc904b..f51b08b3 100644 --- a/json/hoodik.json +++ b/json/hoodik.json @@ -19,9 +19,9 @@ "script": "ct/hoodik.sh", "config_path": "/opt/hoodik/.env", "resources": { - "cpu": 4, - "ram": 4096, - "hdd": 20, + "cpu": 1, + "ram": 1024, + "hdd": 5, "os": "Debian", "version": "13" } @@ -36,21 +36,9 @@ "text": "First visit will prompt you to create an admin account", "type": "info" }, - { - "text": "Installation builds Rust backend and Vue frontend from source - takes 15-20 minutes", - "type": "warning" - }, - { - "text": "Requires 4GB RAM and 20GB disk for build process", - "type": "warning" - }, { "text": "Data is stored in /opt/hoodik_data", "type": "info" - }, - { - "text": "SSL is disabled by default - use a reverse proxy for HTTPS", - "type": "warning" } ] -} +} \ No newline at end of file From 7376a733374e239e6f72b1ec6dfb91ccd37ad859 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Wed, 6 May 2026 09:08:38 +0200 Subject: [PATCH 06/18] Refactor AlmaLinux VM installer & lobehub setup Modernize and simplify VM installer and lobehub installation flows. Key changes: - install/lobehub-install.sh: consolidated apt invocation, replaced manual ParadeDB .deb download with fetch_and_deploy_gh_release helper, added postgresql pg_search preload configuration and restart, moved pnpm installation into setup_nodejs via NODE_MODULE, and adjusted build env handling. - vm/almalinux-10-vm.sh: major refactor to source shared functions from COMMUNITY_SCRIPTS_URL, add load_functions, centralize cloud-init handling (configure_cloudinit_ssh_keys / setup_cloud_init), replace many custom UI/helper functions with standardized helpers and exit_script, update traps and error handling, simplify prompts/defaults, use $STD for apt operations, and streamline image customization and VM creation flow. - Added vm/headers/almalinux-10-vm containing the VM header art and title. These changes centralize common logic, improve cloud-init integration, reduce duplicated code, and make maintenance easier. --- install/lobehub-install.sh | 29 ++-- vm/almalinux-10-vm.sh | 317 ++++++------------------------------- vm/headers/almalinux-10-vm | 11 ++ 3 files changed, 71 insertions(+), 286 deletions(-) create mode 100644 vm/headers/almalinux-10-vm diff --git a/install/lobehub-install.sh b/install/lobehub-install.sh index 3ec0b1bc..ad85b04b 100644 --- a/install/lobehub-install.sh +++ b/install/lobehub-install.sh @@ -14,36 +14,28 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt install -y \ - build-essential +$STD apt install -y build-essential msg_ok "Installed Dependencies" PG_VERSION="17" PG_MODULES="pgvector" setup_postgresql -msg_info "Installing pg_search from ParadeDB" -ARCH=$(dpkg --print-architecture) CODENAME=$(. /etc/os-release && echo "${VERSION_CODENAME:-bookworm}") -PDB_VERSION=$(curl -fsSL "https://api.github.com/repos/paradedb/paradedb/releases/latest" | grep -oP '"tag_name":\s*"\K[^"]+') -PDB_VERSION_NUM="${PDB_VERSION#v}" -DEB_NAME="postgresql-17-pg-search_${PDB_VERSION_NUM}-1PARADEDB-${CODENAME}_${ARCH}.deb" -DEB_URL="https://github.com/paradedb/paradedb/releases/download/${PDB_VERSION}/${DEB_NAME}" -curl -fsSL -o "/tmp/${DEB_NAME}" "$DEB_URL" -dpkg -i "/tmp/${DEB_NAME}" >/dev/null 2>&1 || $STD apt install -f -y -rm -f "/tmp/${DEB_NAME}" -msg_ok "Installed pg_search from ParadeDB" +fetch_and_deploy_gh_release "paradedb" "paradedb/paradedb" "binary" "latest" "" "postgresql-17-pg-search_*-1PARADEDB-${CODENAME}_$(dpkg --print-architecture).deb" + +msg_info "Configuring pg_search preload library" +if ! grep -q "shared_preload_libraries.*pg_search" /etc/postgresql/17/main/postgresql.conf; then + echo "shared_preload_libraries = 'pg_search'" >>/etc/postgresql/17/main/postgresql.conf +fi +systemctl restart postgresql +msg_ok "Configured pg_search preload library" PG_DB_NAME="lobehub" PG_DB_USER="lobehub" PG_DB_EXTENSIONS="vector,pg_search" setup_postgresql_db -NODE_VERSION="24" setup_nodejs - -msg_info "Installing pnpm" -$STD npm install -g pnpm -msg_ok "Installed pnpm" +NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs fetch_and_deploy_gh_release "lobehub" "lobehub/lobehub" "tarball" msg_info "Building Application" cd /opt/lobehub -export NODE_OPTIONS="--max-old-space-size=8192" export DATABASE_URL="postgres://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME}" export DATABASE_DRIVER="node" export KEY_VAULTS_SECRET="$(openssl rand -base64 32)" @@ -51,7 +43,6 @@ export AUTH_SECRET="$(openssl rand -base64 32)" export APP_URL="http://localhost:3210" $STD pnpm install $STD pnpm run build:docker -unset NODE_OPTIONS msg_ok "Built Application" msg_info "Configuring Application" diff --git a/vm/almalinux-10-vm.sh b/vm/almalinux-10-vm.sh index 47173dad..21b84a43 100644 --- a/vm/almalinux-10-vm.sh +++ b/vm/almalinux-10-vm.sh @@ -2,221 +2,51 @@ # Copyright (c) 2021-2026 community-scripts ORG # Author: Agent-Fennec -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/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://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main}" +source <(curl -fsSL "$COMMUNITY_SCRIPTS_URL/misc/api.func") +source <(curl -fsSL "$COMMUNITY_SCRIPTS_URL/misc/vm-core.func") +source <(curl -fsSL "$COMMUNITY_SCRIPTS_URL/misc/cloud-init.func") || true +load_functions -function header_info() { - clear - cat <<"EOF" - - █████╗ ██╗ ███╗ ███╗ █████╗ ██╗ ██╗███╗ ██╗██╗ ██╗██╗ ██╗ - ██╔══██╗██║ ████╗ ████║██╔══██╗██║ ██║████╗ ██║██║ ██║╚██╗██╔╝ - ███████║██║ ██╔████╔██║███████║██║ ██║██╔██╗ ██║██║ ██║ ╚███╔╝ - ██╔══██║██║ ██║╚██╔╝██║██╔══██║██║ ██║██║╚██╗██║██║ ██║ ██╔██╗ - ██║ ██║███████╗██║ ╚═╝ ██║██║ ██║███████╗██║██║ ╚████║╚██████╔╝██╔╝ ██╗ - ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ - - AlmaLinux 10 Installer - (Heliotrope Lion) - -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="" +APP="AlmaLinux 10 VM" +APP_TYPE="vm" NSAPP="almalinux10vm" var_os="almalinux" var_version="10" -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}" - +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," + 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" "INTERRUPTED"' SIGINT +trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM + 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}" + post_update_to_api "failed" "${command}" 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 2>/dev/null || true - if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then - if [[ $exit_code -eq 0 ]]; then - post_update_to_api "done" "none" || true - else - post_update_to_api "failed" "$exit_code" || true - fi - fi - rm -rf "$TEMP_DIR" - rm -f "${WORK_FILE:-}" -} - TEMP_DIR=$(mktemp -d) pushd "$TEMP_DIR" >/dev/null if whiptail --backtitle "Proxmox VE Helper Scripts" --title "AlmaLinux 10 VM" --yesno "This will create a New AlmaLinux 10 VM. Proceed?" 10 58; then : else - header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit + header_info && exit_script 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.1 -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 1 - fi - return 0 - fi - - # Check for Proxmox VE 9.x: allow 9.0 and 9.1 - if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then - local MINOR="${BASH_REMATCH[1]}" - if ((MINOR < 0 || MINOR > 1)); then - msg_error "This version of Proxmox VE is not supported." - msg_error "Supported: Proxmox VE version 9.0 – 9.1" - exit 1 - 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.1" - exit 1 -} - -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" - popd >/dev/null 2>&1 || true - rm -rf "${TEMP_DIR:-}" - rm -f "${WORK_FILE:-}" - exec bash -} - function default_settings() { + configure_cloudinit_ssh_keys || true VMID=$(get_valid_nextid) FORMAT="" MACHINE=" -machine q35" @@ -264,7 +94,7 @@ function advanced_settings() { echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else - exit-script + exit_script fi done @@ -282,10 +112,10 @@ function advanced_settings() { MACHINE=" -machine q35" fi else - exit-script + 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 + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "10" --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" @@ -294,10 +124,10 @@ function advanced_settings() { 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 + exit_script fi else - exit-script + exit_script fi if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ @@ -312,7 +142,7 @@ function advanced_settings() { DISK_CACHE="" fi else - exit-script + exit_script fi if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 almalinux --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then @@ -320,13 +150,10 @@ function advanced_settings() { HN="almalinux" 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 fi echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else - exit-script + exit_script fi if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ @@ -341,7 +168,7 @@ function advanced_settings() { CPU_TYPE=" -cpu x86-64-v3" fi else - exit-script + exit_script fi while true; do @@ -353,7 +180,7 @@ function advanced_settings() { 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 + exit_script fi done @@ -366,17 +193,15 @@ function advanced_settings() { 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 + 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" - fi + if [ -z "$BRG" ]; then BRG="vmbr0"; fi echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else - exit-script + exit_script fi while true; do @@ -391,9 +216,9 @@ function advanced_settings() { echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${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 + whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX." 8 58 else - exit-script + exit_script fi done @@ -410,9 +235,9 @@ function advanced_settings() { 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 + whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank." 8 58 else - exit-script + exit_script fi done @@ -429,9 +254,9 @@ function advanced_settings() { 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 + whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be between 576 and 65520, or leave blank." 8 58 else - exit-script + exit_script fi done @@ -443,6 +268,8 @@ function advanced_settings() { START_VM="no" fi + configure_cloudinit_ssh_keys || true + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create an AlmaLinux 10 VM?" --no-button Do-Over 10 58); then echo -e "${CREATING}${BOLD}${DGN}Creating an AlmaLinux 10 VM using the above advanced settings${CL}" else @@ -464,12 +291,7 @@ function start_script() { fi } -check_root -arch_check -pve_check -ssh_check start_script - post_to_api_vm msg_info "Validating Storage" @@ -496,7 +318,7 @@ 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) || exit-script + "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) || exit_script done msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." @@ -506,8 +328,8 @@ msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." # ============================================================================== if ! command -v virt-customize &>/dev/null; then msg_info "Installing libguestfs-tools" - apt-get -qq update >/dev/null 2>&1 - apt-get -qq install -y libguestfs-tools >/dev/null 2>&1 + $STD apt-get update + $STD apt-get install -y libguestfs-tools msg_ok "Installed libguestfs-tools" fi @@ -530,24 +352,13 @@ cp "$FILE" "$WORK_FILE" popd >/dev/null rm -rf "$TEMP_DIR" -# 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 - -# 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 - -# Enable serial console login virt-customize -q -a "$WORK_FILE" --run-command "systemctl enable serial-getty@ttyS0.service" >/dev/null 2>&1 || true - -# Relabel SELinux contexts after all modifications (required for AlmaLinux/RHEL) virt-customize -q -a "$WORK_FILE" --selinux-relabel >/dev/null 2>&1 || true msg_ok "Customized image" @@ -579,7 +390,6 @@ for i in {0,1,2}; do eval DISK"${i}"_REF="${STORAGE}":"${DISK_REF:-}""${!disk}" done -# For block/btrfs storage, pre-convert qcow2 to raw to ensure compatibility if [[ "$STORAGE_TYPE" != "nfs" && "$STORAGE_TYPE" != "dir" ]]; then msg_info "Converting image to raw format" RAW_FILE=$(mktemp --suffix=.raw) @@ -598,49 +408,19 @@ qm importdisk "$VMID" "${WORK_FILE}" "$STORAGE" ${DISK_IMPORT:-} 1>&/dev/null qm set "$VMID" \ -efidisk0 "${DISK0_REF}"${FORMAT} \ -scsi0 "${DISK1_REF}",${DISK_CACHE}${THIN}size="${DISK_SIZE}" \ - -scsi1 "${STORAGE}":cloudinit \ -tpmstate0 "${DISK2_REF}",version=v2.0 \ -boot order=scsi0 \ -serial0 socket >/dev/null -# Clean up work file rm -f "$WORK_FILE" +msg_ok "Created an AlmaLinux 10 VM ${CL}${BL}(${HN})" -DESCRIPTION=$( - cat < - - Logo - - -

AlmaLinux 10 VM

- -

- - spend Coffee - -

- - - - GitHub - - - - Discussions - - - - Issues - - -EOF -) -qm set "$VMID" -description "$DESCRIPTION" >/dev/null msg_info "Resizing disk to ${DISK_SIZE}" qm resize "$VMID" scsi0 "${DISK_SIZE}" >/dev/null +msg_ok "Resized disk to ${DISK_SIZE}" + +setup_cloud_init "$VMID" "$STORAGE" "$HN" "yes" -msg_ok "Created an AlmaLinux 10 VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting AlmaLinux 10 VM" qm start "$VMID" @@ -649,5 +429,8 @@ fi post_update_to_api "done" "none" -msg_ok "Completed successfully" -echo -e "Setup Cloud-Init before starting \nMore info at https://github.com/community-scripts/ProxmoxVE/discussions/272 \n" +msg_ok "Completed successfully!" +if [ -n "${CLOUDINIT_CRED_FILE:-}" ]; then + echo -e "${INFO}${YW} Cloud-Init credentials saved to: ${BGN}${CLOUDINIT_CRED_FILE}${CL}" +fi + diff --git a/vm/headers/almalinux-10-vm b/vm/headers/almalinux-10-vm new file mode 100644 index 00000000..1bed771d --- /dev/null +++ b/vm/headers/almalinux-10-vm @@ -0,0 +1,11 @@ + + █████╗ ██╗ ███╗ ███╗ █████╗ ██╗ ██╗███╗ ██╗██╗ ██╗██╗ ██╗ + ██╔══██╗██║ ████╗ ████║██╔══██╗██║ ██║████╗ ██║██║ ██║╚██╗██╔╝ + ███████║██║ ██╔████╔██║███████║██║ ██║██╔██╗ ██║██║ ██║ ╚███╔╝ + ██╔══██║██║ ██║╚██╔╝██║██╔══██║██║ ██║██║╚██╗██║██║ ██║ ██╔██╗ + ██║ ██║███████╗██║ ╚═╝ ██║██║ ██║███████╗██║██║ ╚████║╚██████╔╝██╔╝ ██╗ + ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ + + AlmaLinux 10 VM + (Heliotrope Lion) + From 850a4cf4b73d03bc55b1247861370d92626b3967 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 6 May 2026 07:09:05 +0000 Subject: [PATCH 07/18] chore: update app headers [skip ci] --- vm/headers/almalinux-10-vm | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/vm/headers/almalinux-10-vm b/vm/headers/almalinux-10-vm index 1bed771d..f4d423de 100644 --- a/vm/headers/almalinux-10-vm +++ b/vm/headers/almalinux-10-vm @@ -1,11 +1,6 @@ - - █████╗ ██╗ ███╗ ███╗ █████╗ ██╗ ██╗███╗ ██╗██╗ ██╗██╗ ██╗ - ██╔══██╗██║ ████╗ ████║██╔══██╗██║ ██║████╗ ██║██║ ██║╚██╗██╔╝ - ███████║██║ ██╔████╔██║███████║██║ ██║██╔██╗ ██║██║ ██║ ╚███╔╝ - ██╔══██║██║ ██║╚██╔╝██║██╔══██║██║ ██║██║╚██╗██║██║ ██║ ██╔██╗ - ██║ ██║███████╗██║ ╚═╝ ██║██║ ██║███████╗██║██║ ╚████║╚██████╔╝██╔╝ ██╗ - ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ - - AlmaLinux 10 VM - (Heliotrope Lion) - + ___ __ __ _ _______ _ ____ ___ + / | / /___ ___ ____ _/ / (_)___ __ ___ __ < / __ \ | | / / |/ / + / /| | / / __ `__ \/ __ `/ / / / __ \/ / / / |/_/ / / / / / | | / / /|_/ / + / ___ |/ / / / / / / /_/ / /___/ / / / / /_/ /> < / / /_/ / | |/ / / / / +/_/ |_/_/_/ /_/ /_/\__,_/_____/_/_/ /_/\__,_/_/|_| /_/\____/ |___/_/ /_/ + From 3850f46cdb02ec23e53f6a22228b07b8b6be28cf Mon Sep 17 00:00:00 2001 From: MickLesk Date: Wed, 6 May 2026 09:10:02 +0200 Subject: [PATCH 08/18] fix clickstack --- ct/clickhouse.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ct/clickhouse.sh b/ct/clickhouse.sh index 13dbc264..659f05a6 100644 --- a/ct/clickhouse.sh +++ b/ct/clickhouse.sh @@ -83,6 +83,7 @@ function update_script() { exit } +export CLICKSTACK="no" if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "CLICKSTACK" --yesno "Install ClickStack observability stack?\n\n(HyperDX UI + OTel Collector + MongoDB)\nRequires: 4 CPU, 8GB RAM, 30GB Disk" 12 58); then export CLICKSTACK="yes" var_cpu="4" From e287504ab933a3a4e4aaa6d2363820f95a34a052 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Wed, 6 May 2026 10:07:08 +0200 Subject: [PATCH 09/18] fix: improve HyperDX API build process in ClickHouse scripts Co-authored-by: Copilot --- ct/clickhouse.sh | 9 ++++++++- install/clickhouse-install.sh | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/ct/clickhouse.sh b/ct/clickhouse.sh index 659f05a6..4c6ede16 100644 --- a/ct/clickhouse.sh +++ b/ct/clickhouse.sh @@ -51,7 +51,14 @@ function update_script() { msg_info "Building HyperDX" $STD yarn install $STD yarn workspace @hyperdx/common-utils run build - $STD yarn workspace @hyperdx/api run build + rm -rf /opt/clickstack/packages/api/build + yarn workspace @hyperdx/api exec tsc >>"$(get_active_logfile)" 2>&1 || true + $STD yarn workspace @hyperdx/api exec tsc-alias + cp -r /opt/clickstack/packages/api/src/opamp/proto /opt/clickstack/packages/api/build/opamp/ 2>/dev/null || true + [[ -f /opt/clickstack/packages/api/build/index.js ]] || { + msg_error "HyperDX API build failed: build/index.js not found" + exit 1 + } $STD yarn workspace @hyperdx/app run build msg_ok "Built HyperDX" diff --git a/install/clickhouse-install.sh b/install/clickhouse-install.sh index 0564f452..7098b2dc 100644 --- a/install/clickhouse-install.sh +++ b/install/clickhouse-install.sh @@ -207,7 +207,14 @@ EOF msg_info "Building HyperDX" $STD yarn install $STD yarn workspace @hyperdx/common-utils run build - $STD yarn workspace @hyperdx/api run build + rm -rf /opt/clickstack/packages/api/build + yarn workspace @hyperdx/api exec tsc >>"$(get_active_logfile)" 2>&1 || true + $STD yarn workspace @hyperdx/api exec tsc-alias + cp -r /opt/clickstack/packages/api/src/opamp/proto /opt/clickstack/packages/api/build/opamp/ 2>/dev/null || true + [[ -f /opt/clickstack/packages/api/build/index.js ]] || { + msg_error "HyperDX API build failed: build/index.js not found" + exit 1 + } $STD yarn workspace @hyperdx/app run build msg_ok "Built HyperDX" From d1a1c795b71640fabae48a2fb22a889352496a71 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Thu, 7 May 2026 09:36:48 +0200 Subject: [PATCH 10/18] Create ubuntu2604-vm.sh --- vm/ubuntu2604-vm.sh | 591 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 591 insertions(+) create mode 100644 vm/ubuntu2604-vm.sh diff --git a/vm/ubuntu2604-vm.sh b/vm/ubuntu2604-vm.sh new file mode 100644 index 00000000..97aae7d3 --- /dev/null +++ b/vm/ubuntu2604-vm.sh @@ -0,0 +1,591 @@ +#!/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" + __ ____ __ ___ ______ ____ __ __ _ ____ ___ + / / / / /_ __ ______ / /___ __ |__ \ / ____// __ \/ // / | | / / |/ / + / / / / __ \/ / / / __ \/ __/ / / / __/ //___ \ / / / / // /_ | | / / /|_/ / +/ /_/ / /_/ / /_/ / / / / /_/ /_/ / / __/____/ // /_/ /__ __/ | |/ / / / / +\____/_.___/\__,_/_/ /_/\__/\__,_/ /____/_____(_)____/ /_/ |___/_/ /_/ (Plucky Puffin) + +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="ubuntu2604-vm" +var_os="ubuntu" +var_version="2604" + +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}" + +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" + post_update_to_api "failed" "$exit_code" + local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + 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 "Ubuntu 26.04 VM" --yesno "This will create a New Ubuntu 26.04 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.1 +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.1 + if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then + local MINOR="${BASH_REMATCH[1]}" + if ((MINOR < 0 || MINOR > 1)); then + msg_error "This version of Proxmox VE is not supported." + msg_error "Supported: Proxmox VE version 9.0 – 9.1" + 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.1" + 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 default_settings() { + VMID=$(get_valid_nextid) + FORMAT=",efitype=4m" + MACHINE="" + DISK_SIZE="7G" + DISK_CACHE="" + HN="ubuntu" + CPU_TYPE="" + CORE_COUNT="2" + RAM_SIZE="2048" + BRG="vmbr0" + MAC="$GEN_MAC" + VLAN="" + MTU="" + START_VM="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 "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${CREATING}${BOLD}${DGN}Creating a Ubuntu 26.04 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 ubuntu --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $VM_NAME ]; then + HN="ubuntu" + 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 "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 Ubuntu 26.04 VM?" --no-button Do-Over 10 58); then + echo -e "${CREATING}${BOLD}${DGN}Creating a Ubuntu 26.04 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}." +msg_info "Retrieving the URL for the Ubuntu 26.04 Disk Image" +URL=https://cloud-images.ubuntu.com/releases/server/26.04/release/ubuntu-26.04-server-cloudimg-amd64.img +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}" + +STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') +case $STORAGE_TYPE in +nfs | dir | cifs) + 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 Ubuntu 26.04 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 ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null +qm set $VMID \ + -efidisk0 ${DISK0_REF}${FORMAT} \ + -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ + -ide2 ${STORAGE}:cloudinit \ + -boot order=scsi0 \ + -serial0 socket >/dev/null +DESCRIPTION=$( + cat < + + Logo + + +

ubuntu 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 Ubuntu 26.04 VM ${CL}${BL}(${HN})" +if [ "$START_VM" == "yes" ]; then + msg_info "Starting Ubuntu 26.04 VM" + qm start $VMID + msg_ok "Started Ubuntu 26.04 VM" +fi +post_update_to_api "done" "none" +msg_ok "Completed successfully!\n" +echo -e "Setup Cloud-Init before starting \n +More info at https://github.com/community-scripts/ProxmoxVE/discussions/272 \n" From 2f5a5771b0abc7ac2588cc1ec0f4c1111ee45734 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Thu, 7 May 2026 09:45:45 +0200 Subject: [PATCH 11/18] Add interactive VM prompts and error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce interactive whiptail-based helpers and robust error handling for VM creation. - Add error_handler() to report failures (calls post_update_to_api if available), print contextual error info, and call cleanup_vmid. - Ensure TEMP_DIR is removed in cleanup(). - Replace brittle pve_check with version parsing that supports Proxmox VE 8.0–8.9 and 9.0–9.1 (exits with code 105 on unsupported versions). - Add ssh_check() to warn users running the script over SSH. - Add sanitize_vm_hostname() and a suite of vm_* helper functions to prompt and validate interactive settings via whiptail: vm_confirm_new_vm, vm_choose_settings_mode, vm_prompt_vmid, vm_prompt_machine_type, vm_apply_machine_type, vm_prompt_disk_size, vm_prompt_disk_cache, vm_prompt_hostname, vm_prompt_cpu_model, vm_prompt_cpu_cores, vm_prompt_ram, vm_prompt_bridge, vm_prompt_mac, vm_prompt_vlan, vm_prompt_mtu, vm_prompt_start_vm. - Add storage helpers: vm_select_storage, vm_apply_storage_layout, vm_define_disk_references to detect storage pools, set formats/extensions and prepare disk refs. - Use APP/NSAPP for description title by introducing local description_title in set_description(). These changes centralize validation and interactive flow, improve UX, and harden error reporting and cleanup. --- misc/vm-core.func | 467 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 460 insertions(+), 7 deletions(-) diff --git a/misc/vm-core.func b/misc/vm-core.func index c57a1197..fc26a933 100644 --- a/misc/vm-core.func +++ b/misc/vm-core.func @@ -495,6 +495,20 @@ msg_debug() { fi } +error_handler() { + local exit_code="$?" + local line_number="${1:-unknown}" + local command="${2:-unknown}" + + if declare -f post_update_to_api >/dev/null 2>&1; then + post_update_to_api "failed" "$exit_code" + fi + + local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + echo -e "\n$error_message\n" + cleanup_vmid +} + # Displays error message and immediately terminates script fatal() { msg_error "$1" @@ -533,6 +547,9 @@ cleanup() { if [[ "$(dirs -p | wc -l)" -gt 1 ]]; then popd >/dev/null || true fi + if [[ -n "${TEMP_DIR:-}" && -d "$TEMP_DIR" ]]; then + rm -rf "$TEMP_DIR" + fi # Report final telemetry status if post_to_api_vm was called but no update was sent if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then if declare -f post_update_to_api >/dev/null 2>&1; then @@ -557,13 +574,32 @@ check_root() { } pve_check() { - if ! pveversion | grep -Eq "pve-manager/(8\.[1-4]|9\.[0-1])(\.[0-9]+)*"; then - msg_error "This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.1 - 8.4 or 9.0 - 9.1." - echo -e "Exiting..." - sleep 2 - exit + local pve_ver + pve_ver="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" + + 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 + + if [[ "$pve_ver" =~ ^9\.([0-9]+) ]]; then + local minor="${BASH_REMATCH[1]}" + if ((minor < 0 || minor > 1)); then + msg_error "This version of Proxmox VE is not supported." + msg_error "Supported: Proxmox VE version 9.0 – 9.1" + exit 105 + fi + return 0 + fi + + msg_error "This version of Proxmox VE is not supported." + msg_error "Supported versions: Proxmox VE 8.0 – 8.9 or 9.0 – 9.1" + exit 105 } arch_check() { @@ -576,12 +612,427 @@ arch_check() { fi } +ssh_check() { + if command -v pveversion >/dev/null 2>&1 && [ -n "${SSH_CLIENT:-}" ]; 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 + : + else + clear + exit + fi + fi +} + exit_script() { clear echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } +sanitize_vm_hostname() { + local hostname="${1,,}" + hostname=$(echo "$hostname" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') + echo "${hostname:0:63}" +} + +vm_confirm_new_vm() { + local title="$1" + local message="$2" + local height="${3:-10}" + local width="${4:-58}" + + whiptail --backtitle "Proxmox VE Helper Scripts" --title "$title" --yesno "$message" "$height" "$width" +} + +vm_choose_settings_mode() { + local message="${1:-Use Default Settings?}" + local height="${2:-10}" + local width="${3:-58}" + + whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "$message" --no-button Advanced "$height" "$width" +} + +vm_confirm_advanced_settings() { + local message="$1" + local height="${2:-10}" + local width="${3:-58}" + + whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "$message" --no-button Do-Over "$height" "$width" +} + +vm_prompt_vmid() { + local default_vmid="${1:-$(get_valid_nextid)}" + + while true; do + if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 "$default_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_apply_machine_type() { + local machine_type="${1:-i440fx}" + + if [ "$machine_type" = "q35" ]; then + MACHINE_TYPE="q35" + FORMAT="" + MACHINE=" -machine q35" + else + MACHINE_TYPE="i440fx" + FORMAT=",efitype=4m" + MACHINE="" + fi +} + +vm_prompt_machine_type() { + local default_machine="${1:-i440fx}" + local i440fx_default="ON" + local q35_default="OFF" + local machine_choice + + if [ "$default_machine" = "q35" ]; then + i440fx_default="OFF" + q35_default="ON" + fi + + if machine_choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ + "i440fx" "Machine i440fx" "$i440fx_default" \ + "q35" "Machine q35" "$q35_default" \ + 3>&1 1>&2 2>&3); then + vm_apply_machine_type "$machine_choice" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}${MACHINE_TYPE}${CL}" + else + exit_script + fi +} + +vm_prompt_disk_size() { + local default_size="${1:-8G}" + local prompt_message="${2:-Set Disk Size in GiB (e.g., 10, 20)}" + + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "$prompt_message" 8 58 "$default_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 +} + +vm_prompt_disk_cache() { + local default_cache="${1:-none}" + local none_default="ON" + local write_default="OFF" + local cache_choice + + if [ "$default_cache" = "writethrough" ]; then + none_default="OFF" + write_default="ON" + fi + + if cache_choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "None (Default)" "$none_default" \ + "1" "Write Through" "$write_default" \ + 3>&1 1>&2 2>&3); then + if [ "$cache_choice" = "1" ]; then + DISK_CACHE="cache=writethrough," + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" + else + DISK_CACHE="" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + fi + else + exit_script + fi +} + +vm_prompt_hostname() { + local default_hostname="${1:-vm}" + local adjusted_hostname + local input_hostname + + if input_hostname=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$default_hostname" --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$input_hostname" ]; then + HN="$default_hostname" + else + adjusted_hostname=$(sanitize_vm_hostname "$input_hostname") + HN="${adjusted_hostname:-$default_hostname}" + if [ "$HN" != "${input_hostname,,}" ]; 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 + fi + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + else + exit_script + fi +} + +vm_prompt_cpu_model() { + local default_model="${1:-kvm64}" + local kvm_default="ON" + local host_default="OFF" + local cpu_choice + + if [ "$default_model" = "host" ]; then + kvm_default="OFF" + host_default="ON" + fi + + if cpu_choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "KVM64 (Default)" "$kvm_default" \ + "1" "Host" "$host_default" \ + 3>&1 1>&2 2>&3); then + if [ "$cpu_choice" = "1" ]; then + CPU_TYPE=" -cpu host" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" + else + CPU_TYPE="" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + fi + else + exit_script + fi +} + +vm_prompt_cpu_cores() { + local default_cores="${1:-2}" + + while true; do + if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 "$default_cores" --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$CORE_COUNT" ]; then + CORE_COUNT="$default_cores" + 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 +} + +vm_prompt_ram() { + local default_ram="${1:-2048}" + + while true; do + if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 "$default_ram" --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$RAM_SIZE" ]; then + RAM_SIZE="$default_ram" + 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 +} + +vm_prompt_bridge() { + local default_bridge="${1:-vmbr0}" + + if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 "$default_bridge" --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$BRG" ]; then + BRG="$default_bridge" + fi + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else + exit_script + fi +} + +vm_prompt_mac() { + local default_mac="${1:-$GEN_MAC}" + local input_mac + + while true; do + if input_mac=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 "$default_mac" --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$input_mac" ]; then + MAC="$default_mac" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" + break + fi + if [[ "$input_mac" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then + MAC="$input_mac" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${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 +} + +vm_prompt_vlan() { + local default_vlan="${1:-}" + local input_vlan + + while true; do + if input_vlan=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan (leave blank for default)" 8 58 "$default_vlan" --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$input_vlan" ]; then + VLAN="" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" + break + fi + if [[ "$input_vlan" =~ ^[0-9]+$ ]] && [ "$input_vlan" -ge 1 ] && [ "$input_vlan" -le 4094 ]; then + VLAN=",tag=$input_vlan" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$input_vlan${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 +} + +vm_prompt_mtu() { + local default_mtu="${1:-}" + local input_mtu + + while true; do + if input_mtu=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 "$default_mtu" --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$input_mtu" ]; then + MTU="" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + break + fi + if [[ "$input_mtu" =~ ^[0-9]+$ ]] && [ "$input_mtu" -ge 576 ] && [ "$input_mtu" -le 65520 ]; then + MTU=",mtu=$input_mtu" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$input_mtu${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 +} + +vm_prompt_start_vm() { + local default_start="${1:-yes}" + local default_flag=() + + if [ "$default_start" = "no" ]; then + default_flag=(--defaultno) + fi + + if whiptail --backtitle "Proxmox VE Helper Scripts" "${default_flag[@]}" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58; then + START_VM="yes" + else + START_VM="no" + fi + + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}${START_VM}${CL}" +} + +vm_apply_storage_layout() { + local storage_type="$1" + + case $storage_type in + nfs | dir | cifs) + 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 +} + +vm_select_storage() { + local hostname="${1:-${HN:-vm}}" + local storage_menu=() + local msg_max_length=0 + local line tag type free item + local offset=2 + local valid_storage + + 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 " + 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_storage=$(pvesm status -content images | awk 'NR>1') + if [ -z "$valid_storage" ]; then + msg_error "Unable to detect a valid storage location." + exit + elif [ $((${#storage_menu[@]} / 3)) -eq 1 ]; then + STORAGE=${storage_menu[0]} + else + if [ -n "${SPINNER_PID:-}" ] && ps -p "$SPINNER_PID" >/dev/null 2>&1; then + kill "$SPINNER_PID" >/dev/null 2>&1 || true + SPINNER_ACTIVE=0 + printf "\r\e[2K" >&2 + fi + 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 ${hostname}?\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}." + + STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}') + vm_apply_storage_layout "$STORAGE_TYPE" +} + +vm_define_disk_references() { + local disk_count="${1:-2}" + local i disk_name + + for ((i = 0; i < disk_count; i++)); do + disk_name="vm-${VMID}-disk-${i}${DISK_EXT:-}" + printf -v "DISK${i}" '%s' "$disk_name" + printf -v "DISK${i}_REF" '%s' "${STORAGE}:${DISK_REF:-}${disk_name}" + done +} + check_hostname_conflict() { local hostname="$1" if qm list | awk '{print $2}' | grep -qx "$hostname"; then @@ -591,6 +1042,8 @@ check_hostname_conflict() { } set_description() { + local description_title="${APP:-${NSAPP} VM}" + DESCRIPTION=$( cat < @@ -598,7 +1051,7 @@ set_description() { Logo -

${NSAPP} VM

+

${description_title}

From 27f256a20f0d2646ec16d245be131d18f9ef4aa1 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Thu, 7 May 2026 09:52:57 +0200 Subject: [PATCH 12/18] Refactor VM script to use shared vm-core helpers Introduce a shared vm-core loader and refactor the Ubuntu 26.04 VM script to use reusable helper functions. misc/vm-core.func: add COMMUNITY_SCRIPTS_URL default and load_api_functions to dynamically source API helpers, then call load_api_functions from load_functions. vm/ubuntu2604-vm.sh: switch to sourcing the shared vm-core via COMMUNITY_SCRIPTS_URL, replace many inline UI and utility functions with generic vm_* helpers (vm_confirm_new_vm, vm_prompt_*, vm_select_storage, vm_define_disk_references, set_description, etc.), modernize quoting and pushd usage, simplify disk import/resizing logic, and set START_VM default to yes. Also update repository URLs to ProxmoxVED and adjust some behavior (machine type handling, storage/disk references). Overall this centralizes common functionality, reduces duplication, and prepares scripts to use the shared core utilities. --- misc/vm-core.func | 9 + vm/ubuntu2604-vm.sh | 521 ++++---------------------------------------- 2 files changed, 57 insertions(+), 473 deletions(-) diff --git a/misc/vm-core.func b/misc/vm-core.func index fc26a933..a2f0ff0c 100644 --- a/misc/vm-core.func +++ b/misc/vm-core.func @@ -14,9 +14,18 @@ declare -A MSG_INFO_SHOWN [[ -n "${_CORE_FUNC_LOADED:-}" ]] && return _CORE_FUNC_LOADED=1 +COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main}" + +load_api_functions() { + if ! declare -f post_to_api_vm >/dev/null 2>&1; then + source /dev/stdin <<<$(curl -fsSL "$COMMUNITY_SCRIPTS_URL/misc/api.func") + fi +} + load_functions() { [[ -n "${__FUNCTIONS_LOADED:-}" ]] && return __FUNCTIONS_LOADED=1 + load_api_functions color formatting icons diff --git a/vm/ubuntu2604-vm.sh b/vm/ubuntu2604-vm.sh index 97aae7d3..aac821b8 100644 --- a/vm/ubuntu2604-vm.sh +++ b/vm/ubuntu2604-vm.sh @@ -2,9 +2,10 @@ # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) +source <(curl -fsSL "${COMMUNITY_SCRIPTS_URL:-https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main}/misc/vm-core.func") +load_functions function header_info { clear @@ -17,201 +18,39 @@ function header_info { EOF } -header_info -echo -e "\n Loading..." + +APP="Ubuntu 26.04 VM" +APP_TYPE="vm" 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="ubuntu2604-vm" var_os="ubuntu" var_version="2604" - -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}" - 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 -function error_handler() { - local exit_code="$?" - local line_number="$1" - local command="$2" - post_update_to_api "failed" "$exit_code" - local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" - 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 "Ubuntu 26.04 VM" --yesno "This will create a New Ubuntu 26.04 VM. Proceed?" 10 58; then +pushd "$TEMP_DIR" >/dev/null + +if vm_confirm_new_vm "$APP" "This will create a New $APP. Proceed?"; then : else - header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit + header_info && exit_script 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.1 -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.1 - if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then - local MINOR="${BASH_REMATCH[1]}" - if ((MINOR < 0 || MINOR > 1)); then - msg_error "This version of Proxmox VE is not supported." - msg_error "Supported: Proxmox VE version 9.0 – 9.1" - 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.1" - 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 default_settings() { VMID=$(get_valid_nextid) - FORMAT=",efitype=4m" - MACHINE="" + vm_apply_machine_type "i440fx" DISK_SIZE="7G" DISK_CACHE="" HN="ubuntu" @@ -222,10 +61,11 @@ function default_settings() { MAC="$GEN_MAC" VLAN="" MTU="" - START_VM="no" + 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 "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}${MACHINE_TYPE}${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}" @@ -236,209 +76,27 @@ function default_settings() { 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 "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}${START_VM}${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Ubuntu 26.04 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 + vm_prompt_vmid "${VMID:-$(get_valid_nextid)}" + vm_prompt_machine_type "i440fx" + vm_prompt_disk_size "${DISK_SIZE:-7G}" "Set Disk Size in GiB (e.g., 10, 20)" + vm_prompt_disk_cache "none" + vm_prompt_hostname "ubuntu" + 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_start_vm "yes" - 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 ubuntu --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z $VM_NAME ]; then - HN="ubuntu" - 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 "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 Ubuntu 26.04 VM?" --no-button Do-Over 10 58); then + if vm_confirm_advanced_settings "Ready to create a Ubuntu 26.04 VM?"; then echo -e "${CREATING}${BOLD}${DGN}Creating a Ubuntu 26.04 VM using the above advanced settings${CL}" else header_info @@ -448,7 +106,7 @@ function advanced_settings() { } function start_script() { - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then + if vm_choose_settings_mode; then header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" default_settings @@ -458,6 +116,7 @@ function start_script() { advanced_settings fi } + check_root arch_check pve_check @@ -465,127 +124,43 @@ 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}." +vm_select_storage "$HN" +vm_define_disk_references 2 +DISK_IMPORT="-format ${DISK_IMPORT_FORMAT}" + msg_info "Retrieving the URL for the Ubuntu 26.04 Disk Image" -URL=https://cloud-images.ubuntu.com/releases/server/26.04/release/ubuntu-26.04-server-cloudimg-amd64.img +URL="https://cloud-images.ubuntu.com/releases/server/26.04/release/ubuntu-26.04-server-cloudimg-amd64.img" sleep 2 msg_ok "${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" echo -en "\e[1A\e[0K" -FILE=$(basename $URL) +FILE="$(basename "$URL")" msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" -STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') -case $STORAGE_TYPE in -nfs | dir | cifs) - 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 Ubuntu 26.04 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 ${FILE} $STORAGE ${DISK_IMPORT:-} 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=${DISK_SIZE} \ -ide2 ${STORAGE}:cloudinit \ -boot order=scsi0 \ -serial0 socket >/dev/null -DESCRIPTION=$( - cat < - - Logo - +set_description -

ubuntu 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_info "Resizing disk to $DISK_SIZE" +qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null msg_ok "Created a Ubuntu 26.04 VM ${CL}${BL}(${HN})" -if [ "$START_VM" == "yes" ]; then +if [ "$START_VM" = "yes" ]; then msg_info "Starting Ubuntu 26.04 VM" qm start $VMID msg_ok "Started Ubuntu 26.04 VM" fi + post_update_to_api "done" "none" msg_ok "Completed successfully!\n" echo -e "Setup Cloud-Init before starting \n -More info at https://github.com/community-scripts/ProxmoxVE/discussions/272 \n" +More info at https://github.com/community-scripts/ProxmoxVED/discussions/272 \n" From 0c5240302cc7ca78e62505cb3142eba49e5edead Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 7 May 2026 07:53:22 +0000 Subject: [PATCH 13/18] chore: update app headers [skip ci] --- vm/headers/ubuntu2604-vm | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 vm/headers/ubuntu2604-vm diff --git a/vm/headers/ubuntu2604-vm b/vm/headers/ubuntu2604-vm new file mode 100644 index 00000000..92de494a --- /dev/null +++ b/vm/headers/ubuntu2604-vm @@ -0,0 +1,6 @@ + __ ____ __ ___ _____ ____ __ __ _ ____ ___ + / / / / /_ __ ______ / /___ __ |__ \ / ___/ / __ \/ // / | | / / |/ / + / / / / __ \/ / / / __ \/ __/ / / / __/ // __ \ / / / / // /_ | | / / /|_/ / +/ /_/ / /_/ / /_/ / / / / /_/ /_/ / / __// /_/ // /_/ /__ __/ | |/ / / / / +\____/_.___/\__,_/_/ /_/\__/\__,_/ /____/\____(_)____/ /_/ |___/_/ /_/ + From 2a8c6b260f2c57bce2f5a626197de5fd2d207823 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Thu, 7 May 2026 10:16:16 +0200 Subject: [PATCH 14/18] Add optional Cloud-Init and q35 support Introduce optional Cloud-Init integration and better machine type handling for VMs. Changes include: loading cloud-init helpers lazily (load_cloud_init_functions), interactive cloud-init prompt/configuration (vm_prompt_cloud_init) and SSH key handling, a cloud icon for UI, and vm_machine_type_label for readable machine type display. Default machine type switched to q35 and displays the label in prompts and summaries. VM creation logic now conditionally attaches the cloudinit drive (ide2) and runs setup_cloud_init when enabled; otherwise it creates the VM without the cloudinit device. Post-install messaging now either shows cloud-init details or a guidance message about manual guest filesystem expansion. Minor UI/output adjustments and defaults updated accordingly. --- misc/vm-core.func | 42 +++++++++++++++++++++++++++++++++++- vm/ubuntu2604-vm.sh | 52 ++++++++++++++++++++++++++++++++------------- 2 files changed, 78 insertions(+), 16 deletions(-) diff --git a/misc/vm-core.func b/misc/vm-core.func index a2f0ff0c..2c665814 100644 --- a/misc/vm-core.func +++ b/misc/vm-core.func @@ -40,6 +40,12 @@ load_functions() { arch_check } +load_cloud_init_functions() { + if ! declare -f setup_cloud_init >/dev/null 2>&1; then + source <(curl -fsSL "$COMMUNITY_SCRIPTS_URL/misc/cloud-init.func") 2>/dev/null || true + fi +} + # Function to download & save header files get_header() { local app_name=$(echo "${APP,,}" | tr ' ' '-') @@ -107,6 +113,7 @@ icons() { DNSOK="✔️ " DNSFAIL="${TAB}✖️${TAB}" INFO="${TAB}💡${TAB}${CL}" + CLOUD="${TAB}☁️${TAB}${CL}" OS="${TAB}🖥️${TAB}${CL}" OSVERSION="${TAB}🌟${TAB}${CL}" CONTAINERTYPE="${TAB}📦${TAB}${CL}" @@ -704,6 +711,17 @@ vm_apply_machine_type() { fi } +vm_machine_type_label() { + case "${1:-i440fx}" in + q35) + echo "Q35 (Modern)" + ;; + *) + echo "i440fx" + ;; + esac +} + vm_prompt_machine_type() { local default_machine="${1:-i440fx}" local i440fx_default="ON" @@ -720,12 +738,34 @@ vm_prompt_machine_type() { "q35" "Machine q35" "$q35_default" \ 3>&1 1>&2 2>&3); then vm_apply_machine_type "$machine_choice" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}${MACHINE_TYPE}${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$(vm_machine_type_label "$MACHINE_TYPE")${CL}" else exit_script fi } +vm_prompt_cloud_init() { + local default_user="${1:-root}" + + USE_CLOUD_INIT="no" + load_cloud_init_functions + + if ! declare -f configure_cloud_init_interactive >/dev/null 2>&1; then + echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}unavailable${CL}" + return 1 + fi + + configure_cloud_init_interactive "$default_user" || true + USE_CLOUD_INIT="${CLOUDINIT_ENABLE:-no}" + echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}${USE_CLOUD_INIT}${CL}" + + if [ "$USE_CLOUD_INIT" = "yes" ] && declare -f configure_cloudinit_ssh_keys >/dev/null 2>&1; then + configure_cloudinit_ssh_keys || true + fi + + return 0 +} + vm_prompt_disk_size() { local default_size="${1:-8G}" local prompt_message="${2:-Set Disk Size in GiB (e.g., 10, 20)}" diff --git a/vm/ubuntu2604-vm.sh b/vm/ubuntu2604-vm.sh index aac821b8..45c6ea85 100644 --- a/vm/ubuntu2604-vm.sh +++ b/vm/ubuntu2604-vm.sh @@ -28,6 +28,7 @@ NSAPP="ubuntu2604-vm" var_os="ubuntu" var_version="2604" THIN="discard=on,ssd=1," +USE_CLOUD_INIT="no" header_info echo -e "\n Loading..." @@ -48,9 +49,15 @@ else header_info && exit_script fi +check_root +arch_check +pve_check +ssh_check +vm_prompt_cloud_init "ubuntu" + function default_settings() { VMID=$(get_valid_nextid) - vm_apply_machine_type "i440fx" + vm_apply_machine_type "q35" DISK_SIZE="7G" DISK_CACHE="" HN="ubuntu" @@ -65,13 +72,14 @@ function default_settings() { METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}${MACHINE_TYPE}${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$(vm_machine_type_label "$MACHINE_TYPE")${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 "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}${USE_CLOUD_INIT}${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}" @@ -82,8 +90,9 @@ function default_settings() { function advanced_settings() { METHOD="advanced" + echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}${USE_CLOUD_INIT}${CL}" vm_prompt_vmid "${VMID:-$(get_valid_nextid)}" - vm_prompt_machine_type "i440fx" + vm_prompt_machine_type "q35" vm_prompt_disk_size "${DISK_SIZE:-7G}" "Set Disk Size in GiB (e.g., 10, 20)" vm_prompt_disk_cache "none" vm_prompt_hostname "ubuntu" @@ -117,10 +126,6 @@ function start_script() { fi } -check_root -arch_check -pve_check -ssh_check start_script post_to_api_vm @@ -142,17 +147,31 @@ qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -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 $FILE $STORAGE ${DISK_IMPORT:-} 1>&/dev/null -qm set $VMID \ - -efidisk0 ${DISK0_REF}${FORMAT} \ - -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ - -ide2 ${STORAGE}:cloudinit \ - -boot order=scsi0 \ - -serial0 socket >/dev/null +if [ "$USE_CLOUD_INIT" = "yes" ]; then + qm set $VMID \ + -efidisk0 ${DISK0_REF}${FORMAT} \ + -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ + -ide2 ${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 set_description msg_info "Resizing disk to $DISK_SIZE" qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null +if [ "$USE_CLOUD_INIT" = "yes" ] && declare -f setup_cloud_init >/dev/null 2>&1; then + msg_info "Configuring Cloud-Init" + setup_cloud_init "$VMID" "$STORAGE" "$HN" "yes" "${CLOUDINIT_USER:-ubuntu}" "${CLOUDINIT_NETWORK_MODE:-dhcp}" "${CLOUDINIT_IP:-}" "${CLOUDINIT_GW:-}" "${CLOUDINIT_DNS:-${CLOUDINIT_DNS_SERVERS:-1.1.1.1 8.8.8.8}}" + msg_ok "Configured Cloud-Init" +fi + msg_ok "Created a Ubuntu 26.04 VM ${CL}${BL}(${HN})" if [ "$START_VM" = "yes" ]; then msg_info "Starting Ubuntu 26.04 VM" @@ -162,5 +181,8 @@ fi post_update_to_api "done" "none" msg_ok "Completed successfully!\n" -echo -e "Setup Cloud-Init before starting \n -More info at https://github.com/community-scripts/ProxmoxVED/discussions/272 \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 "Cloud-Init is disabled. The VM disk was resized on the Proxmox side only.\nIf the guest does not auto-expand its root filesystem after first boot, expand it manually inside the VM.\n\nMore info at https://github.com/community-scripts/ProxmoxVED/discussions/272 \n" +fi From 4ff70ba166fbd0e9e9e73cd2f06aebfdd1e0bcef Mon Sep 17 00:00:00 2001 From: MickLesk Date: Thu, 7 May 2026 11:24:02 +0200 Subject: [PATCH 15/18] Stop spinner on cleanup & simplify qm set Call stop_spinner in cleanup() to ensure any active spinner is stopped on exit. Consolidate duplicated qm set branches in ubuntu2604-vm.sh into a single invocation to reduce code duplication and simplify VM disk/serial configuration. Remove the explicit ide2 cloudinit device and redundant cloud-init status messages; setup_cloud_init() is still invoked when USE_CLOUD_INIT is enabled, keeping cloud-init configuration centralized. --- misc/vm-core.func | 1 + vm/ubuntu2604-vm.sh | 21 +++++---------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/misc/vm-core.func b/misc/vm-core.func index 2c665814..1383c67e 100644 --- a/misc/vm-core.func +++ b/misc/vm-core.func @@ -560,6 +560,7 @@ cleanup_vmid() { cleanup() { local exit_code=$? + stop_spinner if [[ "$(dirs -p | wc -l)" -gt 1 ]]; then popd >/dev/null || true fi diff --git a/vm/ubuntu2604-vm.sh b/vm/ubuntu2604-vm.sh index 45c6ea85..48d691ee 100644 --- a/vm/ubuntu2604-vm.sh +++ b/vm/ubuntu2604-vm.sh @@ -147,29 +147,18 @@ qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -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 $FILE $STORAGE ${DISK_IMPORT:-} 1>&/dev/null -if [ "$USE_CLOUD_INIT" = "yes" ]; then - qm set $VMID \ - -efidisk0 ${DISK0_REF}${FORMAT} \ - -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ - -ide2 ${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 +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 msg_info "Resizing disk to $DISK_SIZE" qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null if [ "$USE_CLOUD_INIT" = "yes" ] && declare -f setup_cloud_init >/dev/null 2>&1; then - msg_info "Configuring Cloud-Init" setup_cloud_init "$VMID" "$STORAGE" "$HN" "yes" "${CLOUDINIT_USER:-ubuntu}" "${CLOUDINIT_NETWORK_MODE:-dhcp}" "${CLOUDINIT_IP:-}" "${CLOUDINIT_GW:-}" "${CLOUDINIT_DNS:-${CLOUDINIT_DNS_SERVERS:-1.1.1.1 8.8.8.8}}" - msg_ok "Configured Cloud-Init" fi msg_ok "Created a Ubuntu 26.04 VM ${CL}${BL}(${HN})" From df5d0679ecbb1eff6296a0a7ef780d8b17ba922d Mon Sep 17 00:00:00 2001 From: MickLesk Date: Fri, 8 May 2026 09:14:20 +0200 Subject: [PATCH 16/18] Add Stoatchat and xyOps container/install scripts Introduce new container templates, installers, and metadata for Stoatchat and xyOps. Adds ct scripts (ct/stoatchat.sh, ct/xyops.sh), full install scripts (install/stoatchat-install.sh, install/xyops-install.sh) that provision dependencies, build components, and create systemd services, plus app metadata JSON (json/stoatchat.json, json/xyops.json). Stoatchat installer handles Rust backend build, SolidJS frontend build, MinIO, RabbitMQ, MongoDB, nginx reverse proxy and multiple backend services (exposes on port 80). xyOps installer builds the Node app, sets up the xySat satellite, service unit, and uses port 5522 for the web UI. Default resource recommendations and notes are included in the JSON metadata. --- ct/stoatchat.sh | 85 ++++++++++++ ct/xyops.sh | 72 +++++++++++ install/stoatchat-install.sh | 242 +++++++++++++++++++++++++++++++++++ install/xyops-install.sh | 73 +++++++++++ json/stoatchat.json | 52 ++++++++ json/xyops.json | 48 +++++++ 6 files changed, 572 insertions(+) create mode 100644 ct/stoatchat.sh create mode 100644 ct/xyops.sh create mode 100644 install/stoatchat-install.sh create mode 100644 install/xyops-install.sh create mode 100644 json/stoatchat.json create mode 100644 json/xyops.json diff --git a/ct/stoatchat.sh b/ct/stoatchat.sh new file mode 100644 index 00000000..bbdf1140 --- /dev/null +++ b/ct/stoatchat.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/stoatchat/stoatchat + +APP="Stoatchat" +var_tags="${var_tags:-chat;messaging;community}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-8192}" +var_disk="${var_disk:-30}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/stoatchat ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "stoatchat" "stoatchat/stoatchat"; then + msg_info "Stopping Services" + systemctl stop stoatchat-api stoatchat-events stoatchat-autumn stoatchat-january stoatchat-crond + msg_ok "Stopped Services" + + msg_info "Backing up Configuration" + cp /Revolt.toml /opt/stoatchat_revolt.toml.bak + msg_ok "Backed up Configuration" + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "stoatchat" "stoatchat/stoatchat" "tarball" + + msg_info "Rebuilding Backend (Patience)" + cd /opt/stoatchat + $STD cargo build --release --bins + msg_ok "Rebuilt Backend" + + msg_info "Updating Web Frontend" + FORWEB_VERSION=$(get_latest_github_release "stoatchat/for-web") + $STD git -C /opt/stoatchat-web fetch --tags + $STD git -C /opt/stoatchat-web checkout "$FORWEB_VERSION" + $STD git -C /opt/stoatchat-web submodule update --init --recursive + cd /opt/stoatchat-web + $STD pnpm install --frozen-lockfile + $STD pnpm --filter stoat.js build + $STD pnpm --filter solid-livekit-components build + $STD pnpm --filter "@lingui-solid/babel-plugin-lingui-macro" build + $STD pnpm --filter "@lingui-solid/babel-plugin-extract-messages" build + $STD pnpm --filter client exec lingui compile --typescript + $STD pnpm --filter client exec node scripts/copyAssets.mjs + $STD pnpm --filter client exec panda codegen + $STD pnpm --filter client exec vite build + msg_ok "Updated Web Frontend" + + msg_info "Restoring Configuration" + cp /opt/stoatchat_revolt.toml.bak /Revolt.toml + rm -f /opt/stoatchat_revolt.toml.bak + msg_ok "Restored Configuration" + + msg_info "Starting Services" + systemctl start stoatchat-api stoatchat-events stoatchat-autumn stoatchat-january stoatchat-crond + msg_ok "Started Services" + msg_ok "Updated successfully!" + fi + 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}http://${IP}${CL}" diff --git a/ct/xyops.sh b/ct/xyops.sh new file mode 100644 index 00000000..43861b3a --- /dev/null +++ b/ct/xyops.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/pixlcore/xyops + +APP="xyOps" +var_tags="${var_tags:-scheduler;automation;monitoring}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/xyops ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "xyops" "pixlcore/xyops"; then + msg_info "Stopping Service" + systemctl stop xyops + msg_ok "Stopped Service" + + msg_info "Backing up Data" + cp -r /opt/xyops/data /opt/xyops_data_backup + cp -r /opt/xyops/conf /opt/xyops_conf_backup + msg_ok "Backed up Data" + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "xyops" "pixlcore/xyops" "tarball" + + msg_info "Rebuilding Application" + cd /opt/xyops + $STD npm install + $STD node bin/build.js dist + chmod 644 /opt/xyops/node_modules/useragent-ng/lib/regexps.js + msg_ok "Rebuilt Application" + + msg_info "Restoring Data" + cp -r /opt/xyops_data_backup/. /opt/xyops/data + cp -r /opt/xyops_conf_backup/. /opt/xyops/conf + rm -rf /opt/xyops_data_backup /opt/xyops_conf_backup + msg_ok "Restored Data" + + msg_info "Starting Service" + systemctl start xyops + msg_ok "Started Service" + msg_ok "Updated successfully!" + fi + 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}http://${IP}:5522${CL}" diff --git a/install/stoatchat-install.sh b/install/stoatchat-install.sh new file mode 100644 index 00000000..44ea4940 --- /dev/null +++ b/install/stoatchat-install.sh @@ -0,0 +1,242 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/stoatchat/stoatchat + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y \ + pkg-config \ + libssl-dev \ + build-essential \ + git \ + redis-server \ + rabbitmq-server \ + nginx +msg_ok "Installed Dependencies" + +setup_mongodb + +msg_info "Configuring RabbitMQ" +systemctl enable -q --now rabbitmq-server +until rabbitmqctl status &>/dev/null; do sleep 1; done +$STD rabbitmqctl add_user rabbituser rabbitpass +$STD rabbitmqctl set_permissions -p / rabbituser ".*" ".*" ".*" +msg_ok "Configured RabbitMQ" + +setup_rust + +fetch_and_deploy_gh_release "stoatchat" "stoatchat/stoatchat" "tarball" + +msg_info "Building Backend (Patience)" +cd /opt/stoatchat +$STD cargo build --release --bins +msg_ok "Built Backend" + +NODE_VERSION="22" setup_nodejs + +msg_info "Installing pnpm" +$STD npm install -g pnpm@10.28.1 +msg_ok "Installed pnpm" + +msg_info "Cloning Web Frontend" +FORWEB_VERSION=$(get_latest_github_release "stoatchat/for-web") +$STD git clone --recursive "https://github.com/stoatchat/for-web" /opt/stoatchat-web +$STD git -C /opt/stoatchat-web checkout "$FORWEB_VERSION" +$STD git -C /opt/stoatchat-web submodule update --init --recursive +msg_ok "Cloned Web Frontend" + +msg_info "Building Web Frontend" +cd /opt/stoatchat-web +$STD pnpm install --frozen-lockfile +$STD pnpm --filter stoat.js build +$STD pnpm --filter solid-livekit-components build +$STD pnpm --filter "@lingui-solid/babel-plugin-lingui-macro" build +$STD pnpm --filter "@lingui-solid/babel-plugin-extract-messages" build +$STD pnpm --filter client exec lingui compile --typescript +$STD pnpm --filter client exec node scripts/copyAssets.mjs +$STD pnpm --filter client exec panda codegen +VITE_API_URL="http://${LOCAL_IP}/api" \ + VITE_WS_URL="ws://${LOCAL_IP}/ws" \ + VITE_MEDIA_URL="http://${LOCAL_IP}/autumn" \ + VITE_PROXY_URL="http://${LOCAL_IP}/january" \ + $STD pnpm --filter client exec vite build +msg_ok "Built Web Frontend" + +fetch_and_deploy_gh_release "minio" "minio/minio" "singlefile" "latest" "/opt/stoatchat" "minio_linux_amd64" +mv /opt/stoatchat/minio_linux_amd64 /usr/local/bin/minio +chmod +x /usr/local/bin/minio + +fetch_and_deploy_gh_release "mc" "minio/mc" "singlefile" "latest" "/opt/stoatchat" "mc_linux_amd64" +mv /opt/stoatchat/mc_linux_amd64 /usr/local/bin/mc +chmod +x /usr/local/bin/mc + +msg_info "Configuring MinIO" +mkdir -p /opt/stoatchat/data/minio +cat </etc/systemd/system/stoatchat-minio.service +[Unit] +Description=Stoatchat MinIO Object Storage +After=network.target + +[Service] +Type=simple +User=root +Environment=MINIO_ROOT_USER=minioautumn +Environment=MINIO_ROOT_PASSWORD=minioautumn +ExecStart=/usr/local/bin/minio server /opt/stoatchat/data/minio --console-address :9001 +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now stoatchat-minio +msg_ok "Configured MinIO" + +msg_info "Creating MinIO Bucket" +until mc alias set local http://127.0.0.1:9000 minioautumn minioautumn &>/dev/null; do sleep 1; done +$STD mc mb local/revolt-uploads +msg_ok "Created MinIO Bucket" + +FILES_ENCRYPTION_KEY=$(openssl rand -base64 32) + +msg_info "Creating Configuration" +cat </Revolt.toml +[database] +mongodb = "mongodb://127.0.0.1:27017" +redis = "redis://127.0.0.1:6379/" + +[hosts] +app = "http://${LOCAL_IP}" +api = "http://${LOCAL_IP}/api" +events = "ws://${LOCAL_IP}/ws" +autumn = "http://${LOCAL_IP}/autumn" +january = "http://${LOCAL_IP}/january" + +[rabbit] +host = "127.0.0.1" +port = 5672 +username = "rabbituser" +password = "rabbitpass" + +[files] +encryption_key = "${FILES_ENCRYPTION_KEY}" + +[files.s3] +endpoint = "http://127.0.0.1:9000" +path_style_buckets = true +region = "minio" +access_key_id = "minioautumn" +secret_access_key = "minioautumn" +default_bucket = "revolt-uploads" + +[api.registration] +invite_only = false +EOF +ln -sf /Revolt.toml /opt/stoatchat/Revolt.toml +msg_ok "Created Configuration" + +msg_info "Configuring Nginx" +cat </etc/nginx/sites-available/stoatchat +server { + listen 80; + + client_max_body_size 20M; + + location /api { + proxy_pass http://127.0.0.1:14702; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + } + + location /ws { + proxy_pass http://127.0.0.1:14703; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + } + + location /autumn { + proxy_pass http://127.0.0.1:14704; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + } + + location /january { + proxy_pass http://127.0.0.1:14705; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + } + + location / { + root /opt/stoatchat-web/packages/client/dist; + try_files \$uri \$uri/ /index.html; + } +} +EOF +ln -sf /etc/nginx/sites-available/stoatchat /etc/nginx/sites-enabled/stoatchat +rm -f /etc/nginx/sites-enabled/default +systemctl enable -q --now nginx +msg_ok "Configured Nginx" + +msg_info "Creating Backend Services" +for SVC in api events autumn january crond; do + case $SVC in + api) + PORT=14702 + BIN=delta + ;; + events) + PORT=14703 + BIN=bonfire + ;; + autumn) + PORT=14704 + BIN=autumn + ;; + january) + PORT=14705 + BIN=january + ;; + crond) + PORT=0 + BIN=crond + ;; + esac + cat </etc/systemd/system/stoatchat-${SVC}.service +[Unit] +Description=Stoatchat ${SVC} service +After=network.target stoatchat-minio.service + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/stoatchat +ExecStart=/opt/stoatchat/target/release/${BIN} +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF + systemctl enable -q --now "stoatchat-${SVC}" +done +msg_ok "Created Backend Services" + +motd_ssh +customize +cleanup_lxc diff --git a/install/xyops-install.sh b/install/xyops-install.sh new file mode 100644 index 00000000..a9d41db2 --- /dev/null +++ b/install/xyops-install.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/pixlcore/xyops + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y \ + build-essential \ + python3 \ + python3-setuptools \ + pkg-config \ + libssl-dev \ + zlib1g-dev +msg_ok "Installed Dependencies" + +NODE_VERSION="22" setup_nodejs + +fetch_and_deploy_gh_release "xyops" "pixlcore/xyops" "tarball" + +msg_info "Building Application" +cd /opt/xyops +$STD npm install +$STD node bin/build.js dist +chmod 644 /opt/xyops/node_modules/useragent-ng/lib/regexps.js +msg_ok "Built Application" + +fetch_and_deploy_gh_release "xysat" "pixlcore/xysat" "tarball" "latest" "/opt/xyops/satellite" + +msg_info "Building xySat Satellite" +cd /opt/xyops/satellite +$STD npm install +msg_ok "Built xySat Satellite" + +msg_info "Setting up Directories" +mkdir -p /opt/xyops/data /opt/xyops/logs /opt/xyops/temp /opt/xyops/conf +msg_ok "Set up Directories" + +msg_info "Creating Service" +cat </etc/systemd/system/xyops.service +[Unit] +Description=xyOps Task Scheduler and Server Monitor +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/xyops +Environment=XYOPS_foreground=1 +Environment=XYOPS_xysat_local=1 +Environment=XYOPS_masters=${LOCAL_IP} +ExecStart=/usr/bin/node /opt/xyops/lib/main.js +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now xyops +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc diff --git a/json/stoatchat.json b/json/stoatchat.json new file mode 100644 index 00000000..0db6f99d --- /dev/null +++ b/json/stoatchat.json @@ -0,0 +1,52 @@ +{ + "name": "Stoatchat", + "slug": "stoatchat", + "categories": [ + 22 + ], + "date_created": "2026-05-08", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 80, + "documentation": "https://github.com/stoatchat/self-hosted", + "website": "https://stoat.chat", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/stoatchat.webp", + "description": "A self-hostable open-source chat platform and community server. Stoatchat is a fork of Revolt, featuring real-time messaging, voice channels, file sharing, and a full-featured web client. Built with Rust (backend) and SolidJS (frontend).", + "install_methods": [ + { + "type": "default", + "script": "ct/stoatchat.sh", + "config_path": "/Revolt.toml", + "resources": { + "cpu": 4, + "ram": 8192, + "hdd": 30, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Initial setup takes 30-60 minutes due to Rust compilation and frontend build. Do not interrupt the process.", + "type": "warning" + }, + { + "text": "The first account registered becomes the instance administrator. Registration is open by default; set invite_only = true in /Revolt.toml to restrict it.", + "type": "info" + }, + { + "text": "Voice and video calls require additional LiveKit setup. See https://github.com/stoatchat/self-hosted for details.", + "type": "info" + }, + { + "text": "The files encryption key in /Revolt.toml is generated during installation. Back it up — losing it will make all uploaded files unreadable.", + "type": "warning" + } + ] +} \ No newline at end of file diff --git a/json/xyops.json b/json/xyops.json new file mode 100644 index 00000000..2f8c31b0 --- /dev/null +++ b/json/xyops.json @@ -0,0 +1,48 @@ +{ + "name": "xyOps", + "slug": "xyops", + "categories": [ + 19 + ], + "date_created": "2026-05-08", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 5522, + "documentation": "https://github.com/pixlcore/xyops/tree/main/docs", + "website": "https://github.com/pixlcore/xyops", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/xyops.webp", + "description": "A complete task scheduler and server monitoring system with workflow automation, multi-server management, real-time monitoring, and a built-in satellite agent (xySat) for running jobs on remote servers.", + "install_methods": [ + { + "type": "default", + "script": "ct/xyops.sh", + "config_path": "/opt/xyops/conf/config.json", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 8, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": "admin", + "password": "admin" + }, + "notes": [ + { + "text": "Change the default admin password immediately after first login.", + "type": "warning" + }, + { + "text": "A local xySat satellite is started automatically alongside the conductor, allowing jobs to run on this server right away.", + "type": "info" + }, + { + "text": "The WebSocket port 5523 is used for secure wss:// connections. Port 5522 serves the web interface and ws:// connections.", + "type": "info" + } + ] +} \ No newline at end of file From 826ebc3c84c64225d8a8033ff588a0b178cab558 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 8 May 2026 07:14:47 +0000 Subject: [PATCH 17/18] chore: update app headers [skip ci] --- ct/headers/stoatchat | 6 ++++++ ct/headers/xyops | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 ct/headers/stoatchat create mode 100644 ct/headers/xyops diff --git a/ct/headers/stoatchat b/ct/headers/stoatchat new file mode 100644 index 00000000..870f0c19 --- /dev/null +++ b/ct/headers/stoatchat @@ -0,0 +1,6 @@ + _____ __ __ __ __ + / ___// /_____ ____ _/ /______/ /_ ____ _/ /_ + \__ \/ __/ __ \/ __ `/ __/ ___/ __ \/ __ `/ __/ + ___/ / /_/ /_/ / /_/ / /_/ /__/ / / / /_/ / /_ +/____/\__/\____/\__,_/\__/\___/_/ /_/\__,_/\__/ + diff --git a/ct/headers/xyops b/ct/headers/xyops new file mode 100644 index 00000000..7a8be0a2 --- /dev/null +++ b/ct/headers/xyops @@ -0,0 +1,6 @@ + ____ + _ ____ __/ __ \____ _____ + | |/_/ / / / / / / __ \/ ___/ + _> Date: Fri, 8 May 2026 10:19:46 +0200 Subject: [PATCH 18/18] Bump Stoatchat RAM, limit cargo, remove xyops env Increase Stoatchat default RAM from 8192 to 10240 in ct/stoatchat.sh and json/stoatchat.json. Add cargo -j 2 to backend builds in ct/stoatchat.sh and install/stoatchat-install.sh to limit parallel jobs. Remove two environment variables (XYOPS_xysat_local and XYOPS_masters) from install/xyops-install.sh service definition to avoid hardcoded local settings. --- ct/stoatchat.sh | 4 ++-- install/stoatchat-install.sh | 2 +- install/xyops-install.sh | 2 -- json/stoatchat.json | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ct/stoatchat.sh b/ct/stoatchat.sh index bbdf1140..432fcfff 100644 --- a/ct/stoatchat.sh +++ b/ct/stoatchat.sh @@ -8,7 +8,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV APP="Stoatchat" var_tags="${var_tags:-chat;messaging;community}" var_cpu="${var_cpu:-4}" -var_ram="${var_ram:-8192}" +var_ram="${var_ram:-10240}" var_disk="${var_disk:-30}" var_os="${var_os:-debian}" var_version="${var_version:-13}" @@ -42,7 +42,7 @@ function update_script() { msg_info "Rebuilding Backend (Patience)" cd /opt/stoatchat - $STD cargo build --release --bins + $STD cargo build --release --bins -j 2 msg_ok "Rebuilt Backend" msg_info "Updating Web Frontend" diff --git a/install/stoatchat-install.sh b/install/stoatchat-install.sh index 44ea4940..ea2652a3 100644 --- a/install/stoatchat-install.sh +++ b/install/stoatchat-install.sh @@ -39,7 +39,7 @@ fetch_and_deploy_gh_release "stoatchat" "stoatchat/stoatchat" "tarball" msg_info "Building Backend (Patience)" cd /opt/stoatchat -$STD cargo build --release --bins +$STD cargo build --release --bins -j 2 msg_ok "Built Backend" NODE_VERSION="22" setup_nodejs diff --git a/install/xyops-install.sh b/install/xyops-install.sh index a9d41db2..67c2627a 100644 --- a/install/xyops-install.sh +++ b/install/xyops-install.sh @@ -56,8 +56,6 @@ Type=simple User=root WorkingDirectory=/opt/xyops Environment=XYOPS_foreground=1 -Environment=XYOPS_xysat_local=1 -Environment=XYOPS_masters=${LOCAL_IP} ExecStart=/usr/bin/node /opt/xyops/lib/main.js Restart=on-failure RestartSec=5 diff --git a/json/stoatchat.json b/json/stoatchat.json index 0db6f99d..412117e8 100644 --- a/json/stoatchat.json +++ b/json/stoatchat.json @@ -20,7 +20,7 @@ "config_path": "/Revolt.toml", "resources": { "cpu": 4, - "ram": 8192, + "ram": 10240, "hdd": 30, "os": "Debian", "version": "13"