跳转至

在 Ubuntu Linux 和 Alma/Rocky Linux 上安装 GPU NVIDIA 驱动和 CUDA

在这篇文章中

我们建议仅在 Ubuntu 22.04/24.04/26.04 的 LTS 版本上使用 Nvidia 显卡。若要在其他发行版上安装驱动,请使用我们为基于 CentOS 的 RPM 发行版编写的脚本,或遵循官方开发人员指南:

警告

为了确保 Tesla 系列 GPU(例如 NVIDIA Tesla T4)正常运行,请确保在服务器 BIOS 中启用了“above 4G decoding”、“large/64bit BARs”或“Above 4G MMIO BIOS assignment”参数。

打开服务器控制台(通过原生控制台或 SSH),以 root 身份登录,复制下面的脚本,将其粘贴到命令行中,然后按 Enter 即可自动安装驱动和 CUDA。如果您的软件需要 docker 或 docker compose 才能工作,请在安装驱动**之前**安装它们,以便脚本可以将 GPU 支持“透传”给容器。

备注

如果您使用的命令行 Shell 不是 bash(例如 zsh 或其他),请通过执行 exec bash 切换到 bash 以运行脚本。

警告

在安装过程中,您可能需要按 Enter 以确认安装新的内核模块或重启服务。

Ubuntu Linux 安装脚本

#!/bin/bash
#===============================================================================
# Universal NVIDIA Driver + CUDA Installer for Ubuntu 22.04/24.04/26.04 LTS
# - Legacy GPUs (CC < 7.5): pinned driver + CUDA 12.9 (last branch supporting Pascal/Volta)
# - Blackwell GPUs: open kernel modules + latest CUDA
# - All other GPUs (incl. RTX A5000, CC 8.6): proprietary driver 580 + latest CUDA
# - cuda-drivers meta-package is NEVER installed (avoids pulling open modules)
#===============================================================================
set -euo pipefail

#-------------------------------------------------------------------------------
# CONFIGURATION FLAGS
#-------------------------------------------------------------------------------
DO_OS_POLICY_CHECK=1
ALLOWED_UBUNTU_VERSIONS=("22.04" "24.04" "26.04")

DO_APT_UPGRADE=1
DO_INSTALL_HWE=1
DO_INSTALL_KERNEL_HEADERS=1
DO_INSTALL_BUILD_TOOLS=1

DO_PURGE_OLD_PACKAGES=1
DO_SETUP_CUDA_REPO=1
DO_INSTALL_CUDA_STACK=1

# Legacy GPUs: pinned driver per card (CC < 7.5, CUDA 12.x only)
LEGACY_GPUS=(
  "10de:1b06|GeForce GTX 1080 Ti|nvidia-driver-535"
  "10de:1db1|Tesla V100|nvidia-driver-535"
  "10de:1b80|GeForce GTX 1070|nvidia-driver-535"
  "10de:1c03|GeForce GTX 1060|nvidia-driver-535"
)

# Blackwell GPUs: require open kernel modules
BLACKWELL_GPUS=(
  "10de:2b85|GeForce RTX 5090"
  "10de:2b87|GeForce RTX 5090 D"
  "10de:2b84|GeForce RTX 5080"
  "10de:2b83|GeForce RTX 5070 Ti"
  "10de:2b82|GeForce RTX 5070"
  "10de:2f80|GeForce RTX 5070 Laptop GPU"
  "10de:2bb5|RTX PRO 6000 Blackwell Server Edition"
  "10de:2bb4|RTX PRO 6000 Blackwell Workstation Edition"
  "10de:2bb1|RTX PRO 6000 Blackwell Max-Q"
  "10de:2bb3|RTX PRO 5000 Blackwell"
  "10de:2c31|RTX PRO 4500 Blackwell"
  "10de:2c37|RTX PRO 4000 Blackwell"
  "10de:2c36|RTX PRO 3000 Blackwell"
  "10de:2d30|RTX PRO 2000 Blackwell"
  "10de:2c35|RTX PRO 2000 Blackwell (Alternative)"
  "10de:2c3a|RTX PRO 4500 Blackwell Server Edition"
  "10de:2c77|RTX PRO 5000 Blackwell Embedded GPU"
  "10de:2c38|RTX PRO 5000 Blackwell Generation Laptop GPU"
  "10de:2f38|RTX PRO 3000 Blackwell Generation Laptop GPU"
  "10de:2db8|RTX PRO 1000 Blackwell Generation Laptop GPU"
)

DO_BLACKLIST_NOUVEAU=1
DO_TRY_RMMOD_NOUVEAU=1
DO_USER_GROUPS=0
DO_BASHRC_CUDA_PATHS=1
DO_VERIFY_NVIDIA_SMI=1
DO_VERIFY_NVCC=1
DO_INSTALL_NVIDIA_CONTAINER_TOOLKIT=1
DO_CONFIGURE_DOCKER_RUNTIME=1
DO_RESTART_DOCKER=1

# ---- Start ----
echo "Starting NVIDIA driver + CUDA installation (Non-Interactive)..."

#-------------------------------------------------------------------------------
# DEPENDENCY CHECK
#-------------------------------------------------------------------------------
for cmd in lspci wget gpg curl sed awk uname; do
  command -v "$cmd" >/dev/null 2>&1 || { echo "Missing dependency: $cmd"; exit 1; }
done

#-------------------------------------------------------------------------------
# OS DETECTION
#-------------------------------------------------------------------------------
osr="/etc/os-release"
[[ -r "$osr" ]] || osr="/usr/lib/os-release"
[[ -r "$osr" ]] || { echo "Cannot read os-release file. Exiting."; exit 1; }

set -a
. "$osr"
set +a

[[ "${ID:-}" != "ubuntu" ]] && { echo "This script is intended for Ubuntu only. Exiting."; exit 1; }

UBUNTU_VERSION="${VERSION_ID:-}"
UBUNTU_CODENAME="${VERSION_CODENAME:-}"

if [[ -z "${UBUNTU_CODENAME}" ]]; then
  case "${UBUNTU_VERSION}" in
    "22.04") UBUNTU_CODENAME="jammy" ;;
    "24.04") UBUNTU_CODENAME="noble" ;;
    "26.04") UBUNTU_CODENAME="resolute" ;;
    *) UBUNTU_CODENAME="unknown" ;;
  esac
fi

[[ -z "${UBUNTU_VERSION}" ]] && { echo "Cannot detect Ubuntu VERSION_ID. Exiting."; exit 1; }

if [[ "${DO_OS_POLICY_CHECK:-0}" -eq 1 ]]; then
  ok=0
  for v in "${ALLOWED_UBUNTU_VERSIONS[@]}"; do
    [[ "${UBUNTU_VERSION}" == "${v}" ]] && { ok=1; break; }
  done
  [[ "${ok}" -ne 1 ]] && { echo "Unsupported Ubuntu version: ${UBUNTU_VERSION}"; exit 1; }
fi

RELEASE_VERSION="$(echo "${UBUNTU_VERSION}" | sed 's/\([0-9]\+\)\.\([0-9]\+\)/\1\2/')"
echo "OS: Ubuntu ${UBUNTU_VERSION} (${UBUNTU_CODENAME})"

#-------------------------------------------------------------------------------
# GPU DETECTION
#-------------------------------------------------------------------------------
NVIDIA_GPU_LINES="$(lspci -nn | grep -iE 'vga|3d' | grep -i '10de:' || true)"
[[ -z "${NVIDIA_GPU_LINES}" ]] && { echo "No NVIDIA GPU detected (vendor 10de). Exiting."; exit 1; }

echo "NVIDIA GPUs detected:"
echo "${NVIDIA_GPU_LINES}"

DETECTED_GPUS="$(lspci -nn)"
REBOOT_REQUIRED=0

#-------------------------------------------------------------------------------
# SYSTEM PREPARATION
#-------------------------------------------------------------------------------
export DEBIAN_FRONTEND=noninteractive
export NEEDRESTART_MODE=a

if [[ "${DO_APT_UPGRADE}" -eq 1 ]]; then
  echo "Updating system packages..."
  sudo -E apt-get update
  sudo -E apt-get upgrade -y \
    -o Dpkg::Options::="--force-confdef" \
    -o Dpkg::Options::="--force-confold"
fi

if [[ "${DO_INSTALL_HWE}" -eq 1 ]]; then
  case "${UBUNTU_VERSION}" in
    "22.04") HWE_PKG="linux-generic-hwe-22.04" ;;
    "24.04") HWE_PKG="linux-generic-hwe-24.04" ;;
    "26.04") HWE_PKG="" ;;
    *) HWE_PKG="" ;;
  esac
  if [[ -n "${HWE_PKG}" ]]; then
    echo "Installing HWE kernel: ${HWE_PKG}"
    sudo -E apt-get install -y \
      -o Dpkg::Options::="--force-confdef" \
      -o Dpkg::Options::="--force-confold" \
      "${HWE_PKG}"
    REBOOT_REQUIRED=1
  fi
fi

if [[ "${DO_INSTALL_KERNEL_HEADERS}" -eq 1 ]]; then
  CURRENT_KERNEL="$(uname -r)"
  echo "Installing kernel headers for: ${CURRENT_KERNEL}"
  sudo -E apt-get install -y \
    -o Dpkg::Options::="--force-confdef" \
    -o Dpkg::Options::="--force-confold" \
    "linux-headers-${CURRENT_KERNEL}" || {
    echo "Failed to install linux-headers. DKMS may fail."; exit 1; }
fi

if [[ "${DO_INSTALL_BUILD_TOOLS}" -eq 1 ]]; then
  case "${UBUNTU_VERSION}" in
    "22.04") GCC_PACKAGES=("gcc-12" "g++-12") ;;
    "24.04") GCC_PACKAGES=("gcc-13" "g++-13") ;;
    "26.04") GCC_PACKAGES=("gcc-15" "g++-15") ;;
    *) GCC_PACKAGES=("gcc" "g++") ;;
  esac
  echo "Installing build tools: ${GCC_PACKAGES[*]}"
  sudo -E apt-get install -y -o Dpkg::Options::="--force-confdef" "${GCC_PACKAGES[@]}"
fi

#-------------------------------------------------------------------------------
# PURGE OLD PACKAGES
#-------------------------------------------------------------------------------
if [[ "${DO_PURGE_OLD_PACKAGES}" -eq 1 ]]; then
  echo "Purging previous NVIDIA/CUDA installations..."
  sudo dpkg --configure -a || true
  sudo -E apt-get purge -y "nvidia-*" "libnvidia-*" "cuda-*" "nvidia-driver-*" "*cudnn*" "*nsight*" 2>/dev/null || true
  sudo -E apt-get remove --purge -y nvidia-cuda-toolkit nvidia-prime nvidia-settings 2>/dev/null || true
  sudo -E apt-get autoremove -y || true
  sudo -E apt-get --fix-broken install -y || true
  sudo -E apt-get clean -y || true
fi

#-------------------------------------------------------------------------------
# SETUP CUDA REPOSITORY
#-------------------------------------------------------------------------------
if [[ "${DO_SETUP_CUDA_REPO}" -eq 1 ]]; then
  echo "Setting up CUDA repo for ubuntu${RELEASE_VERSION}..."
  wget -q "https://developer.download.nvidia.com/compute/cuda/repos/ubuntu${RELEASE_VERSION}/x86_64/cuda-keyring_1.1-1_all.deb"
  sudo dpkg -i cuda-keyring_1.1-1_all.deb 2>/dev/null || true
  rm -f cuda-keyring_1.1-1_all.deb
  sudo -E apt-get update
fi

#-------------------------------------------------------------------------------
# BLACKLIST NOUVEAU
#-------------------------------------------------------------------------------
if [[ "${DO_BLACKLIST_NOUVEAU}" -eq 1 ]]; then
  BL_FILE="/etc/modprobe.d/blacklist-nouveau.conf"
  if [[ ! -f "${BL_FILE}" ]] || ! grep -q "^blacklist nouveau" "${BL_FILE}" 2>/dev/null; then
    echo "Blacklisting nouveau (requires reboot)..."
    sudo tee "${BL_FILE}" >/dev/null <<'EOF'
blacklist nouveau
options nouveau modeset=0
EOF
    sudo update-initramfs -u
    REBOOT_REQUIRED=1
  fi
fi

#-------------------------------------------------------------------------------
# INSTALL DRIVER
#-------------------------------------------------------------------------------
IS_BLACKWELL=0
IS_LEGACY=0
IS_PROPRIETARY=0

# 1) Blackwell: open modules
for gpu_spec in "${BLACKWELL_GPUS[@]}"; do
  IFS='|' read -r pci_id gpu_name <<< "$gpu_spec"
  if echo "$DETECTED_GPUS" | grep -q "$pci_id"; then
    echo "Detected Blackwell GPU: $gpu_name ($pci_id)"
    echo "Installing open kernel modules..."
    sudo -E apt-get install -y -o Dpkg::Options::="--force-confdef" \
      nvidia-open nvidia-driver-open nvidia-dkms-open
    IS_BLACKWELL=1
    break
  fi
done

# 2) Legacy GPUs: pinned driver
if [[ "$IS_BLACKWELL" -eq 0 ]]; then
  for gpu_spec in "${LEGACY_GPUS[@]}"; do
    IFS='|' read -r pci_id gpu_name driver_pkg <<< "$gpu_spec"
    if echo "$DETECTED_GPUS" | grep -q "$pci_id"; then
      echo "Detected legacy GPU: $gpu_name ($pci_id) -> $driver_pkg"
      sudo -E apt-get install -y -o Dpkg::Options::="--force-confdef" \
        "$driver_pkg" "nvidia-dkms-${driver_pkg#nvidia-driver-}"
      IS_LEGACY=1
      break
    fi
  done
fi

# 3) Everything else: PROPRIETARY driver 580 (RTX A5000 falls here)
if [[ "$IS_BLACKWELL" -eq 0 && "$IS_LEGACY" -eq 0 ]]; then
  echo "Detected non-Blackwell, non-legacy GPU (e.g., RTX A5000)."
  echo "Installing proprietary driver 580..."
  sudo -E apt-get install -y -o Dpkg::Options::="--force-confdef" \
    nvidia-driver-580 nvidia-dkms-580
  IS_PROPRIETARY=1
fi

#-------------------------------------------------------------------------------
# INSTALL CUDA TOOLKIT
# - Legacy (CC < 7.5): CUDA 12.9 (last branch supporting Pascal/Volta)
# - Blackwell / Proprietary (CC >= 7.5): latest cuda-toolkit meta-package
# - cuda-drivers is NEVER installed
#-------------------------------------------------------------------------------
if [[ "${DO_INSTALL_CUDA_STACK}" -eq 1 ]]; then
  if [[ "$IS_LEGACY" -eq 1 ]]; then
    echo "Installing CUDA Toolkit 12.9 (legacy path, last branch supporting Pascal/Volta)..."
    sudo -E apt-get install -y -o Dpkg::Options::="--force-confdef" cuda-toolkit-12-9
  else
    echo "Installing latest available CUDA Toolkit (Blackwell/proprietary path)..."
    sudo -E apt-get install -y -o Dpkg::Options::="--force-confdef" cuda-toolkit
  fi
fi

#-------------------------------------------------------------------------------
# POST-INSTALL STEPS
#-------------------------------------------------------------------------------
if [[ "${DO_TRY_RMMOD_NOUVEAU}" -eq 1 ]]; then
  sudo rmmod -f nouveau 2>/dev/null || true
fi

if [[ "${DO_USER_GROUPS}" -eq 1 ]]; then
  TARGET_USER="${SUDO_USER:-$USER}"
  sudo usermod -aG video,render "${TARGET_USER}" || true
  echo "User ${TARGET_USER} added to video/render groups."
  REBOOT_REQUIRED=1
fi

if [[ "${DO_BASHRC_CUDA_PATHS}" -eq 1 ]]; then
  TARGET_USER="${SUDO_USER:-$USER}"
  TARGET_HOME="$(getent passwd "${TARGET_USER}" | cut -d: -f6)"
  TARGET_BASHRC="${TARGET_HOME}/.bashrc"
  MARKER="NVIDIA CUDA Paths"

  [[ -f "${TARGET_BASHRC}" ]] || sudo -u "${TARGET_USER}" touch "${TARGET_BASHRC}" || true

  if ! grep -q "${MARKER}" "${TARGET_BASHRC}" 2>/dev/null; then
    cat >> "${TARGET_BASHRC}" <<'EOF'

# NVIDIA CUDA Paths
export PATH="/usr/local/cuda/bin:$PATH"
export LD_LIBRARY_PATH="/usr/local/cuda/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
EOF
    echo "Added CUDA PATH to ${TARGET_BASHRC}"
  fi
fi

#-------------------------------------------------------------------------------
# VERIFICATION
#-------------------------------------------------------------------------------
if [[ "${DO_VERIFY_NVIDIA_SMI}" -eq 1 ]]; then
  echo "Verifying nvidia-smi..."
  nvidia-smi || true
fi

if [[ "${DO_VERIFY_NVCC}" -eq 1 ]]; then
  if command -v nvcc >/dev/null 2>&1; then
    echo "Verifying nvcc..."
    nvcc -V || true
  else
    echo "nvcc not found in PATH yet (re-login/reboot may be needed)"
  fi
fi

#-------------------------------------------------------------------------------
# NVIDIA CONTAINER TOOLKIT
#-------------------------------------------------------------------------------
if [[ "${DO_INSTALL_NVIDIA_CONTAINER_TOOLKIT}" -eq 1 ]]; then
  if command -v docker >/dev/null 2>&1; then
    echo "Docker detected: installing NVIDIA Container Toolkit..."
    sudo -E apt-get update
    sudo -E apt-get install -y -o Dpkg::Options::="--force-confdef" curl gnupg2

    sudo install -d -m 0755 /usr/share/keyrings
    curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
          | sudo gpg --dearmor --yes -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg

    curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
      | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
      | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list >/dev/null

    sudo -E apt-get update
    sudo -E apt-get install -y -o Dpkg::Options::="--force-confdef" nvidia-container-toolkit

    if [[ "${DO_CONFIGURE_DOCKER_RUNTIME}" -eq 1 ]]; then
      command -v nvidia-ctk >/dev/null 2>&1 && sudo nvidia-ctk runtime configure --runtime=docker || true
    fi

    if [[ "${DO_RESTART_DOCKER}" -eq 1 ]]; then
      sudo systemctl restart docker || true
    fi
  else
    echo "Docker not found. Skipping container toolkit."
  fi
fi

#-------------------------------------------------------------------------------
# FINAL
#-------------------------------------------------------------------------------
echo ""
echo "========================================"
echo "Installation finished"
echo "========================================"

if [[ "${REBOOT_REQUIRED}" -eq 1 ]]; then
  echo ">> REBOOT REQUIRED: sudo reboot"
fi

echo "After reboot, verify with:"
echo "  nvidia-smi"
echo "  nvcc -V"

警告

运行脚本后,必须使用 sudo reboot 命令重启服务器。这是激活新内核模块所必需的。

重启后,请使用 nvidia-smi 和 nvcc -V 命令验证驱动和 CUDA 的安装情况。您应该会看到类似于以下内容的输出:

+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 595.71.05              Driver Version: 595.71.05      CUDA Version: 13.2     |
+-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce RTX 4090        On  |   00000000:07:00.0 Off |                  Off |
| 31%   27C    P8              7W /  450W |      33MiB /  24564MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI              PID   Type   Process name                        GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|  No running processes found                                                             |
+-----------------------------------------------------------------------------------------+

以及

nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2026 NVIDIA Corporation
Built on Thu_Mar_19_11:12:51_PM_PDT_2026
Cuda compilation tools, release 13.2, V13.2.78
Build cuda_13.2.r13.2/compiler.37668154_0

Alma/Rocky Linux 安装脚本

#!/bin/bash
#===============================================================================
# Universal NVIDIA Driver + CUDA Installer for AlmaLinux/Rocky Linux 9 & 10
# - Legacy GPUs (CC < 7.5): pinned proprietary driver (535-dkms) + CUDA 12.x
# - Blackwell GPUs: open kernel modules + latest CUDA
# - All other GPUs (incl. RTX A4000/A5000, CC 8.6): proprietary latest-dkms
# - Unknown GPUs: fall back to latest-dkms with an explicit DKMS build check
# - cuda-drivers meta-package is NEVER installed
#===============================================================================
set -euo pipefail

#-------------------------------------------------------------------------------
# CONFIGURATION FLAGS
#-------------------------------------------------------------------------------
DO_OS_POLICY_CHECK=1
ALLOWED_OS_MAJOR_VERSIONS=("9" "10")

DO_DNF_UPGRADE=1
DO_INSTALL_KERNEL_HEADERS=1
DO_INSTALL_BUILD_TOOLS=1

DO_PURGE_OLD_PACKAGES=1
DO_SETUP_CUDA_REPO=1
DO_INSTALL_CUDA_STACK=1

# Legacy GPUs: pinned driver per card (CC < 7.5, CUDA 12.x only).
# For RHEL 9, driver 535 is available via module stream. For RHEL 10,
# legacy support relies on whatever is available or RPM Fusion (not covered).
LEGACY_GPUS=(
  "10de:1b06|GeForce GTX 1080 Ti|535"
  "10de:1db1|Tesla V100|535"
  "10de:1b80|GeForce GTX 1070|535"
  "10de:1c03|GeForce GTX 1060|535"
)

# Blackwell GPUs: require open kernel modules
BLACKWELL_GPUS=(
  "10de:2b85|GeForce RTX 5090"
  "10de:2b87|GeForce RTX 5090 D"
  "10de:2b84|GeForce RTX 5080"
  "10de:2b83|GeForce RTX 5070 Ti"
  "10de:2b82|GeForce RTX 5070"
  "10de:2f80|GeForce RTX 5070 Laptop GPU"
  "10de:2bb5|RTX PRO 6000 Blackwell Server Edition"
  "10de:2bb4|RTX PRO 6000 Blackwell Workstation Edition"
  "10de:2bb1|RTX PRO 6000 Blackwell Max-Q"
  "10de:2bb3|RTX PRO 5000 Blackwell"
  "10de:2c31|RTX PRO 4500 Blackwell"
  "10de:2c37|RTX PRO 4000 Blackwell"
  "10de:2c36|RTX PRO 3000 Blackwell"
  "10de:2d30|RTX PRO 2000 Blackwell"
  "10de:2c35|RTX PRO 2000 Blackwell (Alternative)"
  "10de:2c3a|RTX PRO 4500 Blackwell Server Edition"
  "10de:2c77|RTX PRO 5000 Blackwell Embedded GPU"
  "10de:2c38|RTX PRO 5000 Blackwell Generation Laptop GPU"
  "10de:2f38|RTX PRO 3000 Blackwell Generation Laptop GPU"
  "10de:2db8|RTX PRO 1000 Blackwell Generation Laptop GPU"
)

DO_BLACKLIST_NOUVEAU=1
DO_TRY_RMMOD_NOUVEAU=1
DO_USER_GROUPS=0
DO_BASHRC_CUDA_PATHS=1
DO_VERIFY_NVIDIA_SMI=1
DO_VERIFY_NVCC=1
DO_INSTALL_NVIDIA_CONTAINER_TOOLKIT=1
DO_CONFIGURE_DOCKER_RUNTIME=1
DO_RESTART_DOCKER=1

# ---- Start ----
echo "Starting NVIDIA driver + CUDA installation for RHEL-family (Non-Interactive)..."

#-------------------------------------------------------------------------------
# DEPENDENCY CHECK + AUTO-INSTALL (AlmaLinux 9/10)
#-------------------------------------------------------------------------------
# Mapping: command -> package in AlmaLinux 9/10
declare -A PKG_MAP=(
  [lspci]="pciutils"
  [wget]="wget"
  [curl]="curl"
  [sed]="sed"
  [awk]="gawk"
  [uname]="coreutils"
  [dnf]="dnf"
)

MISSING_CMDS=()
MISSING_PKGS=()

# 1. Check
for cmd in "${!PKG_MAP[@]}"; do
  if ! command -v "$cmd" >/dev/null 2>&1; then
    echo "Missing dependency: $cmd"
    MISSING_CMDS+=("$cmd")
    MISSING_PKGS+=("${PKG_MAP[$cmd]}")
  fi
done

# 2. Install missing packages
if [ ${#MISSING_PKGS[@]} -gt 0 ]; then
  UNIQUE_PKGS=$(printf "%s\n" "${MISSING_PKGS[@]}" | sort -u | tr '\n' ' ')
  echo "Installing packages: $UNIQUE_PKGS"

  if ! command -v dnf >/dev/null 2>&1; then
    echo "ERROR: dnf is not available. Cannot install dependencies automatically." >&2
    exit 1
  fi

  if ! sudo dnf install -y $UNIQUE_PKGS; then
    echo "ERROR: Failed to install packages: $UNIQUE_PKGS" >&2
    exit 1
  fi

  echo "Re-checking dependencies..."
  for cmd in "${MISSING_CMDS[@]}"; do
    if ! command -v "$cmd" >/dev/null 2>&1; then
      echo "ERROR: '$cmd' still missing after installation." >&2
      exit 1
    fi
  done
  echo "All dependencies installed successfully."
else
  echo "All dependencies are present."
fi

#-------------------------------------------------------------------------------
# OS DETECTION
#-------------------------------------------------------------------------------
osr="/etc/os-release"
[[ -r "$osr" ]] || { echo "Cannot read os-release file. Exiting."; exit 1; }

set -a
. "$osr"
set +a

if [[ "${ID:-}" != "almalinux" && "${ID:-}" != "rocky" ]]; then
  echo "This script is intended for AlmaLinux or Rocky Linux only. Exiting."
  exit 1
fi

OS_MAJOR_VERSION="${VERSION_ID%%.*}"
OS_NAME="${ID}"

if [[ "${DO_OS_POLICY_CHECK:-0}" -eq 1 ]]; then
  ok=0
  for v in "${ALLOWED_OS_MAJOR_VERSIONS[@]}"; do
    [[ "${OS_MAJOR_VERSION}" == "${v}" ]] && { ok=1; break; }
  done
  [[ "${ok}" -ne 1 ]] && { echo "Unsupported OS major version: ${OS_MAJOR_VERSION}"; exit 1; }
fi

echo "OS: ${OS_NAME} ${VERSION_ID} (major ${OS_MAJOR_VERSION})"

#-------------------------------------------------------------------------------
# GPU DETECTION
#-------------------------------------------------------------------------------
NVIDIA_GPU_LINES="$(lspci -nn | grep -iE 'vga|3d' | grep -i '10de:' || true)"
[[ -z "${NVIDIA_GPU_LINES}" ]] && { echo "No NVIDIA GPU detected (vendor 10de). Exiting."; exit 1; }

echo "NVIDIA GPUs detected:"
echo "${NVIDIA_GPU_LINES}"

DETECTED_GPUS="$(lspci -nn)"
REBOOT_REQUIRED=0

#-------------------------------------------------------------------------------
# SYSTEM PREPARATION
#-------------------------------------------------------------------------------
if [[ "${DO_DNF_UPGRADE}" -eq 1 ]]; then
  echo "Updating system packages..."
  sudo dnf upgrade -y
fi

# install kernel-devel/headers for the RUNNING kernel explicitly.
# kernel-devel-matched alone can miss the running kernel if it is newer than
# the installed kernel package, which makes DKMS fail with missing headers.
if [[ "${DO_INSTALL_KERNEL_HEADERS}" -eq 1 ]]; then
  CURRENT_KERNEL="$(uname -r)"
  echo "Installing kernel headers/devel for running kernel: ${CURRENT_KERNEL}"
  if ! sudo dnf install -y "kernel-devel-${CURRENT_KERNEL}" "kernel-headers-${CURRENT_KERNEL}"; then
    echo "Exact kernel-devel/headers not available; falling back to kernel-devel-matched."
    sudo dnf install -y kernel-devel-matched kernel-headers || {
      echo "ERROR: Failed to install kernel-devel/headers. DKMS will fail." >&2
      exit 1
    }
  fi
fi

if [[ "${DO_INSTALL_BUILD_TOOLS}" -eq 1 ]]; then
  # RHEL 9/10 provide gcc via the base repo; specific versions are not strictly necessary for DKMS
  echo "Installing build tools: gcc, gcc-c++, make, dkms"
  sudo dnf install -y gcc gcc-c++ make dkms
fi

#-------------------------------------------------------------------------------
# PURGE OLD PACKAGES
#-------------------------------------------------------------------------------
if [[ "${DO_PURGE_OLD_PACKAGES}" -eq 1 ]]; then
  echo "Purging previous NVIDIA/CUDA installations..."
  # Disable module streams if they were previously enabled
  sudo dnf module reset -y nvidia-driver 2>/dev/null || true

  # Remove NVIDIA and CUDA packages
  sudo dnf remove -y '*nvidia*' '*cuda*' 2>/dev/null || true

  # remove stale DKMS build trees. Without this, "dkms autoinstall"
  # keeps rebuilding the previously installed version even after packages
  # were swapped, which is exactly how a 610 build survived a 535 switch.
  sudo rm -rf /var/lib/dkms/nvidia/* 2>/dev/null || true

  sudo dnf autoremove -y || true
  sudo dnf clean all || true
fi

#-------------------------------------------------------------------------------
# TEMPORARY SHA1 ENABLE (for old NVIDIA GPG keys)
#-------------------------------------------------------------------------------
echo "Temporarily enabling SHA1 crypto policy for NVIDIA repo compatibility..."
sudo update-crypto-policies --set DEFAULT:SHA1

#-------------------------------------------------------------------------------
# SETUP CUDA REPOSITORY
#-------------------------------------------------------------------------------
if [[ "${DO_SETUP_CUDA_REPO}" -eq 1 ]]; then
  # NVIDIA uses rhel9/rhel10 repo paths for all RHEL-compatible distros
  CUDA_REPO_DISTRO="rhel${OS_MAJOR_VERSION}"
  REPO_URL="https://developer.download.nvidia.com/compute/cuda/repos/${CUDA_REPO_DISTRO}/x86_64/cuda-${CUDA_REPO_DISTRO}.repo"

  echo "Setting up CUDA repo for ${CUDA_REPO_DISTRO}..."
  sudo dnf config-manager --add-repo "${REPO_URL}" 2>/dev/null || {
    # Fallback for dnf5 (RHEL 10) where config-manager syntax differs
    sudo dnf config-manager addrepo --from-repofile="${REPO_URL}" 2>/dev/null || true
  }

  # Disable the nvidia-driver module from the CUDA repo for RHEL 9 only.
  # RHEL 10 does not use module streams for NVIDIA drivers.
  if [[ "${OS_MAJOR_VERSION}" == "9" ]]; then
    sudo dnf module disable -y nvidia-driver 2>/dev/null || true
  fi

  sudo dnf clean all
  sudo dnf makecache
fi

#-------------------------------------------------------------------------------
# BLACKLIST NOUVEAU
#-------------------------------------------------------------------------------
if [[ "${DO_BLACKLIST_NOUVEAU}" -eq 1 ]]; then
  BL_FILE="/etc/modprobe.d/blacklist-nouveau.conf"
  if [[ ! -f "${BL_FILE}" ]] || ! grep -q "^blacklist nouveau" "${BL_FILE}" 2>/dev/null; then
    echo "Blacklisting nouveau (requires reboot)..."
    sudo tee "${BL_FILE}" >/dev/null <<'EOF'
blacklist nouveau
options nouveau modeset=0
EOF
    sudo dracut --force
    REBOOT_REQUIRED=1
  fi
fi

#-------------------------------------------------------------------------------
# DRIVER INSTALL HELPERS
#-------------------------------------------------------------------------------
install_open_driver() {
  echo "Installing open kernel modules..."
  if [[ "${OS_MAJOR_VERSION}" == "9" ]]; then
    sudo dnf module install -y nvidia-driver:latest-open
  else
    sudo dnf install -y nvidia-open
  fi
}

# Helper to install proprietary driver (legacy or generic)
# $1: optional module stream (e.g. "535") for RHEL 9
install_proprietary_driver() {
  local stream="${1:-}"
  if [[ "${OS_MAJOR_VERSION}" == "9" ]]; then
    if [[ -n "${stream}" ]]; then
      echo "Installing proprietary driver stream ${stream}-dkms (RHEL 9)..."
      sudo dnf module install -y "nvidia-driver:${stream}-dkms"
    else
      echo "Installing proprietary driver stream latest-dkms (RHEL 9)..."
      sudo dnf module install -y "nvidia-driver:latest-dkms"
    fi
  else
    # RHEL 10: no module streams; install the driver package directly.
    # Note: cuda-drivers pulls proprietary modules; we avoid it per original logic.
    echo "Installing proprietary driver package (RHEL 10)..."
    sudo dnf install -y nvidia-driver
  fi
}

IS_BLACKWELL=0
IS_LEGACY=0
IS_PROPRIETARY=0

# 1) Blackwell: open modules
for gpu_spec in "${BLACKWELL_GPUS[@]}"; do
  IFS='|' read -r pci_id gpu_name <<< "$gpu_spec"
  if echo "$DETECTED_GPUS" | grep -q "$pci_id"; then
    echo "Detected Blackwell GPU: $gpu_name ($pci_id)"
    install_open_driver
    IS_BLACKWELL=1
    break
  fi
done

# 2) Legacy GPUs: pinned driver
if [[ "$IS_BLACKWELL" -eq 0 ]]; then
  for gpu_spec in "${LEGACY_GPUS[@]}"; do
    IFS='|' read -r pci_id gpu_name driver_stream <<< "$gpu_spec"
    if echo "$DETECTED_GPUS" | grep -q "$pci_id"; then
      echo "Detected legacy GPU: $gpu_name ($pci_id) -> stream $driver_stream"
      if [[ "${OS_MAJOR_VERSION}" == "9" ]]; then
        install_proprietary_driver "$driver_stream"
      else
        echo "Warning: Legacy driver streams (${driver_stream}) may not be available on RHEL 10. Attempting latest proprietary."
        install_proprietary_driver
      fi
      IS_LEGACY=1
      break
    fi
  done
fi

# 3) Everything else: proprietary latest-dkms (RTX A4000/A5000 fall here).
# FIX #4: unknown GPUs are handled by this same branch instead of aborting.
if [[ "$IS_BLACKWELL" -eq 0 && "$IS_LEGACY" -eq 0 ]]; then
  echo "Detected non-Blackwell, non-legacy GPU (e.g. RTX A4000/A5000)."
  echo "Installing proprietary latest-dkms driver..."
  install_proprietary_driver
  IS_PROPRIETARY=1
fi

#-------------------------------------------------------------------------------
# EXPLICIT DKMS BUILD + VERIFICATION
# do not rely on the package hook alone. Build the module for the
# running kernel and fail loudly if it did not land. This turns a silent
# "nvidia-smi cannot communicate" into an actionable script error.
#-------------------------------------------------------------------------------
CURRENT_KERNEL="$(uname -r)"
echo "Building DKMS module for kernel ${CURRENT_KERNEL}..."
if ! sudo dkms autoinstall -k "${CURRENT_KERNEL}"; then
  echo "ERROR: DKMS build failed for kernel ${CURRENT_KERNEL}." >&2
  echo "Check the build log under /var/lib/dkms/nvidia/*/build/make.log" >&2
  exit 1
fi

if ! dkms status | grep -q "nvidia/.*installed"; then
  echo "ERROR: DKMS reports no installed nvidia module after build." >&2
  echo "Current dkms status:" >&2
  dkms status >&2 || true
  exit 1
fi
echo "DKMS module installed successfully:"
dkms status | grep nvidia || true

#-------------------------------------------------------------------------------
# INSTALL CUDA TOOLKIT
# - Legacy (CC < 7.5): CUDA 12.x (last branch supporting Pascal/Volta)
# - Blackwell / Proprietary (CC >= 7.5): latest cuda-toolkit
# - cuda-drivers is NEVER installed
#-------------------------------------------------------------------------------
if [[ "${DO_INSTALL_CUDA_STACK}" -eq 1 ]]; then
  if [[ "$IS_LEGACY" -eq 1 ]]; then
    # For RHEL 9, CUDA 12.9 is available. On RHEL 10, only newer CUDA is available.
    if [[ "${OS_MAJOR_VERSION}" == "9" ]]; then
      echo "Installing CUDA Toolkit 12.9 (legacy path)..."
      sudo dnf install -y cuda-toolkit-12-9
    else
      echo "Warning: CUDA 12.9 may not be available on RHEL 10. Installing latest cuda-toolkit."
      sudo dnf install -y cuda-toolkit
    fi
  else
    echo "Installing latest available CUDA Toolkit..."
    sudo dnf install -y cuda-toolkit
  fi
fi

#-------------------------------------------------------------------------------
# POST-INSTALL STEPS
#-------------------------------------------------------------------------------
if [[ "${DO_TRY_RMMOD_NOUVEAU}" -eq 1 ]]; then
  sudo rmmod -f nouveau 2>/dev/null || true
fi

if [[ "${DO_USER_GROUPS}" -eq 1 ]]; then
  TARGET_USER="${SUDO_USER:-$USER}"
  sudo usermod -aG video,render "${TARGET_USER}" || true
  echo "User ${TARGET_USER} added to video/render groups."
  REBOOT_REQUIRED=1
fi

if [[ "${DO_BASHRC_CUDA_PATHS}" -eq 1 ]]; then
  TARGET_USER="${SUDO_USER:-$USER}"
  TARGET_HOME="$(getent passwd "${TARGET_USER}" | cut -d: -f6)"
  TARGET_BASHRC="${TARGET_HOME}/.bashrc"
  MARKER="NVIDIA CUDA Paths"

  [[ -f "${TARGET_BASHRC}" ]] || sudo -u "${TARGET_USER}" touch "${TARGET_BASHRC}" || true

  if ! grep -q "${MARKER}" "${TARGET_BASHRC}" 2>/dev/null; then
    cat >> "${TARGET_BASHRC}" <<'EOF'

# NVIDIA CUDA Paths
export PATH="/usr/local/cuda/bin:$PATH"
export LD_LIBRARY_PATH="/usr/local/cuda/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
EOF
    echo "Added CUDA PATH to ${TARGET_BASHRC}"
  fi
fi

#-------------------------------------------------------------------------------
# VERIFICATION
#-------------------------------------------------------------------------------
if [[ "${DO_VERIFY_NVIDIA_SMI}" -eq 1 ]]; then
  echo "Verifying nvidia-smi..."
  nvidia-smi || true
fi

if [[ "${DO_VERIFY_NVCC}" -eq 1 ]]; then
  if command -v nvcc >/dev/null 2>&1; then
    echo "Verifying nvcc..."
    nvcc -V || true
  else
    echo "nvcc not found in PATH yet (re-login/reboot may be needed)"
  fi
fi

#-------------------------------------------------------------------------------
# NVIDIA CONTAINER TOOLKIT
#-------------------------------------------------------------------------------
if [[ "${DO_INSTALL_NVIDIA_CONTAINER_TOOLKIT}" -eq 1 ]]; then
  if command -v docker >/dev/null 2>&1 || command -v podman >/dev/null 2>&1; then
    echo "Container engine detected: installing NVIDIA Container Toolkit..."
    # Add repo (works for RHEL family)
    curl -s -L https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo \
      | sudo tee /etc/yum.repos.d/nvidia-container-toolkit.repo >/dev/null

    sudo dnf install -y nvidia-container-toolkit

    if [[ "${DO_CONFIGURE_DOCKER_RUNTIME}" -eq 1 ]] && command -v docker >/dev/null 2>&1; then
      command -v nvidia-ctk >/dev/null 2>&1 && sudo nvidia-ctk runtime configure --runtime=docker || true
    fi

    if [[ "${DO_RESTART_DOCKER}" -eq 1 ]] && command -v docker >/dev/null 2>&1; then
      sudo systemctl restart docker || true
    fi
  else
    echo "Docker/Podman not found. Skipping container toolkit."
  fi
fi

#-------------------------------------------------------------------------------
# RESTORE DEFAULT CRYPTO POLICY
#-------------------------------------------------------------------------------
echo "Restoring default crypto policy (SHA1 disabled)..."
sudo update-crypto-policies --set DEFAULT

#-------------------------------------------------------------------------------
# FINAL
#-------------------------------------------------------------------------------
echo ""
echo "========================================"
echo "Installation finished"
echo "========================================"

if [[ "${REBOOT_REQUIRED}" -eq 1 ]]; then
  echo ">> REBOOT REQUIRED: sudo reboot"
fi

echo "After reboot, verify with:"
echo "  nvidia-smi"
echo "  nvcc -V"
question_mark
Is there anything I can help you with?
question_mark
AI Assistant ×