Skip to content

Instantly share code, notes, and snippets.

@digitaltrails
Last active March 12, 2026 07:44
Show Gist options
  • Select an option

  • Save digitaltrails/c06154deb43306baa6921cb24616c6e3 to your computer and use it in GitHub Desktop.

Select an option

Save digitaltrails/c06154deb43306baa6921cb24616c6e3 to your computer and use it in GitHub Desktop.
update/install/lock Nvidia OpenSUSE Open Gxx and kernel packages
#!/bin/bash
# nvidia_OpenSUSE_Gxx_updater - update/install/lock Nvidia OpenSUSE Open Gxx and kernel packages
# ==============================================================================================
# Copyright (C) 2026 Michael Hamilton
#
# This script attempts to identify the correct or current Nvidia
# OpenSUSE packaged versions for the G06/G07 driver,
# supporting packages, and kernel. If that succeeds, locks can
# optionally be applied to keep the working combo in sync. If
# a new viable combo is identified, the script can use zypper
# to do version specific updates to precisely that combo.
#
# The script is careful to identify matching versions of packages
# from the Nvidia/CUDA repos and OpenSUSE repos. This prevents
# broken updates from occurring due to the participating repo's
# latest package versions not being in sync.
#
# For example, the CUDA repo includes multiple versions of each
# package. The latest CUDA packages are often ahead of OpenSUSE's
# signed driver packages. Zypper isn't aware of the cross repo
# version dependencies, it will install the latest versions from
# each, which may be a non-function mismatch. This script
# prevents these kind of mismatches.
#
# The script won't make any changes without first detailing them
# and prompting for permission.
#
# The script defaults to using the OpenSUSE signed driver, but
# the default can be overridden.
#
# The script has only been tested for the G06 and G07 drivers, I don't
# have the environments necessary to test for any others. It may
# be a viable approach for maintaining earlier drivers, but
# modifications may be required.
#
# Usage
# =====
#
# It's a good idea to do a backup first (snap or whatever).
#
# First time use
# --------------
#
# Run the script once to put in place the locks. If it's been
# some time since you've updated, you can elect not to perform
# anything except the locking.
#
# Subsequent use
# --------------
#
# Once the locks are in place, the script can be run after
# zypper-dup to separately update the kernel and driver,
# or simply run it any time you think its time to update
# the kernel and driver. For example:
#
# 0, Pre-zypper-dup backups (as per your normal processes).
#
# 1. Use zypper dup as per normal, the locks prevent
# any driver or kernel updates, for example:
#
# zypper dup --auto-agree-with-licenses --no-allow-vendor-change
#
# 2. After a dup also run nvidia_open_Gxx_updater.bash which
# will determine if a kernel+driver pairing is available,
# and optionally perform the updates.
#
# nvidia_OpenSUSE_Gxx_updater.bash
#
# Note it will ask questions allowing you to alter or
# confirm choices it has made.
#
#
# GNU License
# ===========
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, version 3.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see https://www.gnu.org/licenses/.
#
echo "INFO: OpenSUSE Open Gxx Nvidia driver update script - for desktop PC with a modern Nvidia card"
echo "WARNING: This scripts adds zypper locks to prevent zypper dup from updating the kernel and driver"
echo ""
if [ "$1" == 'y' ]
then
DEFAULT_UPDATE_CONFIRM='y'
else
DEFAULT_UPDATE_CONFIRM='n'
fi
CONFIG_DIR="$HOME/.config/nvidia_open_Gxx_updater"
mkdir -p "$CONFIG_DIR"
CONFIG_FILE="$CONFIG_DIR/$HOSTNAME"
if [ -f "$CONFIG_FILE" ]
then
echo "INFO: reading config from $CONFIG_FILE"
cat < "$CONFIG_FILE"
echo ""
source "$CONFIG_FILE"
fi
confirm() {
echo "" >&2
default_value="${DEFAULT:-no default}"
if [ -n "$REMEMBER" ]
then
config_value=${!REMEMBER}
if [ "$config_value" == "y" -o "$config_value" == "n" ]
then
echo "INFO: config $REMEMBER=$config_value"
default_value="$config_value"
fi
fi
while true; do
read -p ">>>> $1 [y/n]? ($default_value) " yn
if [ -z "$yn" -a "$default_value" != "no default" ]
then
yn="$default_value"
fi
case $yn in
[Yy]* ) echo ""; value=0; break;;
[Nn]* ) echo ""; value=1; break;;
* ) echo "Please answer yes or no.";;
esac
done
if [ -n "$REMEMBER" ]
then
[ -f $CONFIG_FILE ] && sed -i "/^$REMEMBER[=]/d" $CONFIG_FILE
echo "$REMEMBER=$([ -n $value ] && echo 'y' || echo 'n')" >> $CONFIG_FILE
fi
return $value
}
prompt_for_input() {
if [ -n "$REMEMBER" ]
then
config_value="${!REMEMBER}"
if [ -n "$config_value" ]
then
echo "INFO: config $REMEMBER='$config_value'" >&2
default_value="$config_value"
fi
fi
echo "" >&2
prompt=$1
shift
default_value="${DEFAULT}"
possible_values=""
if [ $# -gt 1 ]
then
possible_values="$@"
prompt="$prompt [$(echo $possible_values | sed 's| |/|g')]"
fi
while true
do
read -p ">>>> $prompt ($default_value) " value
value=${value:-$default_value}
if [ -n "$value" ]
then
if [ -z "$possible_values" ]
then
break
fi
if echo " $possible_values " | grep -q " $value "
then
break
fi
fi
done
echo "" >&2
if [ -n "$REMEMBER" ]
then
[ -f $CONFIG_FILE ] && sed -i "/^$REMEMBER[=]/d" $CONFIG_FILE
echo "$REMEMBER='$value'" >> $CONFIG_FILE
fi
echo $value
}
drop_output() {
cat > /dev/null
}
dedent() {
local first_line
# Read the first line and determine its leading whitespace
IFS= read -r first_line
local indent=$(echo "$first_line" | sed 's/^\([[:space:]]*\).*/\1/')
# Process the first line and then the rest of the stream
{ echo "${first_line#"$indent"}"; sed "s/^$indent//"; }
}
nvidia_gpu=$(lspci | awk '$2 == "VGA" && $0 ~ /NVIDIA/ { sub(".*: ", ""); sub("NVIDIA Corporation ",""); print }')
if [ -n "$nvidia_gpu" ]
then
echo "INFO: GPU: $nvidia_gpu"
else
echo "WARNING: lspci scan failed to identify any installed Nvidia GPU."
fi
# highest priority repo that refers to download.nvidia.com
nvidia_repo_list=$(zypper repos -E -u -P |
awk -F' +[|] +' '
$8 ~ /download.nvidia.com/ {
repos[++count] = $2;
priority[$2] = $7;
}
END {
for (i=1; i<=count; i++) {
printf("%s%s", sep, repos[i]);
sep = " ";
if (i > 1) {
if (priority[repos[i]] == priority[repos[1]]) {
printf("WARNING: more than one Nvidia repo with the same priority %s %s and %s!\n",
priority[repos[1]], repos[1], repos[i]) > "/dev/stderr";
}
else {
printf("WARNING: more than one enabled Nvidia repo %s (priority %s) and %s (priority %s)!\n",
repos[1], priority[repos[1]], repos[i], priority[repos[i]]) > "/dev/stderr";
}
}
}
print "";
}
'
)
if [ -n "$nvidia_repo_list" ]
then
nvidia_repo=$(echo "$nvidia_repo_list" | awk '{print $1;}')
echo "INFO: Detected Nvidia proprietary repo: $nvidia_repo"
if [ -n "$NVIDIA_REPO" -a "$nvidia_repo" != "$NVIDIA_REPO" ]
then
echo "WARNING: detected and config repos differ - $nvidia_repo != $NVIDIA_REPO - ignoring config."
NVIDIA_REPO=""
fi
fi
nvidia_repo=$(DEFAULT="$nvidia_repo" REMEMBER=NVIDIA_REPO prompt_for_input "Nvidia repo name?")
possible_g_variants="G07 G06 G05 G04"
g_variant=$(zypper se -i 'nvidia-open-driver*' |
awk -F' +[|] +' '$2 ~ /^nvidia-open-driver/ { sub(".*G", "G", $2); sub("-.*", "", $2); print $2; exit 0 }')
if [ -n "$g_variant" ]
then
echo "INFO: Nvidia driver OpenSUSE package variant $g_variant already installed."
fi
if [ -z "$g_variant" -a -n "$nvidia_gpu" ]
then
echo "INFO: Failed to detect an installed OpenSUSE Gxx package."
echo "INFO: Trying to match an appropriate Gxx variant for a $nvidia_gpu"
g_variant=$(awk -vgpu="$nvidia_gpu" '
BEGIN {
g_driver["RTX [34]0[0-9]{2}"] = "G07";
g_driver["GTX 1[0-9]{3}"] = "G06";
g_driver["GTX [67][0-9]{2}"] = "G06";
g_driver["GT [2345][0-9]{2}"] = "G05";
for (gd in g_driver) {
if (gpu ~ gd) {
print g_driver[gd];
exit 0;
}
}
}
')
if [ -n "$g_variant" ]
then
echo "INFO: Matched OpenSUSE $g_variant package for a $nvidia_gpu"
else
echo "INFO: Failed to match an OpenSUSE Gxx package, defaulting to G07 (the latest)."
fi
fi
g_variant=$(DEFAULT="${g_variant:-G07}" REMEMBER=GXX_VARIANT prompt_for_input "Nvidia driver OpenSUSE package variant?" $possible_g_variants)
if [ "$g_variant" != "G06" -a "$g_variant" != "G07" ]
then
echo "ERROR: This script has only been tested with open G06 and G07 variants. The $g_variant has not been tested."
exit 1
fi
echo "INFO: Nvidia repo: $nvidia_repo"
echo "INFO: Nvidia driver OpenSUSE package variant: $g_variant"
kernel=$(uname -r)
kernel_version=$(echo $kernel | sed s/-.*$//)
kernel_variant=$(echo $kernel | sed s/^[0-9.-]*//)
kernel_package_name=kernel-$kernel_variant
case $g_variant in
'G06' | 'G07' )
nvidia_driver_prefix=nvidia-open-driver
supporting_packages="nvidia-video-Gxx nvidia-compute-utils-Gxx nvidia-persistenced"
if zypper --no-refresh lr --show-enabled-only $nvidia_repo | grep '^URI' | grep -q cuda
then
k_variant="signed-cuda-kmp-$kernel_variant"
else
k_variant="signed-kmp-$kernel_variant"
fi
nvidia_driver_name="${nvidia_driver_prefix}-${g_variant}-${k_variant}"
supporting_packages="nvidia-video-Gxx nvidia-compute-utils-Gxx nvidia-persistenced"
;;
'G04' | 'G05')
nvidia_driver_name="nvidia-open-${g_variant}-${k_variant}"
supporting_packages="x11-video-nvidiaGxx nvidia-computeGxx"
;;
*)
echo "ERROR: unsupported Gxx variant $g_variant"
exit 1
;;
esac
echo "INFO: OpenSUSE Nvidia Gxx driver default: $nvidia_driver_name"
echo "INFO: Supporting packages: $supporting_packages"
nvidia_driver_name=$(DEFAULT="$nvidia_driver_name" REMEMBER=NVIDIA_DRIVER_NAME prompt_for_input "Nvidia driver name: ")
echo "INFO: Linux kernel package: $kernel_package_name"
echo "INFO: Nvidia driver OpenSUSE package: $nvidia_driver_name"
# need to match with the running kernel - assumes the running kernel came from a package
installed_kernel_version=$kernel_version
echo "INFO: Current kernel version: $installed_kernel_version"
installed_nvidia_version=$(zypper search --details -i $nvidia_driver_name | awk -F' +[|] +' '$3 ~ /package/ { sub(/-.*$/, "", $4); print $4; exit 0 }')
if [ -n $installed_version ]
then
echo "INFO: Installed version of $nvidia_driver_name is $installed_nvidia_version"
else
echo "INFO: Driver not currently installed."
fi
available_nvidia_version=$(zypper --no-refresh info $nvidia_driver_name | awk -F' +[:] +' '$1 == "Version" { sub(/-.*$/, "", $2); print $2; exit 0 }')
if [ -n $available_nvidia_version ]
then
echo "INFO: Available version of $nvidia_driver_name is $available_nvidia_version"
else
echo "INFO: Driver not currently available!"
exit -1
fi
if [ -n $available_nvidia_version ]
then
required_kernel_version=$(echo $available_nvidia_version | sed 's/[^_]*_k//;s/_.*//')
echo "INFO: Required kernel is $kernel_package_name $required_kernel_version"
if [ -n "$(zypper --no-refresh se -s --match-exact $kernel_package_name | awk -vkv="$required_kernel_version" -F' +[|] +' '$4 ~ "^" kv {print}')" ]
then
echo "INFO: Required $kernel_package_name $required_kernel_version is available"
else
echo "WARNING: required kernel $kernel_package_name $required_kernel_version is unavailable from zypper."
possible_kernel_version=$(zypper --no-refresh se -s --match-exact $kernel_package_name | awk -F' +[|] +' -vrkv=$required_kernel_version '
BEGIN {
prefix=rkv; sub("[.].*$", "", prefix); # Remove last part of kernel version number
}
$4 ~ "^" prefix {
split(rkv, rkvparts, ".");
split($4, akvparts, ".");
if (akvparts[1] == rkvparts[1] && akvparts[2] == rkvparts[2]) {
if (akvparts[3] > rkvparts[3]) { # Available kernel last-part of version > required last-part
sub("-.*", "", $4);
print $4; # Possible alternative kernel
exit 0;
}
}
}')
if [ -n "$possible_kernel_version" ]
then
echo "INFO: Kernel $possible_kernel_version may work (often minor version changes will work OK)."
if confirm "Do you want to try with $kernel_package_name $possible_kernel_version instead of $required_kernel_version?"
then
required_kernel_version=$possible_kernel_version
else
echo "ERROR: No possible kernel, cannot continue."
exit 1
fi
else
echo "ERROR: No matching kernel and no more up to date kernel available, cannot continue."
exit 1
fi
fi
fi
nvidia_release=$(echo $available_nvidia_version | awk -F_ '{print $1}')
if [ -n "$supporting_packages" ]
then
final_list=""
for package in $supporting_packages
do
package_g_variant=$(echo $package | sed s/Gxx/$g_variant/)
package_fully_qualified="$package_g_variant-$nvidia_release"
if zypper --no-refresh search -r "$nvidia_repo" $package_fully_qualified > /dev/null
then
if zypper --no-refresh search -ir "$nvidia_repo" --match-exact $package_fully_qualified > /dev/null
then
echo "INFO: Supporting $package_fully_qualified is already installed (from $nvidia_repo repo)"
else
echo "INFO: Supporting $package_fully_qualified is available (from $nvidia_repo repo)"
final_list="$final_list $package"
fi
else
echo "ERROR: Supporting $package_fully_qualified is not available from $nvidia_repo repo"
exit 1
fi
done
supporting_packages_with_updates="$final_list"
if [ -n "$supporting_packages_with_updates" ]
then
echo "INFO: Supporting packages with updates: $supporting_packages_with_updates"
else
echo "INFO: Supporting packages already up to date for $available_nvidia_version."
fi
fi
if [ "$required_kernel_version" == "$kernel_version" ]
then
echo "INFO: Required Linux kernel $required_kernel_version already installed"
elif [ -n "$required_kernel_version" ]
then
echo "INFO: Linux kernel requires upgrading to $required_kernel_version before updating the Nvidia driver"
else
echo "ERROR: Cannot match Nvidia driver $available_nvidia_version to Linux kernel $required_kernel_version"
exit 1
fi
if [ "$available_nvidia_version" == "$installed_nvidia_version" -a -z "$supporting_packages_with_updates" ]
then
DEFAULT="n" confirm "No Nvidia updates required/available, proceed anyway (to setup locks perhaps)?" || exit 0
fi
kernel_lock="kernel-*"
kernel_supporting_packages=""
g_variant_lock="*nvidia*${g_variant}*"
g_supporting_packages_locks=$(echo "$supporting_packages" | sed 's/[a-zA-Z0-9_-]*Gxx[a-zA-Z0-9_-]*//g')
if DEFAULT='y' REMEMBER=LOCK_KERNEL_AND_DRIVER confirm "Lock down Linux kernel $kernel_lock $kernel_supporting_packages and $g_variant packages to keep them in sync?"
then
set -x
zypper al "$kernel_lock" $kernel_supporting_packages "$g_variant_lock" $g_supporting_packages_locks
{ set +x; } 2>/dev/null
fi
if DEFAULT='y' REMEMBER=LOCK_OTHER_GXX confirm "Lock down other Gxx packages to keep them from erroneously being picked up by zypper dup?"
then
for other_variant in $possible_g_variants
do
if [ "$other_variant" != "$g_variant" ]
then
other_variant_lock="*nvidia*${other_variant}*"
set -x
zypper al "$other_variant_lock"
{ set +x; } 2>/dev/null
fi
done
fi
package_specifiers=(
"$kernel_package_name" == "$required_kernel_version"
"${kernel_package_name}-devel" == "$required_kernel_version"
"$nvidia_driver_name" == "$available_nvidia_version")
for package in $supporting_packages
do
package_g_variant=$(echo $package | sed s/Gxx/$g_variant/)
control_variable=$(awk -vp="$package_g_variant" 'BEGIN {p = toupper(p); sub("-", "_", p); print "" p "_UPDATE"; exit 0}')
package_specifiers+=("$package_g_variant" == "$nvidia_release")
done
echo ""
echo "INFO: Package list: " "${package_specifiers[@]}"
echo ""
echo "INFO: Proposed zypper commands:"
cat <<EOF
zypper --quiet rl "$kernel_lock" $kernel_supporting_packages "$g_variant_lock" $g_supporting_packages_locks
zypper --no-refresh install --no-allow-vendor-change ${package_specifiers[@]}
zypper --quiet al "$kernel_lock" $kernel_supporting_packages "$g_variant_lock" $g_supporting_packages_locks
EOF
if DEFAULT="$DEFAULT_UPDATE_CONFIRM" confirm "Perform updates?"
then
echo "INFO: note that zypper will only update packages that actually require updating"
set -x
zypper --quiet rl "$kernel_lock" $kernel_supporting_packages "$g_variant_lock" $g_supporting_packages_locks
zypper --no-refresh install --no-allow-vendor-change ${package_specifiers[@]}
zypper --quiet al "$kernel_lock" $kernel_supporting_packages "$g_variant_lock" $g_supporting_packages_locks
{ set +x; } 2>/dev/null
fi
echo ""
echo "INFO: finished"
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment