Skip to content

Instantly share code, notes, and snippets.

@in03
Last active December 29, 2024 05:51
Show Gist options
  • Save in03/990359840db63d2debc47387f8766c8e to your computer and use it in GitHub Desktop.
Save in03/990359840db63d2debc47387f8766c8e to your computer and use it in GitHub Desktop.
Configures an LXC container with Nvidia GPU passthrough.
#!/bin/bash
# Constants
SCRIPT_NAME="install_nvidia_driver_host.sh"
SCRIPT_PATH="/usr/local/bin/$SCRIPT_NAME"
DRIVER_DIR="/usr/local/share/nvidia" # Directory to store the NVIDIA driver file
KERNEL_HOOK_PATH="/etc/kernel/postinst.d/nvidia-driver-reinstall"
KERNEL_TRACK_FILE="/var/lib/nvidia-driver-installed-kernel"
# Logging function
log() {
local COLOR=""
local RESET="\033[0m"
local DATE=$(date +%Y-%m-%d)
local LOG_FILE="/var/log/nvidia-driver-install.log"
local MESSAGE="$1"
case "$1" in
Success*|*finish*|*done*|*complete*) COLOR="\033[32m" ;; # Green
Error*) COLOR="\033[31m" ;; # Red
Warning*) COLOR="\033[33m" ;; # Orange
Info*) COLOR="\033[36m" ;; # Cyan
Debug*) COLOR="\033[35m" ;; # Magenta
*) COLOR=""
esac
# Print to terminal with color
echo -e "$DATE | ${COLOR}${MESSAGE}${RESET}"
# Log to file without color codes
# echo "$DATE | $MESSAGE" >> "$LOG_FILE"
}
trap 'echo; log "Error: Process killed"; exit 1' SIGTERM
trap 'echo; log "Warning: User exited."; exit 0' SIGINT
# Function to install script
install_self() {
log "Info: Installing script to $SCRIPT_PATH..."
cp "$0" "$SCRIPT_PATH"
chmod +x "$SCRIPT_PATH"
log "Success: Script installed to $SCRIPT_PATH."
log "Info: Installing kernel hook to $KERNEL_HOOK_PATH..."
echo "#!/bin/bash" > "$KERNEL_HOOK_PATH"
echo "$SCRIPT_PATH --silent" >> "$KERNEL_HOOK_PATH"
chmod +x "$KERNEL_HOOK_PATH"
log "Info: Kernel hook successfully installed."
}
# Function to install the NVIDIA driver
install_driver() {
DRIVER_FILE=$(ls "$DRIVER_DIR"/NVIDIA-Linux-*.run 2>/dev/null | head -n 1)
if [[ -z "$DRIVER_FILE" ]]; then
log "Error: NVIDIA driver file not found."
log "Info: Please ensure a driver file is added to $DRIVER_DIR, matching pattern \"NVIDIA-Linux-*.run\""
exit 1
fi
log "Info: Successfully detected NVIDIA driver file: $DRIVER_FILE."
log "Info: Current kernel is $(uname -r)"
log "Info: Installing kernel headers and build tools"
apt update && apt install -y gcc make dkms build-essential pve-headers-"$(uname -r)" linux-source
chmod +x "$DRIVER_FILE"
"${DRIVER_FILE}" --dkms --silent
if [[ $? -eq 0 ]]; then
log "Info: NVIDIA driver installed successfully."
echo "$CURRENT_KERNEL" > "$KERNEL_TRACK_FILE"
else
log "Error: NVIDIA driver installation failed."
exit 1
fi
}
# Check for silent mode
SILENT=false
if [[ "$1" == "--silent" ]]; then
SILENT=true
fi
# Detect interactive mode
if ! $SILENT; then
echo "
██████╗ ██╗ ██╗███████╗ ███╗ ██╗██╗ ██╗ ███╗ ██████╗ ████████╗██████╗
██╔══██╗██║ ██║██╔════╝ ████╗ ██║██║ ██║██╔██╗██╔══██╗╚══██╔══╝██╔══██╗
██████╔╝██║ ██║█████╗ ██╔██╗ ██║██║ ██║╚═╝╚═╝██║ ██║ ██║ ██████╔╝
██╔═══╝ ╚██╗ ██╔╝██╔══╝ ██║╚██╗██║╚██╗ ██╔╝ ██║ ██║ ██║ ██╔══██╗
██║ ╚████╔╝ ███████╗ ██║ ╚████║ ╚████╔╝ ██████╔╝ ██║ ██║ ██║
╚═╝ ╚═══╝ ╚══════╝ ╚═╝ ╚═══╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝
Proxmox Virtual Environment - Nvidia Updater.
"
log "Info: Running in interactive mode."
# Prompt for installation
echo "------------"
read -p "Do you want to install this script and enable it as a post-kernel update hook? (y/n): " INSTALL
if [[ "$INSTALL" =~ ^[Yy]$ ]]; then
install_self
else
log "Warning: Installation skipped. Run manually if needed."
exit 0
fi
else
log "Info: Running in silent mode."
fi
# Check current kernel version
CURRENT_KERNEL=$(uname -r)
log "Info: Current kernel is $CURRENT_KERNEL."
# Check if the driver is already installed for this kernel
if [[ -f "$KERNEL_TRACK_FILE" ]]; then
LAST_KERNEL=$(cat "$KERNEL_TRACK_FILE")
if [[ "$CURRENT_KERNEL" == "$LAST_KERNEL" ]]; then
log "Info: NVIDIA driver already installed for kernel $CURRENT_KERNEL. Nothing to be done."
exit 0
fi
fi
# Install the NVIDIA driver (store and reference driver file)
log "Info: Installing NVIDIA driver for kernel $CURRENT_KERNEL..."
install_driver
log "Info: NVIDIA driver installation complete."
# Optional reboot prompt (interactive mode only)
if ! $SILENT; then
echo "---"
read -p "Do you want to reboot now? (y/n): " REBOOT
if [[ "$REBOOT" =~ ^[Yy]$ ]]; then
log "Info: Rebooting system..."
reboot
else
log "Warning: Reboot skipped. Please reboot manually to complete the installation."
fi
fi
#!/bin/bash
# Check if whiptail is installed
if ! command -v whiptail &> /dev/null; then
echo "whiptail is required but not installed. Installing whiptail..."
apt update && apt install -y whiptail
fi
# List all LXC containers
LXC_CONTAINERS=$(pct list | awk 'NR>1 {print $1 " " $2}')
if [ -z "$LXC_CONTAINERS" ]; then
echo "No LXC containers found."
exit 1
fi
# Prompt the user to select an LXC container
LXC_ID=$(whiptail --title "Select LXC Container" --menu "Choose the LXC container to install the NVIDIA driver:" 20 78 10 $(echo "$LXC_CONTAINERS") 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
echo "No LXC container selected. Exiting..."
exit 1
fi
# Detect the NVIDIA driver file
DRIVER_FILE=$(ls NVIDIA-Linux-*.run | head -n 1)
if [ ! -f "$DRIVER_FILE" ]; then
echo "NVIDIA driver file not found in the current directory."
exit 1
fi
echo "Detected NVIDIA driver: $DRIVER_FILE"
# Parse the major device numbers for NVIDIA devices
echo "Fetching NVIDIA device numbers from /dev/nvidia*..."
DEVICES_OUTPUT=$(ls -al /dev/nvidia*)
if [ -z "$DEVICES_OUTPUT" ]; then
echo "NVIDIA devices not found. Ensure your GPU is detected."
exit 1
fi
# Extract the major numbers
MAJOR_1=$(echo "$DEVICES_OUTPUT" | grep -m1 '/dev/nvidia0' | awk '{print $5}' | cut -d, -f1)
MAJOR_2=$(echo "$DEVICES_OUTPUT" | grep -m1 '/dev/nvidia-uvm' | awk '{print $5}' | cut -d, -f1)
if [ -z "$MAJOR_1" ] || [ -z "$MAJOR_2" ]; then
echo "Failed to detect major device numbers. Exiting..."
exit 1
fi
echo "Detected major numbers: 195=$MAJOR_1, 507=$MAJOR_2"
# Inject the necessary device configurations into the LXC configuration
LXC_CONF_PATH="/etc/pve/lxc/$LXC_ID.conf"
echo "Updating LXC container configuration at $LXC_CONF_PATH..."
# Add the required lines for NVIDIA GPU passthrough
cat <<EOL >> "$LXC_CONF_PATH"
# NVIDIA GPU Passthrough
lxc.cgroup2.devices.allow: c $MAJOR_1:* rwm
lxc.cgroup2.devices.allow: c $MAJOR_2:* rwm
lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file
lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file
lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file
lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file
EOL
echo "LXC container configuration updated with NVIDIA passthrough."
# Copy NVIDIA driver to LXC container
echo "Copying NVIDIA driver to LXC container $LXC_ID..."
pct push "$LXC_ID" "$DRIVER_FILE" "/root/nvidia-driver.run"
# Install the NVIDIA driver in the LXC container
echo "Installing the NVIDIA driver in the LXC container..."
pct exec "$LXC_ID" -- bash -c "
chmod +x /root/nvidia-driver.run &&
/root/nvidia-driver.run --silent &&
rm /root/nvidia-driver.run
"
# Restart the LXC container to apply changes
echo "Restarting the LXC container $LXC_ID..."
pct stop "$LXC_ID" && pct start "$LXC_ID"
echo "NVIDIA driver installation and passthrough configuration completed in LXC container $LXC_ID."
@in03
Copy link
Author

in03 commented Oct 20, 2024

Largely the work of some "prompt engineering" after some labourious frustrations with doing this manually one too many times.
Haven't tested it yet, but the idea is here. Will come back and tweak as needed.

Credit to the original manual guide I followed:
https://yomis.blog/nvidia-gpu-in-proxmox-lxc/
Thanks Yomi Ikuru.

@in03
Copy link
Author

in03 commented Oct 20, 2024

Added host install script too for good measure.

@in03
Copy link
Author

in03 commented Oct 21, 2024

  • Removed steps
  • Renamed weird MAJOR_195, etc. variables
  • Removed gcc and make as deps from non dkms install, since not required to build

@in03
Copy link
Author

in03 commented Dec 29, 2024

A little bit of a WIP.

  • Host installation script now hooks post-kernel-installation.
  • No real testing done to confirm. Running interactively should work fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment