Last active
December 29, 2024 05:51
-
-
Save in03/990359840db63d2debc47387f8766c8e to your computer and use it in GitHub Desktop.
Configures an LXC container with Nvidia GPU passthrough.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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." |
Added host install script too for good measure.
- Removed steps
- Renamed weird
MAJOR_195
, etc. variables - Removed
gcc
andmake
as deps from non dkms install, since not required to build
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
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.