From 087f817bf614d521c0d003895295d32e3269894f Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Sat, 7 Mar 2026 15:44:44 +1000 Subject: [PATCH 01/22] misc scripts: add support for arm64 --- misc/build.func | 541 ++++++++++++++++++++++++++++------------------ misc/core.func | 7 +- misc/install.func | 1 + misc/tools.func | 2 +- 4 files changed, 334 insertions(+), 217 deletions(-) diff --git a/misc/build.func b/misc/build.func index 706e5b57c50..16bebd78279 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3425,6 +3425,9 @@ start() { set_std_mode ensure_profile_loaded get_lxc_ip + if [[ "$(dpkg --print-architecture)" == "arm64" ]] && declare -f update_script_arm64 >/dev/null 2>&1; then + update_script_arm64 + fi update_script update_motd_ip cleanup_lxc @@ -3453,6 +3456,9 @@ start() { esac ensure_profile_loaded get_lxc_ip + if [[ "$(dpkg --print-architecture)" == "arm64" ]] && declare -f update_script_arm64 >/dev/null 2>&1; then + update_script_arm64 + fi update_script update_motd_ip cleanup_lxc @@ -4096,7 +4102,16 @@ EOF' # that sends "configuring" status AFTER the host already reported "failed" export CONTAINER_INSTALLING=true - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh)" + local _install_script + _install_script="$(curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh")" + if [[ "$ARCH" == "arm64" ]]; then + local _arm_script + _arm_script="$(curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/arm/${var_install}.sh" 2>/dev/null || true)" + if [[ -n "$_arm_script" ]]; then + _install_script="${_arm_script}"$'\n'"${_install_script}" + fi + fi + lxc-attach -n "$CTID" -- bash -c "$_install_script" local lxc_exit=$? unset CONTAINER_INSTALLING @@ -4474,7 +4489,16 @@ EOF' # Re-run install script in existing container (don't destroy/recreate) set +Eeuo pipefail trap - ERR - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh)" + local _install_script + _install_script="$(curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh")" + if [[ "$ARCH" == "arm64" ]]; then + local _arm_script + _arm_script="$(curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/arm/${var_install}.sh" 2>/dev/null || true)" + if [[ -n "$_arm_script" ]]; then + _install_script="${_arm_script}"$'\n'"${_install_script}" + fi + fi + lxc-attach -n "$CTID" -- bash -c "$_install_script" local apt_retry_exit=$? set -Eeuo pipefail trap 'error_handler' ERR @@ -4993,6 +5017,64 @@ create_lxc_container() { esac } + ARCH="$(dpkg --print-architecture)" + + # Maps OS type + version to the release variant name used by ARM64 template sources. + arm64_template_variant() { + case "$1" in + debian) + case "$2" in + 11 | 11.*) echo "bullseye" ;; 12 | 12.*) echo "bookworm" ;; + 13 | 13.*) echo "trixie" ;; *) echo "trixie" ;; + esac ;; + alpine) echo "3.22" ;; + ubuntu) + case "$2" in + 20.04* | focal) echo "focal" ;; 24.04* | noble) echo "noble" ;; + 24.10* | oracular) echo "oracular" ;; *) echo "jammy" ;; + esac ;; + *) return 1 ;; + esac + } + + # Downloads an ARM64 LXC rootfs template to $1. + # Debian: fetches latest release from asylumexp/debian-ifupdown2-lxc on GitHub. + # Others: fetches from jenkins.linuxcontainers.org. + download_arm64_template() { + local dest="$1" url + + mkdir -p "$(dirname "$dest")" || { msg_error "Cannot create template dir."; exit 207; } + + if [[ "$PCT_OSTYPE" == "debian" ]]; then + url=$(curl -fsSL "https://api.github.com/repos/asylumexp/debian-ifupdown2-lxc/releases/latest" \ + | grep -Eo "https://[^\"]*debian-${CUSTOM_TEMPLATE_VARIANT}-arm64-rootfs\.tar\.xz" | head -n1) + [[ -n "$url" ]] || { msg_error "Could not find Debian ${CUSTOM_TEMPLATE_VARIANT} ARM64 template URL."; exit 207; } + else + url="https://jenkins.linuxcontainers.org/job/image-${PCT_OSTYPE}/architecture=arm64,release=${CUSTOM_TEMPLATE_VARIANT},variant=default/lastStableBuild/artifact/rootfs.tar.xz" + fi + + msg_info "Downloading ${PCT_OSTYPE^} ${CUSTOM_TEMPLATE_VARIANT} ARM64 template" + if ! curl -fsSL -o "$dest" "$url"; then + msg_error "Failed to download ARM64 template from: $url" + exit 208 + fi + msg_ok "Downloaded ARM64 LXC template" + } + + # Architecture-aware template download wrapper. + # Optional $1 overrides destination path (for local-storage fallback). + download_template() { + local dest="${1:-$TEMPLATE_PATH}" + if [[ "$ARCH" == "arm64" ]]; then + download_arm64_template "$dest" + else + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >>"${BUILD_LOG:-/dev/null}" 2>&1 || { + msg_error "Failed to download template '$TEMPLATE' to storage '$TEMPLATE_STORAGE'" + exit 222 + } + fi + } + # ------------------------------------------------------------------------------ # Required input variables # ------------------------------------------------------------------------------ @@ -5129,153 +5211,116 @@ create_lxc_container() { # ------------------------------------------------------------------------------ # Template discovery & validation # ------------------------------------------------------------------------------ - TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" - case "$PCT_OSTYPE" in - debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;; - alpine | fedora | rocky | centos) TEMPLATE_PATTERN="-default_" ;; - *) TEMPLATE_PATTERN="" ;; - esac + CUSTOM_TEMPLATE_VARIANT="" - msg_info "Searching for template '$TEMPLATE_SEARCH'" + if [[ "$ARCH" == "arm64" ]]; then + # ARM64: use custom template download from linuxcontainers.org / GitHub + msg_info "Preparing ARM64 template" - # Initialize variables - ONLINE_TEMPLATE="" - ONLINE_TEMPLATES=() + CUSTOM_TEMPLATE_VARIANT=$(arm64_template_variant "$PCT_OSTYPE" "${PCT_OSVERSION:-}") || { + msg_error "No ARM64 template mapping for ${PCT_OSTYPE} ${PCT_OSVERSION:-latest}" + exit 207 + } - # Step 1: Check local templates first (instant) - mapfile -t LOCAL_TEMPLATES < <( - pveam list "$TEMPLATE_STORAGE" 2>/dev/null | - awk -v search="${TEMPLATE_SEARCH}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | - sed 's|.*/||' | sort -t - -k 2 -V - ) + TEMPLATE="${PCT_OSTYPE}-${CUSTOM_TEMPLATE_VARIANT}-rootfs.tar.xz" + TEMPLATE_SOURCE="custom-arm64" - # Step 2: If local template found, use it immediately (skip pveam update) - if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then - TEMPLATE="${LOCAL_TEMPLATES[-1]}" - TEMPLATE_SOURCE="local" - msg_ok "Template search completed" - else - # Step 3: No local template - need to check online (this may be slow) - msg_info "No local template found, checking online catalog..." + # Resolve template path: pvesm → storage.cfg fallback → default + TEMPLATE_PATH="$(pvesm path "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" 2>/dev/null || true)" + if [[ -z "$TEMPLATE_PATH" ]]; then + local _tpl_base + _tpl_base=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + TEMPLATE_PATH="${_tpl_base:-/var/lib/vz}/template/cache/$TEMPLATE" + fi - # Update catalog with timeout to prevent long hangs - if command -v timeout &>/dev/null; then - if ! timeout 30 pveam update >/dev/null 2>&1; then - msg_warn "Template catalog update timed out (possible network/DNS issue). Run 'pveam update' manually to diagnose." - fi + # Download if missing, too small, or corrupt (single pass) + if [[ ! -f "$TEMPLATE_PATH" ]]; then + download_arm64_template "$TEMPLATE_PATH" + elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]] || ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + msg_warn "Local template invalid – re-downloading." + rm -f "$TEMPLATE_PATH" + download_arm64_template "$TEMPLATE_PATH" else - pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)" + msg_ok "Template ${BL}$TEMPLATE${CL} found locally." fi - ONLINE_TEMPLATES=() - mapfile -t ONLINE_TEMPLATES < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk '{print $2}' | grep -E "^${TEMPLATE_SEARCH}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) - [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" - - TEMPLATE="$ONLINE_TEMPLATE" - TEMPLATE_SOURCE="online" - msg_ok "Template search completed" - fi - - # If still no template, try to find alternatives - if [[ -z "$TEMPLATE" ]]; then - msg_warn "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..." - - # Get all available versions for this OS type - AVAILABLE_VERSIONS=() - mapfile -t AVAILABLE_VERSIONS < <( - pveam available -section system 2>/dev/null | - grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk -F'\t' '{print $1}' | - grep "^${PCT_OSTYPE}-" | - sed -E "s/.*${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" | - sort -u -V 2>/dev/null - ) + else + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + case "$PCT_OSTYPE" in + debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;; + alpine | fedora | rocky | centos) TEMPLATE_PATTERN="-default_" ;; + *) TEMPLATE_PATTERN="" ;; + esac - if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then - echo "" - echo "${BL}Available ${PCT_OSTYPE} versions:${CL}" - for i in "${!AVAILABLE_VERSIONS[@]}"; do - echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" - done - echo "" - read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or press Enter to cancel: " choice /dev/null | - grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk '{print $2}' | - grep -E "^${TEMPLATE_SEARCH}-.*${TEMPLATE_PATTERN}" | - sort -t - -k 2 -V 2>/dev/null || true - ) + # Step 1: Check local templates first (instant) + mapfile -t LOCAL_TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" 2>/dev/null | + awk -v search="${TEMPLATE_SEARCH}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) - if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then - TEMPLATE="${ONLINE_TEMPLATES[-1]}" - TEMPLATE_SOURCE="online" - else - msg_error "No templates available for ${PCT_OSTYPE} ${PCT_OSVERSION}" - exit 225 + # Step 2: If local template found, use it immediately (skip pveam update) + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + msg_ok "Template search completed" + else + # Step 3: No local template - need to check online (this may be slow) + msg_info "No local template found, checking online catalog..." + + # Update catalog with timeout to prevent long hangs + if command -v timeout &>/dev/null; then + if ! timeout 30 pveam update >/dev/null 2>&1; then + msg_warn "Template catalog update timed out (possible network/DNS issue). Run 'pveam update' manually to diagnose." fi else - msg_custom "đŸšĢ" "${YW}" "Installation cancelled" - exit 0 + pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)" fi - else - msg_error "No ${PCT_OSTYPE} templates available at all" - exit 225 - fi - fi - TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" - if [[ -z "$TEMPLATE_PATH" ]]; then - TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) - [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" - fi + ONLINE_TEMPLATES=() + mapfile -t ONLINE_TEMPLATES < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk '{print $2}' | grep -E "^${TEMPLATE_SEARCH}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" - # If we still don't have a path but have a valid template name, construct it - if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then - TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" - fi + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + msg_ok "Template search completed" + fi - [[ -n "$TEMPLATE_PATH" ]] || { + # If still no template, try to find alternatives if [[ -z "$TEMPLATE" ]]; then - msg_error "Template ${PCT_OSTYPE} ${PCT_OSVERSION} not available" + msg_warn "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..." - # Get available versions + # Get all available versions for this OS type + AVAILABLE_VERSIONS=() mapfile -t AVAILABLE_VERSIONS < <( pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | grep "^${PCT_OSTYPE}-" | - sed -E 's/.*'"${PCT_OSTYPE}"'-([0-9]+\.[0-9]+).*/\1/' | - grep -E '^[0-9]+\.[0-9]+$' | - sort -u -V 2>/dev/null || sort -u + sed -E "s/.*${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" | + sort -u -V 2>/dev/null ) if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then - echo -e "\n${BL}Available versions:${CL}" + echo "" + echo "${BL}Available ${PCT_OSTYPE} versions:${CL}" for i in "${!AVAILABLE_VERSIONS[@]}"; do echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" done - echo "" - read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or Enter to exit: " choice /dev/null | - awk -v search="${TEMPLATE_SEARCH}-" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | - sed 's|.*/||' | sort -t - -k 2 -V - ) + ONLINE_TEMPLATES=() mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | @@ -5283,109 +5328,181 @@ create_lxc_container() { grep -E "^${TEMPLATE_SEARCH}-.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true ) - ONLINE_TEMPLATE="" - [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" - if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then - TEMPLATE="${LOCAL_TEMPLATES[-1]}" - TEMPLATE_SOURCE="local" - else - TEMPLATE="$ONLINE_TEMPLATE" + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${ONLINE_TEMPLATES[-1]}" TEMPLATE_SOURCE="online" + else + msg_error "No templates available for ${PCT_OSTYPE} ${PCT_OSVERSION}" + exit 225 fi - - TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" - if [[ -z "$TEMPLATE_PATH" ]]; then - TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) - [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" - fi - - # If we still don't have a path but have a valid template name, construct it - if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then - TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" - fi - - [[ -n "$TEMPLATE_PATH" ]] || { - msg_error "Template still not found after version change" - exit 220 - } else msg_custom "đŸšĢ" "${YW}" "Installation cancelled" exit 0 fi else - msg_error "No ${PCT_OSTYPE} templates available" - exit 220 + msg_error "No ${PCT_OSTYPE} templates available at all" + exit 225 fi fi - } - # Validate that we found a template - if [[ -z "$TEMPLATE" ]]; then - msg_error "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}" - msg_custom "â„šī¸" "${YW}" "Please check:" - msg_custom " â€ĸ" "${YW}" "Is pveam catalog available? (run: pveam available -section system)" - msg_custom " â€ĸ" "${YW}" "Does the template exist for your OS version?" - exit 225 - fi - - msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" - msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" - - NEED_DOWNLOAD=0 - if [[ ! -f "$TEMPLATE_PATH" ]]; then - msg_info "Template not present locally – will download." - NEED_DOWNLOAD=1 - elif [[ ! -r "$TEMPLATE_PATH" ]]; then - msg_error "Template file exists but is not readable – check permissions." - exit 221 - elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then - if [[ -n "$ONLINE_TEMPLATE" ]]; then - msg_warn "Template file too small (<1MB) – re-downloading." - NEED_DOWNLOAD=1 - else - msg_warn "Template looks too small, but no online version exists. Keeping local file." + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + if [[ -z "$TEMPLATE_PATH" ]]; then + TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" fi - elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then - if [[ -n "$ONLINE_TEMPLATE" ]]; then - msg_warn "Template appears corrupted – re-downloading." - NEED_DOWNLOAD=1 - else - msg_warn "Template appears corrupted, but no online version exists. Keeping local file." + + # If we still don't have a path but have a valid template name, construct it + if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then + TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" fi - else - $STD msg_ok "Template $TEMPLATE is present and valid." - fi - if [[ "$TEMPLATE_SOURCE" == "local" && -n "$ONLINE_TEMPLATE" && "$TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then - msg_warn "Local template is outdated: $TEMPLATE (latest available: $ONLINE_TEMPLATE)" - if whiptail --yesno "A newer template is available:\n$ONLINE_TEMPLATE\n\nDo you want to download and use it instead?" 12 70; then - TEMPLATE="$ONLINE_TEMPLATE" + [[ -n "$TEMPLATE_PATH" ]] || { + if [[ -z "$TEMPLATE" ]]; then + msg_error "Template ${PCT_OSTYPE} ${PCT_OSVERSION} not available" + + # Get available versions + mapfile -t AVAILABLE_VERSIONS < <( + pveam available -section system 2>/dev/null | + grep "^${PCT_OSTYPE}-" | + sed -E 's/.*'"${PCT_OSTYPE}"'-([0-9]+\.[0-9]+).*/\1/' | + grep -E '^[0-9]+\.[0-9]+$' | + sort -u -V 2>/dev/null || sort -u + ) + + if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then + echo -e "\n${BL}Available versions:${CL}" + for i in "${!AVAILABLE_VERSIONS[@]}"; do + echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" + done + + echo "" + read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or Enter to exit: " choice /dev/null | + awk -v search="${TEMPLATE_SEARCH}-" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk '{print $2}' | + grep -E "^${TEMPLATE_SEARCH}-.*${TEMPLATE_PATTERN}" | + sort -t - -k 2 -V 2>/dev/null || true + ) + ONLINE_TEMPLATE="" + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + else + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + fi + + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + if [[ -z "$TEMPLATE_PATH" ]]; then + TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" + fi + + # If we still don't have a path but have a valid template name, construct it + if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then + TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + fi + + [[ -n "$TEMPLATE_PATH" ]] || { + msg_error "Template still not found after version change" + exit 220 + } + else + msg_custom "đŸšĢ" "${YW}" "Installation cancelled" + exit 0 + fi + else + msg_error "No ${PCT_OSTYPE} templates available" + exit 220 + fi + fi + } + + # Validate that we found a template + if [[ -z "$TEMPLATE" ]]; then + msg_error "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}" + msg_custom "â„šī¸" "${YW}" "Please check:" + msg_custom " â€ĸ" "${YW}" "Is pveam catalog available? (run: pveam available -section system)" + msg_custom " â€ĸ" "${YW}" "Does the template exist for your OS version?" + exit 225 + fi + + msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" + msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" + + NEED_DOWNLOAD=0 + if [[ ! -f "$TEMPLATE_PATH" ]]; then + msg_info "Template not present locally – will download." NEED_DOWNLOAD=1 + elif [[ ! -r "$TEMPLATE_PATH" ]]; then + msg_error "Template file exists but is not readable – check permissions." + exit 221 + elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template file too small (<1MB) – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template looks too small, but no online version exists. Keeping local file." + fi + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template appears corrupted, but no online version exists. Keeping local file." + fi else - msg_custom "â„šī¸" "${BL}" "Continuing with local template $TEMPLATE" + $STD msg_ok "Template $TEMPLATE is present and valid." fi - fi - if [[ "$NEED_DOWNLOAD" -eq 1 ]]; then - [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" - for attempt in {1..3}; do - msg_info "Attempt $attempt: Downloading template $TEMPLATE to $TEMPLATE_STORAGE" - if pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >>"${BUILD_LOG:-/dev/null}" 2>&1; then - msg_ok "Template download successful." - break - fi - if [[ $attempt -eq 3 ]]; then - msg_error "Failed after 3 attempts. Please check network access, permissions, or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" - exit 222 + if [[ "$TEMPLATE_SOURCE" == "local" && -n "$ONLINE_TEMPLATE" && "$TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then + msg_warn "Local template is outdated: $TEMPLATE (latest available: $ONLINE_TEMPLATE)" + if whiptail --yesno "A newer template is available:\n$ONLINE_TEMPLATE\n\nDo you want to download and use it instead?" 12 70; then + TEMPLATE="$ONLINE_TEMPLATE" + NEED_DOWNLOAD=1 + else + msg_custom "â„šī¸" "${BL}" "Continuing with local template $TEMPLATE" fi - sleep $((attempt * 5)) - done - fi + fi + + if [[ "$NEED_DOWNLOAD" -eq 1 ]]; then + [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" + for attempt in {1..3}; do + msg_info "Attempt $attempt: Downloading template $TEMPLATE to $TEMPLATE_STORAGE" + if pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >>"${BUILD_LOG:-/dev/null}" 2>&1; then + msg_ok "Template download successful." + break + fi + if [[ $attempt -eq 3 ]]; then + msg_error "Failed after 3 attempts. Please check network access, permissions, or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" + exit 222 + fi + sleep $((attempt * 5)) + done + fi - if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then - msg_error "Template $TEMPLATE not available in storage $TEMPLATE_STORAGE after download." - exit 223 + if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then + msg_error "Template $TEMPLATE not available in storage $TEMPLATE_STORAGE after download." + exit 223 + fi fi # ------------------------------------------------------------------------------ @@ -5464,19 +5581,13 @@ create_lxc_container() { if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH" 2>/dev/null || echo 0)" -lt 1000000 ]]; then msg_info "Template file missing or too small – downloading" rm -f "$TEMPLATE_PATH" - pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >>"${BUILD_LOG:-/dev/null}" 2>&1 || { - msg_error "Failed to download template '$TEMPLATE' to storage '$TEMPLATE_STORAGE'" - exit 222 - } + download_template msg_ok "Template downloaded" elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then - if [[ -n "$ONLINE_TEMPLATE" ]]; then + if [[ "$ARCH" == "arm64" || -n "$ONLINE_TEMPLATE" ]]; then msg_info "Template appears corrupted – re-downloading" rm -f "$TEMPLATE_PATH" - pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >>"${BUILD_LOG:-/dev/null}" 2>&1 || { - msg_error "Failed to re-download template '$TEMPLATE'" - exit 222 - } + download_template msg_ok "Template re-downloaded" else msg_warn "Template appears corrupted, but no online version exists. Skipping re-download." @@ -5497,7 +5608,7 @@ create_lxc_container() { if grep -qiE 'unable to open|corrupt|invalid' "$LOGFILE"; then msg_info "Template may be corrupted – re-downloading" rm -f "$TEMPLATE_PATH" - pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >>"${BUILD_LOG:-/dev/null}" 2>&1 + download_template msg_ok "Template re-downloaded" fi @@ -5510,7 +5621,11 @@ create_lxc_container() { if [[ ! -f "$LOCAL_TEMPLATE_PATH" ]]; then msg_ok "Trying local storage fallback" msg_info "Downloading template to local" - pveam download local "$TEMPLATE" >>"${BUILD_LOG:-/dev/null}" 2>&1 + if [[ "$ARCH" == "arm64" ]]; then + download_arm64_template "$LOCAL_TEMPLATE_PATH" + else + pveam download local "$TEMPLATE" >>"${BUILD_LOG:-/dev/null}" 2>&1 + fi msg_ok "Template downloaded to local" else msg_ok "Trying local storage fallback" @@ -5646,15 +5761,15 @@ description() { - GitHub + GitHub - Discussions + Discussions - Issues + Issues EOF diff --git a/misc/core.func b/misc/core.func index e3b9d2cff2e..980dfe11124 100644 --- a/misc/core.func +++ b/misc/core.func @@ -344,9 +344,10 @@ pve_check() { # - Provides link to ARM64-compatible scripts # ------------------------------------------------------------------------------ arch_check() { - if [ "$(dpkg --print-architecture)" != "amd64" ]; then - msg_error "This script will not work with PiMox (ARM architecture detected)." - msg_warn "Visit https://github.com/asylumexp/Proxmox for ARM64 support." + local arch + arch="$(dpkg --print-architecture)" + if [[ "$arch" != "amd64" && "$arch" != "arm64" ]]; then + msg_error "This script requires amd64 or arm64 (detected: $arch)." sleep 2 exit 106 fi diff --git a/misc/install.func b/misc/install.func index 94f005b26f9..4e39b69daae 100644 --- a/misc/install.func +++ b/misc/install.func @@ -235,6 +235,7 @@ EOF fi apt_update_safe $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade + $STD apt-get install -y sudo curl mc gnupg2 openssh-server wget gcc rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Updated Container OS" post_progress_to_api diff --git a/misc/tools.func b/misc/tools.func index cce5f303bee..c4de1036cae 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -3267,7 +3267,7 @@ function fetch_and_deploy_gh_release() { fi if [[ -z "$url_match" ]]; then for u in $assets; do - if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then + if [[ "$u" =~ ($arch|aarch64|arm64).*\.deb$ ]]; then url_match="$u" break fi From 6f62656c96d33e4571ef03477d7882ac5b74a377 Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Sat, 7 Mar 2026 15:55:21 +1000 Subject: [PATCH 02/22] Replace tools.func call that checks arch --- misc/tools.func | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index c4de1036cae..95ab71bc479 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2735,10 +2735,13 @@ function fetch_and_deploy_codeberg_release() { # Fall back to architecture heuristic if [[ -z "$url_match" ]]; then for u in $assets; do - if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then - url_match="$u" - break + if [[ "${arch,,}" =~ ^(amd64|x86_64)$ ]]; then + [[ "$u" =~ (amd64|x86_64).*\.deb$ ]] || continue + elif [[ "${arch,,}" =~ ^(arm64|aarch64)$ ]]; then + [[ "$u" =~ (arm64|aarch64).*\.deb$ ]] || continue fi + url_match="$u" + break done fi @@ -3035,7 +3038,11 @@ _gh_scan_older_releases() { done) fi if [[ "$has_match" != "true" ]]; then - has_match=$(echo "$releases_list" | jq -r ".[$i].assets[].browser_download_url" | grep -qE "($arch|amd64|x86_64|aarch64|arm64).*\.deb$" && echo true) + if [[ "${arch,,}" =~ ^(amd64|x86_64)$ ]]; then + has_match=$(echo "$releases_list" | jq -r ".[$i].assets[].browser_download_url" | grep -qE '(amd64|x86_64).*\.deb$' && echo true) + elif [[ "${arch,,}" =~ ^(arm64|aarch64)$ ]]; then + has_match=$(echo "$releases_list" | jq -r ".[$i].assets[].browser_download_url" | grep -qE '(arm64|aarch64).*\.deb$' && echo true) + fi fi if [[ "$has_match" != "true" ]]; then has_match=$(echo "$releases_list" | jq -r ".[$i].assets[].browser_download_url" | grep -qE '\.deb$' && echo true) @@ -3233,10 +3240,13 @@ function fetch_and_deploy_gh_release() { # If no match via explicit pattern, fall back to architecture heuristic if [[ -z "$url_match" ]]; then for u in $assets; do - if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then - url_match="$u" - break + if [[ "${arch,,}" =~ ^(amd64|x86_64)$ ]]; then + [[ "$u" =~ (amd64|x86_64).*\.deb$ ]] || continue + elif [[ "${arch,,}" =~ ^(arm64|aarch64)$ ]]; then + [[ "$u" =~ (arm64|aarch64).*\.deb$ ]] || continue fi + url_match="$u" + break done fi @@ -3267,10 +3277,13 @@ function fetch_and_deploy_gh_release() { fi if [[ -z "$url_match" ]]; then for u in $assets; do - if [[ "$u" =~ ($arch|aarch64|arm64).*\.deb$ ]]; then - url_match="$u" - break + if [[ "${arch,,}" =~ ^(amd64|x86_64)$ ]]; then + [[ "$u" =~ (amd64|x86_64).*\.deb$ ]] || continue + elif [[ "${arch,,}" =~ ^(arm64|aarch64)$ ]]; then + [[ "$u" =~ (arm64|aarch64).*\.deb$ ]] || continue fi + url_match="$u" + break done fi if [[ -z "$url_match" ]]; then From b9d401b17821d27b046dd67f18e902a09ea012f9 Mon Sep 17 00:00:00 2001 From: Sam Heinz Date: Wed, 11 Mar 2026 09:56:18 +1000 Subject: [PATCH 03/22] Update misc/build.func Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 16bebd78279..97fefada9e4 100644 --- a/misc/build.func +++ b/misc/build.func @@ -5453,7 +5453,7 @@ create_lxc_container() { msg_info "Template not present locally – will download." NEED_DOWNLOAD=1 elif [[ ! -r "$TEMPLATE_PATH" ]]; then - msg_error "Template file exists but is not readable – check permissions." + msg_error "Template file exists but is not readable, check permissions." exit 221 elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then if [[ -n "$ONLINE_TEMPLATE" ]]; then From e4db6be2573b7fe407fd9f0b9b655f2daa8555d5 Mon Sep 17 00:00:00 2001 From: Sam Heinz Date: Wed, 11 Mar 2026 09:56:34 +1000 Subject: [PATCH 04/22] Update misc/build.func Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 97fefada9e4..fc3d6e89c15 100644 --- a/misc/build.func +++ b/misc/build.func @@ -5450,7 +5450,7 @@ create_lxc_container() { NEED_DOWNLOAD=0 if [[ ! -f "$TEMPLATE_PATH" ]]; then - msg_info "Template not present locally – will download." + msg_info "Template not present locally, will download it." NEED_DOWNLOAD=1 elif [[ ! -r "$TEMPLATE_PATH" ]]; then msg_error "Template file exists but is not readable, check permissions." From a6a83b9541f30dd61d5588078ae95f5481e6044a Mon Sep 17 00:00:00 2001 From: Sam Heinz Date: Wed, 11 Mar 2026 09:59:42 +1000 Subject: [PATCH 05/22] Update misc/build.func Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index fc3d6e89c15..56aa7c5c662 100644 --- a/misc/build.func +++ b/misc/build.func @@ -5457,7 +5457,7 @@ create_lxc_container() { exit 221 elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then if [[ -n "$ONLINE_TEMPLATE" ]]; then - msg_warn "Template file too small (<1MB) – re-downloading." + msg_warn "Template file too small (<1MB), re-downloading." NEED_DOWNLOAD=1 else msg_warn "Template looks too small, but no online version exists. Keeping local file." From 78979189c1ee622707788133a8624fd8b057bcdd Mon Sep 17 00:00:00 2001 From: Sam Heinz Date: Wed, 11 Mar 2026 10:19:58 +1000 Subject: [PATCH 06/22] Update misc/build.func Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 56aa7c5c662..f846a7535da 100644 --- a/misc/build.func +++ b/misc/build.func @@ -5464,7 +5464,7 @@ create_lxc_container() { fi elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then if [[ -n "$ONLINE_TEMPLATE" ]]; then - msg_warn "Template appears corrupted – re-downloading." + msg_warn "Template appears corrupted, re-downloading." NEED_DOWNLOAD=1 else msg_warn "Template appears corrupted, but no online version exists. Keeping local file." From 35b3b93ca61da66ba1721106e10d837711221aa5 Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Wed, 11 Mar 2026 20:59:33 +1000 Subject: [PATCH 07/22] build.func: change back to VE --- misc/build.func | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/misc/build.func b/misc/build.func index f846a7535da..1c3f5f78d19 100644 --- a/misc/build.func +++ b/misc/build.func @@ -5341,7 +5341,7 @@ create_lxc_container() { exit 0 fi else - msg_error "No ${PCT_OSTYPE} templates available at all" + msg_error "No ${PCT_OSTYPE} templates available" exit 225 fi fi @@ -5377,7 +5377,7 @@ create_lxc_container() { done echo "" - read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or Enter to exit: " choice - GitHub + GitHub - Discussions + Discussions - Issues + Issues EOF From 6f8aa6eadc142dd81a07275d86e3b59821436973 Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Wed, 11 Mar 2026 21:17:38 +1000 Subject: [PATCH 08/22] build.func: remove arm64 support for focal, bullseye Legacy support removed since no cts use them anymore. --- misc/build.func | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/misc/build.func b/misc/build.func index 1c3f5f78d19..1bf45a69f3f 100644 --- a/misc/build.func +++ b/misc/build.func @@ -5024,14 +5024,14 @@ create_lxc_container() { case "$1" in debian) case "$2" in - 11 | 11.*) echo "bullseye" ;; 12 | 12.*) echo "bookworm" ;; - 13 | 13.*) echo "trixie" ;; *) echo "trixie" ;; + 12 | 12.*) echo "bookworm" ;; 13 | 13.*) echo "trixie" ;; + *) echo "trixie" ;; esac ;; alpine) echo "3.22" ;; ubuntu) case "$2" in - 20.04* | focal) echo "focal" ;; 24.04* | noble) echo "noble" ;; - 24.10* | oracular) echo "oracular" ;; *) echo "jammy" ;; + 24.04* | noble) echo "noble" ;; 24.10* | oracular) echo "oracular" ;; + *) echo "jammy" ;; esac ;; *) return 1 ;; esac From 7c051fb64891f72ce030896a3ac14375031c98b4 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:23:56 +0100 Subject: [PATCH 09/22] Improve arm64 support and arch-specific downloads Add clearer architecture error messages and gate arm64 usage, plus implement architecture-aware behavior across the toolkit. Changes include: update exit-code messages to reference amd64/arm64, refuse arm64 unless explicitly enabled, show architecture in summaries, and use arch-specific package lists when installing in containers. Make downloads for FFmpeg and yq choose the correct amd64/arm64 binaries, tighten template download error handling and formatting, and clean up minor whitespace/comment issues. These changes aim to make arm64 handling explicit and downloads/installations more robust for non-amd64 systems. --- misc/api.func | 10 +-- misc/build.func | 153 +++++++++++++++++++++------------------- misc/core.func | 5 ++ misc/error_handler.func | 2 +- misc/install.func | 1 - misc/tools.func | 21 +++++- 6 files changed, 110 insertions(+), 82 deletions(-) diff --git a/misc/api.func b/misc/api.func index e29975e2cc6..7815d4a33e1 100644 --- a/misc/api.func +++ b/misc/api.func @@ -196,7 +196,7 @@ explain_exit_code() { 103) echo "Validation: Shell is not Bash" ;; 104) echo "Validation: Not running as root (or invoked via sudo)" ;; 105) echo "Validation: Proxmox VE version not supported" ;; - 106) echo "Validation: Architecture not supported (ARM / PiMox)" ;; + 106) echo "Validation: Unsupported architecture (requires amd64 or arm64)" ;; 107) echo "Validation: Kernel key parameters unreadable" ;; 108) echo "Validation: Kernel key limits exceeded" ;; 109) echo "Proxmox: No available container ID after max attempts" ;; @@ -348,10 +348,10 @@ explain_exit_code() { json_escape() { # Escape a string for safe JSON embedding using awk (handles any input size). # Pipeline: strip ANSI → remove control chars → escape \ " TAB → join lines with \n - printf '%s' "$1" \ - | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' \ - | tr -d '\000-\010\013\014\016-\037\177\r' \ - | awk ' + printf '%s' "$1" | + sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' | + tr -d '\000-\010\013\014\016-\037\177\r' | + awk ' BEGIN { ORS = "" } { gsub(/\\/, "\\\\") # backslash → \\ diff --git a/misc/build.func b/misc/build.func index 1bf45a69f3f..5ce216cd94d 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1826,9 +1826,9 @@ advanced_settings() { while [ $STEP -le $MAX_STEP ]; do case $STEP in - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 1: Container Type - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 1) local default_on="ON" local default_off="OFF" @@ -1851,9 +1851,9 @@ advanced_settings() { fi ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 2: Root Password - # ════════════════════════════════════════════════════════════════════════īŋŊīŋŊīŋŊ══ + # ------------------------------------------------------------------------------ 2) if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "ROOT PASSWORD" \ @@ -1905,9 +1905,9 @@ advanced_settings() { fi ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 3: Container ID - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 3) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "CONTAINER ID" \ @@ -1939,9 +1939,9 @@ advanced_settings() { fi ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 4: Hostname - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 4) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "HOSTNAME" \ @@ -1962,9 +1962,9 @@ advanced_settings() { fi ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 5: Disk Size - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 5) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "DISK SIZE" \ @@ -1983,9 +1983,9 @@ advanced_settings() { fi ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 6: CPU Cores - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 6) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "CPU CORES" \ @@ -2004,9 +2004,9 @@ advanced_settings() { fi ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 7: RAM Size - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 7) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "RAM SIZE" \ @@ -2025,9 +2025,9 @@ advanced_settings() { fi ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 8: Network Bridge - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 8) if [[ ${#BRIDGE_MENU_OPTIONS[@]} -eq 0 ]]; then # Validate default bridge exists @@ -2063,9 +2063,9 @@ advanced_settings() { fi ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 9: IPv4 Configuration - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 9) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "IPv4 CONFIGURATION" \ @@ -2160,9 +2160,9 @@ advanced_settings() { fi ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 10: IPv6 Configuration - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 10) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "IPv6 CONFIGURATION" \ @@ -2235,9 +2235,9 @@ advanced_settings() { fi ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 11: MTU Size - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 11) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "MTU SIZE" \ @@ -2255,9 +2255,9 @@ advanced_settings() { fi ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 12: DNS Search Domain - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 12) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "DNS SEARCH DOMAIN" \ @@ -2271,9 +2271,9 @@ advanced_settings() { fi ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 13: DNS Server - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 13) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "DNS SERVER" \ @@ -2287,9 +2287,9 @@ advanced_settings() { fi ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 14: MAC Address - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 14) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "MAC ADDRESS" \ @@ -2307,9 +2307,9 @@ advanced_settings() { fi ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 15: VLAN Tag - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 15) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "VLAN TAG" \ @@ -2327,9 +2327,9 @@ advanced_settings() { fi ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 16: Tags - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 16) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "CONTAINER TAGS" \ @@ -2349,18 +2349,18 @@ advanced_settings() { fi ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 17: SSH Settings - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 17) configure_ssh_settings "Step $STEP/$MAX_STEP" # configure_ssh_settings handles its own flow, always advance ((STEP++)) ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 18: FUSE Support - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 18) local fuse_default_flag="--defaultno" [[ "$_enable_fuse" == "yes" || "$_enable_fuse" == "1" ]] && fuse_default_flag="" @@ -2382,9 +2382,9 @@ advanced_settings() { ((STEP++)) ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 19: TUN/TAP Support - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 19) local tun_default_flag="--defaultno" [[ "$_enable_tun" == "yes" || "$_enable_tun" == "1" ]] && tun_default_flag="" @@ -2406,9 +2406,9 @@ advanced_settings() { ((STEP++)) ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 20: Nesting Support - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 20) local nesting_default_flag="" [[ "$_enable_nesting" == "0" || "$_enable_nesting" == "no" ]] && nesting_default_flag="--defaultno" @@ -2436,9 +2436,9 @@ advanced_settings() { ((STEP++)) ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 21: GPU Passthrough - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 21) local gpu_default_flag="--defaultno" [[ "$_enable_gpu" == "yes" ]] && gpu_default_flag="" @@ -2460,9 +2460,9 @@ advanced_settings() { ((STEP++)) ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 22: Keyctl Support (Docker/systemd) - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 22) local keyctl_default_flag="--defaultno" [[ "$_enable_keyctl" == "1" ]] && keyctl_default_flag="" @@ -2484,9 +2484,9 @@ advanced_settings() { ((STEP++)) ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 23: APT Cacher Proxy - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 23) local apt_cacher_default_flag="--defaultno" [[ "$_apt_cacher" == "yes" ]] && apt_cacher_default_flag="" @@ -2516,9 +2516,9 @@ advanced_settings() { ((STEP++)) ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 24: Container Timezone - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 24) local tz_hint="$_ct_timezone" [[ -z "$tz_hint" ]] && tz_hint="(empty - will use host timezone)" @@ -2541,9 +2541,9 @@ advanced_settings() { fi ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 25: Container Protection - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 25) local protect_default_flag="--defaultno" [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_default_flag="" @@ -2565,9 +2565,9 @@ advanced_settings() { ((STEP++)) ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 26: Device Node Creation (mknod) - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 26) local mknod_default_flag="--defaultno" [[ "$_enable_mknod" == "1" ]] && mknod_default_flag="" @@ -2589,9 +2589,9 @@ advanced_settings() { ((STEP++)) ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 27: Mount Filesystems - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 27) local mount_hint="" [[ -n "$_mount_fs" ]] && mount_hint="$_mount_fs" || mount_hint="(none)" @@ -2608,9 +2608,9 @@ advanced_settings() { fi ;; - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # STEP 28: Verbose Mode & Confirmation - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ 28) local verbose_default_flag="--defaultno" [[ "$_verbose" == "yes" ]] && verbose_default_flag="" @@ -2676,9 +2676,9 @@ Advanced: esac done - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ # Apply all collected values to global variables - # ═══════════════════════════════════════════════════════════════════════════ + # ------------------------------------------------------------------------------ CT_TYPE="$_ct_type" PW="$_pw" CT_ID="$_ct_id" @@ -2895,6 +2895,9 @@ echo_default() { echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + if [[ "$(dpkg --print-architecture)" == "arm64" ]]; then + echo -e "${INFO}${BOLD}${DGN}Architecture: ${BGN}arm64${CL}" + fi if [[ -n "${var_gpu:-}" && "${var_gpu}" == "yes" ]]; then echo -e "${GPU}${BOLD}${DGN}GPU Passthrough: ${BGN}Enabled${CL}" fi @@ -3425,9 +3428,6 @@ start() { set_std_mode ensure_profile_loaded get_lxc_ip - if [[ "$(dpkg --print-architecture)" == "arm64" ]] && declare -f update_script_arm64 >/dev/null 2>&1; then - update_script_arm64 - fi update_script update_motd_ip cleanup_lxc @@ -3456,9 +3456,6 @@ start() { esac ensure_profile_loaded get_lxc_ip - if [[ "$(dpkg --print-architecture)" == "arm64" ]] && declare -f update_script_arm64 >/dev/null 2>&1; then - update_script_arm64 - fi update_script update_motd_ip cleanup_lxc @@ -4071,7 +4068,11 @@ EOF' msg_warn "Skipping timezone setup – zone '$tz' not found in container" fi - pct exec "$CTID" -- bash -c "apt-get update 2>&1 && apt-get install -y sudo curl mc gnupg2 jq 2>&1" >>"$BUILD_LOG" 2>&1 || { + local _base_pkgs="sudo curl mc gnupg2 jq" + if [[ "${ARCH:-amd64}" == "arm64" ]]; then + _base_pkgs+=" openssh-server wget gcc" + fi + pct exec "$CTID" -- bash -c "apt-get update 2>&1 && apt-get install -y ${_base_pkgs} 2>&1" >>"$BUILD_LOG" 2>&1 || { msg_error "apt-get base packages installation failed" install_exit_code=1 } @@ -5026,13 +5027,15 @@ create_lxc_container() { case "$2" in 12 | 12.*) echo "bookworm" ;; 13 | 13.*) echo "trixie" ;; *) echo "trixie" ;; - esac ;; + esac + ;; alpine) echo "3.22" ;; ubuntu) case "$2" in - 24.04* | noble) echo "noble" ;; 24.10* | oracular) echo "oracular" ;; + 24.04* | noble) echo "noble" ;; 24.10* | oracular) echo "oracular" ;; *) echo "jammy" ;; - esac ;; + esac + ;; *) return 1 ;; esac } @@ -5043,12 +5046,18 @@ create_lxc_container() { download_arm64_template() { local dest="$1" url - mkdir -p "$(dirname "$dest")" || { msg_error "Cannot create template dir."; exit 207; } + mkdir -p "$(dirname "$dest")" || { + msg_error "Cannot create template dir." + exit 207 + } if [[ "$PCT_OSTYPE" == "debian" ]]; then - url=$(curl -fsSL "https://api.github.com/repos/asylumexp/debian-ifupdown2-lxc/releases/latest" \ - | grep -Eo "https://[^\"]*debian-${CUSTOM_TEMPLATE_VARIANT}-arm64-rootfs\.tar\.xz" | head -n1) - [[ -n "$url" ]] || { msg_error "Could not find Debian ${CUSTOM_TEMPLATE_VARIANT} ARM64 template URL."; exit 207; } + url=$(curl -fsSL "https://api.github.com/repos/asylumexp/debian-ifupdown2-lxc/releases/latest" | + grep -Eo "https://[^\"]*debian-${CUSTOM_TEMPLATE_VARIANT}-arm64-rootfs\.tar\.xz" | head -n1) + [[ -n "$url" ]] || { + msg_error "Could not find Debian ${CUSTOM_TEMPLATE_VARIANT} ARM64 template URL." + exit 207 + } else url="https://jenkins.linuxcontainers.org/job/image-${PCT_OSTYPE}/architecture=arm64,release=${CUSTOM_TEMPLATE_VARIANT},variant=default/lastStableBuild/artifact/rootfs.tar.xz" fi diff --git a/misc/core.func b/misc/core.func index 980dfe11124..7d44395d69b 100644 --- a/misc/core.func +++ b/misc/core.func @@ -351,6 +351,11 @@ arch_check() { sleep 2 exit 106 fi + if [[ "$arch" == "arm64" && "${var_arm64:-}" != "yes" ]]; then + msg_error "This script does not yet support arm64." + sleep 2 + exit 106 + fi } # ------------------------------------------------------------------------------ diff --git a/misc/error_handler.func b/misc/error_handler.func index 39e5e667f35..e547a15737e 100644 --- a/misc/error_handler.func +++ b/misc/error_handler.func @@ -99,7 +99,7 @@ if ! declare -f explain_exit_code &>/dev/null; then 103) echo "Validation: Shell is not Bash" ;; 104) echo "Validation: Not running as root (or invoked via sudo)" ;; 105) echo "Validation: Proxmox VE version not supported" ;; - 106) echo "Validation: Architecture not supported (ARM / PiMox)" ;; + 106) echo "Validation: Unsupported architecture (requires amd64 or arm64)" ;; 107) echo "Validation: Kernel key parameters unreadable" ;; 108) echo "Validation: Kernel key limits exceeded" ;; 109) echo "Proxmox: No available container ID after max attempts" ;; diff --git a/misc/install.func b/misc/install.func index 4e39b69daae..94f005b26f9 100644 --- a/misc/install.func +++ b/misc/install.func @@ -235,7 +235,6 @@ EOF fi apt_update_safe $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade - $STD apt-get install -y sudo curl mc gnupg2 openssh-server wget gcc rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Updated Container OS" post_progress_to_api diff --git a/misc/tools.func b/misc/tools.func index 95ab71bc479..09cd7786e51 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -3651,7 +3651,12 @@ function setup_ffmpeg() { # Binary fallback mode if [[ "$TYPE" == "binary" ]]; then - if ! CURL_TIMEOUT=300 curl_with_retry "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz" "$TMP_DIR/ffmpeg.tar.xz"; then + local ffmpeg_arch + case "$(dpkg --print-architecture 2>/dev/null || echo amd64)" in + arm64) ffmpeg_arch="arm64" ;; + *) ffmpeg_arch="amd64" ;; + esac + if ! CURL_TIMEOUT=300 curl_with_retry "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-${ffmpeg_arch}-static.tar.xz" "$TMP_DIR/ffmpeg.tar.xz"; then msg_error "Failed to download FFmpeg binary" rm -rf "$TMP_DIR" return 1 @@ -3733,7 +3738,12 @@ function setup_ffmpeg() { # If no source download (either VERSION empty or download failed), use binary if [[ -z "$VERSION" ]]; then msg_info "Setup FFmpeg from pre-built binary" - if ! CURL_TIMEOUT=300 curl_with_retry "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz" "$TMP_DIR/ffmpeg.tar.xz"; then + local ffmpeg_arch + case "$(dpkg --print-architecture 2>/dev/null || echo amd64)" in + arm64) ffmpeg_arch="arm64" ;; + *) ffmpeg_arch="amd64" ;; + esac + if ! CURL_TIMEOUT=300 curl_with_retry "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-${ffmpeg_arch}-static.tar.xz" "$TMP_DIR/ffmpeg.tar.xz"; then msg_error "Failed to download FFmpeg pre-built binary" rm -rf "$TMP_DIR" return 1 @@ -7706,7 +7716,12 @@ function setup_yq() { msg_info "Setup yq $LATEST_VERSION" fi - if ! curl_with_retry "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_amd64" "$TMP_DIR/yq"; then + local yq_arch + case "$(dpkg --print-architecture 2>/dev/null || echo amd64)" in + arm64) yq_arch="arm64" ;; + *) yq_arch="amd64" ;; + esac + if ! curl_with_retry "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_${yq_arch}" "$TMP_DIR/yq"; then msg_error "Failed to download yq" rm -rf "$TMP_DIR" return 1 From 231945dfa740955f3068b5027d6a33e7552b436c Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:26:03 +0100 Subject: [PATCH 10/22] remove arm64 overlay --- misc/build.func | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/misc/build.func b/misc/build.func index 5ce216cd94d..5eb93a1aed3 100644 --- a/misc/build.func +++ b/misc/build.func @@ -4105,13 +4105,6 @@ EOF' local _install_script _install_script="$(curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh")" - if [[ "$ARCH" == "arm64" ]]; then - local _arm_script - _arm_script="$(curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/arm/${var_install}.sh" 2>/dev/null || true)" - if [[ -n "$_arm_script" ]]; then - _install_script="${_arm_script}"$'\n'"${_install_script}" - fi - fi lxc-attach -n "$CTID" -- bash -c "$_install_script" local lxc_exit=$? @@ -4492,13 +4485,6 @@ EOF' trap - ERR local _install_script _install_script="$(curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh")" - if [[ "$ARCH" == "arm64" ]]; then - local _arm_script - _arm_script="$(curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/arm/${var_install}.sh" 2>/dev/null || true)" - if [[ -n "$_arm_script" ]]; then - _install_script="${_arm_script}"$'\n'"${_install_script}" - fi - fi lxc-attach -n "$CTID" -- bash -c "$_install_script" local apt_retry_exit=$? set -Eeuo pipefail From 44ec223d20bffc1deed7bc64f2755837508f09d6 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:37:43 +0100 Subject: [PATCH 11/22] update ifupdown2 source --- misc/build.func | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index e8884ad798f..33de914026d 100644 --- a/misc/build.func +++ b/misc/build.func @@ -5038,7 +5038,7 @@ create_lxc_container() { } # Downloads an ARM64 LXC rootfs template to $1. - # Debian: fetches latest release from asylumexp/debian-ifupdown2-lxc on GitHub. + # Debian: fetches latest release from community-scripts/debian-arm64-lxc on GitHub. # Others: fetches from jenkins.linuxcontainers.org. download_arm64_template() { local dest="$1" url @@ -5049,7 +5049,7 @@ create_lxc_container() { } if [[ "$PCT_OSTYPE" == "debian" ]]; then - url=$(curl -fsSL "https://api.github.com/repos/asylumexp/debian-ifupdown2-lxc/releases/latest" | + url=$(curl -fsSL "https://api.github.com/repos/community-scripts/debian-arm64-lxc/releases/latest" | grep -Eo "https://[^\"]*debian-${CUSTOM_TEMPLATE_VARIANT}-arm64-rootfs\.tar\.xz" | head -n1) [[ -n "$url" ]] || { msg_error "Could not find Debian ${CUSTOM_TEMPLATE_VARIANT} ARM64 template URL." From 866b6950c0e7a7cae5b746b3e864dd80525358d2 Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Thu, 12 Mar 2026 16:42:41 +1000 Subject: [PATCH 12/22] Revert "remove arm64 overlay" This reverts commit 231945dfa740955f3068b5027d6a33e7552b436c. --- misc/build.func | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/misc/build.func b/misc/build.func index 33de914026d..d4c06bbdfac 100644 --- a/misc/build.func +++ b/misc/build.func @@ -4116,6 +4116,13 @@ EOF' local _install_script _install_script="$(curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh")" + if [[ "$ARCH" == "arm64" ]]; then + local _arm_script + _arm_script="$(curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/arm/${var_install}.sh" 2>/dev/null || true)" + if [[ -n "$_arm_script" ]]; then + _install_script="${_arm_script}"$'\n'"${_install_script}" + fi + fi lxc-attach -n "$CTID" -- bash -c "$_install_script" local lxc_exit=$? @@ -4496,6 +4503,13 @@ EOF' trap - ERR local _install_script _install_script="$(curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh")" + if [[ "$ARCH" == "arm64" ]]; then + local _arm_script + _arm_script="$(curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/arm/${var_install}.sh" 2>/dev/null || true)" + if [[ -n "$_arm_script" ]]; then + _install_script="${_arm_script}"$'\n'"${_install_script}" + fi + fi lxc-attach -n "$CTID" -- bash -c "$_install_script" local apt_retry_exit=$? set -Eeuo pipefail From 24f134799017763ccf2eafc5d72b98a858bc7de1 Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Thu, 12 Mar 2026 16:45:00 +1000 Subject: [PATCH 13/22] Revert changes from "Improve arm64 support and arch-specific downloads" --- misc/build.func | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/misc/build.func b/misc/build.func index d4c06bbdfac..a3e3dba2f7e 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3428,6 +3428,9 @@ start() { set_std_mode ensure_profile_loaded get_lxc_ip + if [[ "$(dpkg --print-architecture)" == "arm64" ]] && declare -f update_script_arm64 >/dev/null 2>&1; then + update_script_arm64 + fi update_script update_motd_ip cleanup_lxc @@ -3456,6 +3459,9 @@ start() { esac ensure_profile_loaded get_lxc_ip + if [[ "$(dpkg --print-architecture)" == "arm64" ]] && declare -f update_script_arm64 >/dev/null 2>&1; then + update_script_arm64 + fi update_script update_motd_ip cleanup_lxc From 73e121b67980e0ade64817fe6805372c6634e5d7 Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Tue, 12 May 2026 14:16:02 +1000 Subject: [PATCH 14/22] Reapply "Improve arm64 support and arch-specific downloads" This reapplies commit 7c051fb64891f72ce030896a3ac14375031c98b4. --- misc/build.func | 6 ------ 1 file changed, 6 deletions(-) diff --git a/misc/build.func b/misc/build.func index a3e3dba2f7e..d4c06bbdfac 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3428,9 +3428,6 @@ start() { set_std_mode ensure_profile_loaded get_lxc_ip - if [[ "$(dpkg --print-architecture)" == "arm64" ]] && declare -f update_script_arm64 >/dev/null 2>&1; then - update_script_arm64 - fi update_script update_motd_ip cleanup_lxc @@ -3459,9 +3456,6 @@ start() { esac ensure_profile_loaded get_lxc_ip - if [[ "$(dpkg --print-architecture)" == "arm64" ]] && declare -f update_script_arm64 >/dev/null 2>&1; then - update_script_arm64 - fi update_script update_motd_ip cleanup_lxc From 314b1b843b2efe58fb01f3f4a6d4e182946fa882 Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Tue, 12 May 2026 14:16:04 +1000 Subject: [PATCH 15/22] Reapply "remove arm64 overlay" This reverts commit 866b6950c0e7a7cae5b746b3e864dd80525358d2. --- misc/build.func | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/misc/build.func b/misc/build.func index d4c06bbdfac..33de914026d 100644 --- a/misc/build.func +++ b/misc/build.func @@ -4116,13 +4116,6 @@ EOF' local _install_script _install_script="$(curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh")" - if [[ "$ARCH" == "arm64" ]]; then - local _arm_script - _arm_script="$(curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/arm/${var_install}.sh" 2>/dev/null || true)" - if [[ -n "$_arm_script" ]]; then - _install_script="${_arm_script}"$'\n'"${_install_script}" - fi - fi lxc-attach -n "$CTID" -- bash -c "$_install_script" local lxc_exit=$? @@ -4503,13 +4496,6 @@ EOF' trap - ERR local _install_script _install_script="$(curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh")" - if [[ "$ARCH" == "arm64" ]]; then - local _arm_script - _arm_script="$(curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/arm/${var_install}.sh" 2>/dev/null || true)" - if [[ -n "$_arm_script" ]]; then - _install_script="${_arm_script}"$'\n'"${_install_script}" - fi - fi lxc-attach -n "$CTID" -- bash -c "$_install_script" local apt_retry_exit=$? set -Eeuo pipefail From 9f7b951ec6ebe7456110a91bc7689bbc4d57bd09 Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Tue, 12 May 2026 15:12:59 +1000 Subject: [PATCH 16/22] Redo template name handling --- misc/build.func | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/misc/build.func b/misc/build.func index 33de914026d..b2269e987d9 100644 --- a/misc/build.func +++ b/misc/build.func @@ -52,6 +52,10 @@ variables() { # as "/tmp/${NSAPP}-${CTID}-${SESSION_ID}.log" (requires CTID, not available here) CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" + # ARM64 Template default variables + DEBIAN_DEFAULT_CODENAME="trixie" + UBUNTU_DEFAULT_CODENAME="noble" + # Parse dev_mode early parse_dev_mode @@ -5019,21 +5023,18 @@ create_lxc_container() { # Maps OS type + version to the release variant name used by ARM64 template sources. arm64_template_variant() { - case "$1" in - debian) - case "$2" in - 12 | 12.*) echo "bookworm" ;; 13 | 13.*) echo "trixie" ;; - *) echo "trixie" ;; - esac - ;; - alpine) echo "3.22" ;; - ubuntu) - case "$2" in - 24.04* | noble) echo "noble" ;; 24.10* | oracular) echo "oracular" ;; - *) echo "jammy" ;; - esac - ;; - *) return 1 ;; + case "$1:$2" in + debian:12) echo "bookworm" ;; + debian:13) echo "trixie" ;; + debian:) echo "$(DEBIAN_DEFAULT_CODENAME)" ;; + + ubuntu:24.04) echo "noble" ;; + ubuntu:26.04) echo "questing" ;; + ubuntu:) echo "$(UBUNTU_DEFAULT_CODENAME)" ;; + + alpine:*) echo "$2" ;; + + *) return 1 ;; esac } @@ -5231,7 +5232,7 @@ create_lxc_container() { TEMPLATE="${PCT_OSTYPE}-${CUSTOM_TEMPLATE_VARIANT}-rootfs.tar.xz" TEMPLATE_SOURCE="custom-arm64" - # Resolve template path: pvesm → storage.cfg fallback → default + # Resolve template path TEMPLATE_PATH="$(pvesm path "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" 2>/dev/null || true)" if [[ -z "$TEMPLATE_PATH" ]]; then local _tpl_base @@ -5239,11 +5240,11 @@ create_lxc_container() { TEMPLATE_PATH="${_tpl_base:-/var/lib/vz}/template/cache/$TEMPLATE" fi - # Download if missing, too small, or corrupt (single pass) + # Download if missing, too small, or corrupt if [[ ! -f "$TEMPLATE_PATH" ]]; then download_arm64_template "$TEMPLATE_PATH" elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]] || ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then - msg_warn "Local template invalid – re-downloading." + msg_warn "Local template invalid - re-downloading." rm -f "$TEMPLATE_PATH" download_arm64_template "$TEMPLATE_PATH" else @@ -5585,13 +5586,13 @@ create_lxc_container() { # Validate template before pct create (while holding lock) if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH" 2>/dev/null || echo 0)" -lt 1000000 ]]; then - msg_info "Template file missing or too small – downloading" + msg_info "Template file missing or too small - downloading" rm -f "$TEMPLATE_PATH" download_template msg_ok "Template downloaded" elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then if [[ "$ARCH" == "arm64" || -n "$ONLINE_TEMPLATE" ]]; then - msg_info "Template appears corrupted – re-downloading" + msg_info "Template appears corrupted - re-downloading" rm -f "$TEMPLATE_PATH" download_template msg_ok "Template re-downloaded" @@ -5612,7 +5613,7 @@ create_lxc_container() { # Check if template issue - retry with fresh download if grep -qiE 'unable to open|corrupt|invalid' "$LOGFILE"; then - msg_info "Template may be corrupted – re-downloading" + msg_info "Template may be corrupted - re-downloading" rm -f "$TEMPLATE_PATH" download_template msg_ok "Template re-downloaded" From 0aeaa5ae97277ca81d07eb738aeeccf75d0a4692 Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Tue, 12 May 2026 15:24:21 +1000 Subject: [PATCH 17/22] fix template name handling change --- misc/build.func | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/misc/build.func b/misc/build.func index b2269e987d9..1b1741da06e 100644 --- a/misc/build.func +++ b/misc/build.func @@ -52,9 +52,10 @@ variables() { # as "/tmp/${NSAPP}-${CTID}-${SESSION_ID}.log" (requires CTID, not available here) CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" - # ARM64 Template default variables - DEBIAN_DEFAULT_CODENAME="trixie" - UBUNTU_DEFAULT_CODENAME="noble" + # ARM64 Template default variables + DEBIAN_DEFAULT_CODENAME="trixie" + UBUNTU_DEFAULT_CODENAME="noble" + ALPINE_DEFAULT_VERSION="3.23" # Parse dev_mode early parse_dev_mode @@ -5026,13 +5027,13 @@ create_lxc_container() { case "$1:$2" in debian:12) echo "bookworm" ;; debian:13) echo "trixie" ;; - debian:) echo "$(DEBIAN_DEFAULT_CODENAME)" ;; + debian:) echo "$DEBIAN_DEFAULT_CODENAME" ;; ubuntu:24.04) echo "noble" ;; ubuntu:26.04) echo "questing" ;; - ubuntu:) echo "$(UBUNTU_DEFAULT_CODENAME)" ;; + ubuntu:) echo "$UBUNTU_DEFAULT_CODENAME" ;; - alpine:*) echo "$2" ;; + alpine:*) echo "${2:-$ALPINE_DEFAULT_VERSION}" ;; *) return 1 ;; esac @@ -5050,8 +5051,13 @@ create_lxc_container() { } if [[ "$PCT_OSTYPE" == "debian" ]]; then - url=$(curl -fsSL "https://api.github.com/repos/community-scripts/debian-arm64-lxc/releases/latest" | - grep -Eo "https://[^\"]*debian-${CUSTOM_TEMPLATE_VARIANT}-arm64-rootfs\.tar\.xz" | head -n1) + url=$( + curl -fsSL "https://api.github.com/repos/community-scripts/debian-arm64-lxc/releases/latest" | + jq -r --arg v "$CUSTOM_TEMPLATE_VARIANT" \ + '.assets[].browser_download_url | select(test("debian-" + $v + "-arm64-rootfs\\.tar\\.xz$"))' | + head -n1 + ) + [[ -n "$url" ]] || { msg_error "Could not find Debian ${CUSTOM_TEMPLATE_VARIANT} ARM64 template URL." exit 207 From 079afa46009e529fd9ae3530f57403265abdc9c3 Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Tue, 12 May 2026 15:28:43 +1000 Subject: [PATCH 18/22] add ensure_whiptail function This is required as some arm64 systems will not have whiptail installed, as it is not installed by default. --- misc/build.func | 3 +-- misc/core.func | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index 1b1741da06e..8f21f071cc0 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2939,6 +2939,7 @@ install_script() { pve_check shell_check root_check + ensure_whiptail arch_check ssh_check maxkeys_check @@ -5074,8 +5075,6 @@ create_lxc_container() { msg_ok "Downloaded ARM64 LXC template" } - # Architecture-aware template download wrapper. - # Optional $1 overrides destination path (for local-storage fallback). download_template() { local dest="${1:-$TEMPLATE_PATH}" if [[ "$ARCH" == "arm64" ]]; then diff --git a/misc/core.func b/misc/core.func index 7d44395d69b..3257ace210e 100644 --- a/misc/core.func +++ b/misc/core.func @@ -1718,6 +1718,25 @@ function get_lxc_ip() { export LOCAL_IP } +# ------------------------------------------------------------------------------ +# ensure_whiptail() +# +# - Ensures whiptail is installed +# - Some ARM64 systems will not have whiptail installed. +# - Exits with error message if installation fails +# ------------------------------------------------------------------------------ +ensure_whiptail() { + command -v whiptail >/dev/null 2>&1 && return 0 + + msg_info "Installing whiptail" + apt_update_safe + $STD apt-get install -y whiptail || { + msg_error "Failed to install whiptail" + exit 115 + } + msg_ok "Installed whiptail" +} + # ============================================================================== # SIGNAL TRAPS # ============================================================================== From 5f6a02986d40939decd25b9ea709b546d9ceb93c Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Tue, 12 May 2026 15:38:38 +1000 Subject: [PATCH 19/22] add arm64_notice function --- misc/build.func | 1 + misc/core.func | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 8f21f071cc0..41e53bcacc2 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2941,6 +2941,7 @@ install_script() { root_check ensure_whiptail arch_check + arm64_notice ssh_check maxkeys_check diagnostics_check diff --git a/misc/core.func b/misc/core.func index 3257ace210e..2c991b3e30a 100644 --- a/misc/core.func +++ b/misc/core.func @@ -1722,7 +1722,7 @@ function get_lxc_ip() { # ensure_whiptail() # # - Ensures whiptail is installed -# - Some ARM64 systems will not have whiptail installed. +# - Some ARM64 systems will not have whiptail installed # - Exits with error message if installation fails # ------------------------------------------------------------------------------ ensure_whiptail() { @@ -1737,6 +1737,19 @@ ensure_whiptail() { msg_ok "Installed whiptail" } +# ------------------------------------------------------------------------------ +# arm64_notice() +# +# - Shows a short warning when running scripts on ARM64 systems +# ------------------------------------------------------------------------------ +arm64_notice() { + [[ "$(dpkg --print-architecture)" == "arm64" ]] || return 0 + + whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "ARM64 SUPPORT" \ + --ok-button "Continue" \ + --msgbox "ARM64 support is in active development.\n\nSome scripts, packages, or application releases may not be fully tested or working yet." 10 68 +} # ============================================================================== # SIGNAL TRAPS # ============================================================================== From 16ac4407e28273b8cf81d1d10260ece2821f5ddc Mon Sep 17 00:00:00 2001 From: Sam Heinz Date: Wed, 13 May 2026 00:45:53 +1000 Subject: [PATCH 20/22] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- misc/tools.func | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 887db46711c..7c30afda85b 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -4076,9 +4076,14 @@ function setup_ffmpeg() { # If no source download (either VERSION empty or download failed), use binary if [[ -z "$VERSION" ]]; then msg_info "Setup FFmpeg from pre-built binary" - local ffmpeg_arch - case "$(dpkg --print-architecture 2>/dev/null || echo amd64)" in - arm64) ffmpeg_arch="arm64" ;; + local ffmpeg_arch detected_arch + detected_arch="$(dpkg --print-architecture 2>/dev/null || true)" + if [[ -z "$detected_arch" ]]; then + detected_arch="$(uname -m 2>/dev/null || true)" + fi + case "$detected_arch" in + arm64 | aarch64) ffmpeg_arch="arm64" ;; + amd64 | x86_64) ffmpeg_arch="amd64" ;; *) ffmpeg_arch="amd64" ;; esac if ! CURL_TIMEOUT=300 curl_with_retry "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-${ffmpeg_arch}-static.tar.xz" "$TMP_DIR/ffmpeg.tar.xz"; then From 2c1f2161894b546e0913c84d63a40561440759a5 Mon Sep 17 00:00:00 2001 From: Sam Heinz Date: Wed, 13 May 2026 00:46:44 +1000 Subject: [PATCH 21/22] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- misc/tools.func | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 7c30afda85b..38b5a7b07da 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -8262,9 +8262,16 @@ function setup_yq() { msg_info "Setup yq $LATEST_VERSION" fi - local yq_arch - case "$(dpkg --print-architecture 2>/dev/null || echo amd64)" in - arm64) yq_arch="arm64" ;; + local yq_arch detected_arch + if command -v dpkg &>/dev/null; then + detected_arch="$(dpkg --print-architecture 2>/dev/null)" + else + detected_arch="$(uname -m 2>/dev/null)" + fi + + case "$detected_arch" in + arm64 | aarch64) yq_arch="arm64" ;; + amd64 | x86_64) yq_arch="amd64" ;; *) yq_arch="amd64" ;; esac if ! curl_with_retry "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_${yq_arch}" "$TMP_DIR/yq"; then From 88a6b07334c74df443ac7a574a7ab31e55514de3 Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Wed, 13 May 2026 00:47:01 +1000 Subject: [PATCH 22/22] fix for copilot detected bugs --- misc/build.func | 6 +++++- misc/core.func | 2 +- misc/tools.func | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index c6b3e6006df..0298d639b8a 100644 --- a/misc/build.func +++ b/misc/build.func @@ -5934,7 +5934,11 @@ create_lxc_container() { TEMPLATE_PATH="$(pvesm path "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" 2>/dev/null || true)" if [[ -z "$TEMPLATE_PATH" ]]; then local _tpl_base - _tpl_base=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + _tpl_base=$(awk -v s="$TEMPLATE_STORAGE" ' + $0 ~ "^[^:]+:[[:space:]]*" s "$" {f=1; next} + f && /^[^[:space:]]/ {f=0} + f && $1 == "path" {print $2; exit} + ' /etc/pve/storage.cfg) TEMPLATE_PATH="${_tpl_base:-/var/lib/vz}/template/cache/$TEMPLATE" fi diff --git a/misc/core.func b/misc/core.func index be9a65955a8..c30ad1945a5 100644 --- a/misc/core.func +++ b/misc/core.func @@ -1732,7 +1732,7 @@ ensure_whiptail() { apt_update_safe $STD apt-get install -y whiptail || { msg_error "Failed to install whiptail" - exit 115 + exit 100 } msg_ok "Installed whiptail" } diff --git a/misc/tools.func b/misc/tools.func index 38b5a7b07da..8d70e09b3af 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -3060,6 +3060,7 @@ function fetch_and_deploy_codeberg_release() { # Fall back to architecture heuristic if [[ -z "$url_match" ]]; then for u in $assets; do + [[ "$u" =~ \.deb$ ]] || continue if [[ "${arch,,}" =~ ^(amd64|x86_64)$ ]]; then [[ "$u" =~ (amd64|x86_64).*\.deb$ ]] || continue elif [[ "${arch,,}" =~ ^(arm64|aarch64)$ ]]; then @@ -3573,6 +3574,7 @@ function fetch_and_deploy_gh_release() { # If no match via explicit pattern, fall back to architecture heuristic if [[ -z "$url_match" ]]; then for u in $assets; do + [[ "$u" =~ \.deb$ ]] || continue if [[ "${arch,,}" =~ ^(amd64|x86_64)$ ]]; then [[ "$u" =~ (amd64|x86_64).*\.deb$ ]] || continue elif [[ "${arch,,}" =~ ^(arm64|aarch64)$ ]]; then @@ -3610,6 +3612,7 @@ function fetch_and_deploy_gh_release() { fi if [[ -z "$url_match" ]]; then for u in $assets; do + [[ "$u" =~ \.deb$ ]] || continue if [[ "${arch,,}" =~ ^(amd64|x86_64)$ ]]; then [[ "$u" =~ (amd64|x86_64).*\.deb$ ]] || continue elif [[ "${arch,,}" =~ ^(arm64|aarch64)$ ]]; then