Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a5c60d4
test: resolve fallback templates from the linuxcontainers catalog
MickLesk Sep 4, 2026
9c22b37
Find the images a distro actually publishes, not just the default var…
MickLesk Sep 7, 2026
6729e36
Fix script slug generation for descriptions
MickLesk Sep 7, 2026
aead014
test: resolve fallback templates from the linuxcontainers catalog
MickLesk Sep 4, 2026
ac005d5
Find the images a distro actually publishes, not just the default var…
MickLesk Sep 7, 2026
0fc1f96
Only offer another template version when there is one
MickLesk Sep 7, 2026
f68f896
Merge branch 'test/linuxcontainers-full-catalog' of https://github.co…
MickLesk Sep 7, 2026
9592069
Track detected HW accel vendor in setup
MickLesk Sep 7, 2026
074f3ce
Reach the image catalog on every architecture, not just arm64
MickLesk Sep 7, 2026
39a6ad4
Merge remote-tracking branch 'origin/main' into test/linuxcontainers-…
MickLesk Sep 25, 2026
18d830e
Translate the releases Proxmox and the image catalog disagree on
MickLesk Sep 25, 2026
c1098e9
Create Amazon Linux containers and keep the MOTD working without host…
MickLesk Sep 25, 2026
3b5af6d
Give the patched template a release PVE actually accepts
MickLesk Sep 25, 2026
5638f0b
Merge remote-tracking branch 'origin/main' into test/linuxcontainers-…
Oct 6, 2026
4ae0ccd
Install dependencies with the container's own package manager
MickLesk Sep 24, 2026
121ec84
Merge remote-tracking branch 'origin/main' into test/linuxcontainers-…
MickLesk Oct 6, 2026
8ae4fa1
Close the linuxcontainers.org lookup message
MickLesk Oct 6, 2026
74635f5
Stop early when the image has no build for this architecture
MickLesk Oct 6, 2026
3a2cda3
Set up Arch Linux ARM containers
MickLesk Oct 6, 2026
9f05d66
Autoremove before dnf clean all
MickLesk Oct 6, 2026
f6c5b82
Drop the second MOTD on Incus
MickLesk Oct 6, 2026
b4e5e67
Stop leaked message blocks from spinning over the install
MickLesk Oct 6, 2026
c7b3113
Merge remote-tracking branch 'origin/main' into test/linuxcontainers-…
MickLesk Oct 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 37 additions & 34 deletions incus/backend.func
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,18 @@ _incus_restart_ct() {
[[ "$(incus_instance_field "${CT_NAME}" s)" == "RUNNING" ]]
}

# An alias resolves whether or not the image exists for this host's
# architecture; the launch then fails with "architecture isn't supported".
_incus_image_fits_host() {
local arch
arch=$(incus image info "$1" 2>/dev/null | awk '/^Architecture:/ {print $2; exit}' || true)
[[ -n "$arch" ]] || return 2
[[ "$arch" == "$(uname -m)" ]]
}

_incus_resolve_launch_image() {
# Tries images:OS/VER (+ /cloud, codename alias, local cache). Sets INCUS_LAUNCH_IMAGE.
# Returns 2 when the image exists, but not for this architecture.
local os="${IMAGE_OS:-debian}" ver="${IMAGE_VERSION:-13}" alias=""
os="${os,,}"
alias="$(incus_get_image_alias "$os" "$ver" 2>/dev/null || true)"
Expand All @@ -129,28 +139,34 @@ _incus_resolve_launch_image() {
for cand in "${candidates[@]}"; do
if [[ -n "$local_aliases" ]] &&
grep -qxF "${cand#images:}" <<<"$local_aliases" &&
incus image info "${cand#images:}" &>/dev/null; then
_incus_image_fits_host "${cand#images:}"; then
INCUS_LAUNCH_IMAGE="${cand#images:}"
return 0
fi
done

local fit other_arch=0
for cand in "${candidates[@]}"; do
if incus image info "$cand" &>/dev/null; then
fit=0
_incus_image_fits_host "$cand" || fit=$?
if ((fit == 0)); then
INCUS_LAUNCH_IMAGE="$cand"
return 0
fi
((fit == 1)) && other_arch=1
done

# Not on the image server under any name we know: take its newest version.
local newest
newest=$(incus image alias list images: "${os}/" -f csv 2>/dev/null | cut -d, -f1 |
awk -F/ 'NF == 2 && $2 ~ /^[0-9][0-9.]*$/ {print $2}' | sort -uV | tail -n1 || true)
if [[ -n "$newest" ]] && incus image info "images:${os}/${newest}" &>/dev/null; then
msg_warn "${os} ${ver} is not on the image server; using ${os} ${newest}"
INCUS_LAUNCH_IMAGE="images:${os}/${newest}"
return 0
fi
for newest in $(incus image alias list images: "${os}/" -f csv 2>/dev/null | cut -d, -f1 |
awk -F/ 'NF == 2 && $2 ~ /^[0-9][0-9.]*$/ {print $2}' | sort -urV || true); do
if _incus_image_fits_host "images:${os}/${newest}"; then
msg_warn "${os} ${ver} is not on the image server; using ${os} ${newest}"
INCUS_LAUNCH_IMAGE="images:${os}/${newest}"
return 0
fi
done
((other_arch)) && return 2

# Last resort: try remote launch of primary (incus may pull on demand)
INCUS_LAUNCH_IMAGE="images:${os}/${ver}"
Expand Down Expand Up @@ -365,7 +381,12 @@ incus_create_lxc_container() {

msg_info "Resolving container image"
local image_name=""
if _incus_resolve_launch_image; then
local resolve_rc=0
_incus_resolve_launch_image || resolve_rc=$?
if ((resolve_rc == 2)); then
msg_error "${IMAGE_OS} ${IMAGE_VERSION} has no $(uname -m) image on the image server"
exit 106
elif ((resolve_rc == 0)); then
image_name="${INCUS_LAUNCH_IMAGE}"
msg_ok "Image ${BL}${image_name}${CL}"
else
Expand Down Expand Up @@ -430,12 +451,13 @@ incus_create_lxc_container() {

if ! incus launch "${image_name}" "${CT_NAME}" "${launch_args[@]}" >>"$LOGFILE" 2>&1; then
# Retry alternate candidates once if primary pull failed
local retry_img="" launched=0
local retry_img="" launched=0 tried=" ${image_name} "
for retry_img in "images:${IMAGE_OS}/${IMAGE_VERSION}/cloud" \
"images:${IMAGE_OS}/$(incus_get_image_alias "${IMAGE_OS}" "${IMAGE_VERSION}" 2>/dev/null || true)" \
"images:${IMAGE_OS}/$(incus_get_image_alias "${IMAGE_OS}" "${IMAGE_VERSION}" 2>/dev/null || true)/cloud"; do
[[ -z "$retry_img" || "$retry_img" == "$image_name" || "$retry_img" == */ ]] && continue
msg_warn "Launch failed with ${image_name} — retrying ${retry_img}"
[[ -z "$retry_img" || "$retry_img" == */ || "$tried" == *" ${retry_img} "* ]] && continue
tried+="${retry_img} "
msg_warn "Launch failed, retrying with ${retry_img}"
if incus launch "${retry_img}" "${CT_NAME}" "${launch_args[@]}" >>"$LOGFILE" 2>&1; then
image_name="$retry_img"
launched=1
Expand Down Expand Up @@ -1026,26 +1048,6 @@ EOF"
msg_ok "Customized LXC Container"
}

incus_setup_motd_pve_style() {
msg_info "Setting up MOTD"
local motd_file
motd_file=$(mktemp)
cat >"$motd_file" <<MOTDEOF
#!/bin/bash
# Community Scripts MOTD
if [[ -f /etc/os-release ]]; then . /etc/os-release; fi
echo ''
echo ' 🚀 ${APP} LXC'
echo " 🖥️ OS: \${PRETTY_NAME:-Linux}"
echo " 📡 IP: \$(hostname -I 2>/dev/null | awk '{print \$1}')"
echo ''
MOTDEOF
incus file push "$motd_file" "${CT_NAME}/etc/profile.d/99-community-scripts-motd.sh" >>"${LOGFILE:-$INCUS_BUILD_LOG}" 2>&1
incus_ct_exec chmod +x /etc/profile.d/99-community-scripts-motd.sh >>"${LOGFILE:-$INCUS_BUILD_LOG}" 2>&1
rm -f "$motd_file"
msg_ok "MOTD configured"
}

_incus_push_functions_and_run_install() {
# Sets INCUS_LAST_INSTALL_EXIT (do not capture stdout — msg_* writes there).
INCUS_LAST_INSTALL_EXIT=0
Expand All @@ -1066,6 +1068,8 @@ INNEREOF" >>"${LOGFILE:-$INCUS_BUILD_LOG}" 2>&1 || { msg_error "Failed to push f
# prompts - the spinner kept redrawing over them, which is why Immich's
# machine-learning menu could not be read and the keystroke landed in the
# spinner line. A static line, and the script owns the terminal from here.
# Emptied first, or msg_custom would resume a block the host left open.
_MSG_BLOCKS=()
msg_custom "🚀" "${GN}" "Starting application installation"
local _install_script _run_env lxc_exit=0 install_exit_code=0
# Checked here: inside $(...) below, stdout is the capture pipe, never the terminal.
Expand Down Expand Up @@ -1489,7 +1493,6 @@ incus_build_container() {
incus_fix_debian13_root_ownership
incus_customize_container
install_ssh_keys_into_ct
incus_setup_motd_pve_style
incus_run_install_script_with_recovery
}

Expand Down
2 changes: 1 addition & 1 deletion incus/tools.func
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ motd_ssh() {
local ip
ip=$(get_lxc_ip)
cat <<EOF >/etc/motd
🚀 Incus Container: $(hostname)
🚀 Incus Container: $(uname -n)
🖥️ OS: $(grep ^PRETTY_NAME /etc/os-release 2>/dev/null | cut -d= -f2 | tr -d '"')
📡 IP Address: ${ip}
EOF
Expand Down
42 changes: 40 additions & 2 deletions lib/system.func
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,41 @@ ensure_dependencies() {
return 0
fi

# dnf/zypper/pacman/emerge: no cheap name-to-binary map, so go by command and
# let the manager skip whatever is already there.
local family=""
declare -F _cs_os_family >/dev/null 2>&1 && family="$(_cs_os_family)"
case "$family" in
rhel | suse | arch | gentoo)
local -a pm=()
case "$family" in
rhel)
if command -v dnf >/dev/null 2>&1; then pm=(dnf install -y); else pm=(yum install -y); fi
;;
suse) pm=(zypper --non-interactive install --no-recommends) ;;
arch) pm=(pacman -S --noconfirm --needed) ;;
gentoo) pm=(emerge --quiet --noreplace) ;;
esac

for dep in "${deps[@]}"; do
command -v "$dep" >/dev/null 2>&1 || missing+=("$dep")
done
((${#missing[@]})) || return 0

$STD "${pm[@]}" "${missing[@]}" || {
local failed=()
for pkg in "${missing[@]}"; do
$STD "${pm[@]}" "$pkg" 2>/dev/null || failed+=("$pkg")
done
if ((${#failed[@]})); then
msg_error "Failed to install dependencies: ${failed[*]}"
return 1
fi
}
return 0
;;
esac

# Debian/Ubuntu: Fast batch check using dpkg-query
local installed_pkgs
installed_pkgs=$(dpkg-query -W -f='${Package}\n' 2>/dev/null | sort -u)
Expand Down Expand Up @@ -2102,8 +2137,11 @@ verify_gpg_fingerprint() {
create_self_signed_cert() {
local APP_NAME="${1:-${APPLICATION}}"
local EXTRA_SAN="${2:-}"
local HOSTNAME="$(hostname -f)"
local IP="$(hostname -I | awk '{print $1}')"
# RHEL-family minimal images ship no hostname binary, which left CN and SAN empty.
local HOSTNAME IP
HOSTNAME="$(hostname -f 2>/dev/null || uname -n)"
IP="$(hostname -I 2>/dev/null | awk '{print $1}')"
[[ -n "$IP" ]] || IP="$(ip -4 route get 1 2>/dev/null | sed -n 's/.* src \([0-9.]\+\).*/\1/p' | head -1)"
local APP_NAME_LC=$(echo "${APP_NAME,,}" | tr -d ' ')
local CERT_DIR="/etc/ssl/${APP_NAME_LC}"
local CERT_KEY="${CERT_DIR}/${APP_NAME_LC}.key"
Expand Down
8 changes: 4 additions & 4 deletions lxc/install.func
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ detect_os() {
OS_FAMILY="suse"
PKG_MANAGER="zypper"
;;
arch | archlinux)
arch | archlinux | archarm)
OS_TYPE="arch"
OS_FAMILY="arch"
PKG_MANAGER="pacman"
Expand Down Expand Up @@ -869,8 +869,8 @@ pkg_clean() {
$STD apk cache clean
;;
dnf)
$STD dnf clean all
$STD dnf autoremove -y
$STD dnf clean all
;;
yum)
$STD yum clean all
Expand Down Expand Up @@ -1216,7 +1216,7 @@ setting_up_container() {
# upgrade). The Arch base template ships without an initialized keyring.
if [[ ! -f /etc/pacman.d/gnupg/pubring.gpg ]] || [[ ! -s /etc/pacman.d/gnupg/pubring.gpg ]]; then
$STD pacman-key --init
$STD pacman-key --populate archlinux
$STD pacman-key --populate
fi
fi

Expand Down Expand Up @@ -1518,7 +1518,7 @@ EOF
echo -e "${GATEWAY:-}${YW:-}Provided by: ${GN:-}community-scripts ORG ${YW:-}| GitHub: ${GN:-}https://github.com/community-scripts/ProxmoxVE${CL:-}"
echo ""
echo -e "${OS:-}${YW:-}OS: ${GN:-}${os_name} - Version: ${os_version}${CL:-}"
echo -e "${HOSTNAME:-}${YW:-}Hostname: ${GN:-}\$(hostname)${CL:-}"
echo -e "${HOSTNAME:-}${YW:-}Hostname: ${GN:-}\$(uname -n)${CL:-}"
echo -e "${INFO:-}${YW:-}IP Address: ${GN:-}\$(ip -4 route get 1 2>/dev/null | sed -n 's/.* src \\([0-9.]\\+\\).*/\\1/p' | head -1)${CL:-}"
EOF

Expand Down
Loading
Loading