Skip to content

Instantly share code, notes, and snippets.

@siniradam
Created June 9, 2025 18:10
Show Gist options
  • Save siniradam/89b008ea24edfa6059775ab449c8bd3f to your computer and use it in GitHub Desktop.
Save siniradam/89b008ea24edfa6059775ab449c8bd3f to your computer and use it in GitHub Desktop.
Proxmox disk passthrough script.
#!/usr/bin/env bash
# Copyright (c) 2025
# Author: Atlas (siniradam)
# License: MIT
# https://gist.github.com/siniradam
# This package, is for passthrough of a disk to a VM on Proxmox.
# You can use this script to passthrough a disk to a VM just by answering a few questions.
# This script is intended to be run on the Proxmox host.
# It will not work on a VM.
YELLOW='\033[1;33m'
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# - Ask user to if they want to continue
echo ""
echo -e "${YELLOW}This script will passthrough a disk to a VM on Proxmox.${NC}"
read -p "Do you want to continue? (y/n) " -n 1 -r
echo # (optional) move to a new line
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
# - Check if lswh is installed if not install it.
if ! command -v lshw &> /dev/null
then
echo -e "${BLUE}lshw could not be found, installing it now.${NC}"
apt-get update && apt-get install -y lshw
fi
# Find the boot disk (the root filesystem)
BOOT_DISK=$(lsblk -no PKNAME $(findmnt -n -o SOURCE /) | head -n1)
# List all non-boot disks with their serials
DISK_LIST=()
DISK_INFO=()
i=1
while IFS= read -r line; do
# Extract disk path (e.g., ../../sdb or ../../nvme0n1p1)
disk_symlink=$(echo "$line" | awk '{print $9}')
real_device=$(readlink -f "$disk_symlink")
disk_name=$(basename "$real_device")
# Remove partition suffix (e.g., sda1 -> sda, nvme0n1p1 -> nvme0n1)
base_disk_name=$(echo "$disk_name" | sed -E 's/([a-z]+)[0-9]+$/\1/; s/(nvme[0-9]+n[0-9]+)p[0-9]+$/\1/')
# Exclude boot disk
if [[ "$base_disk_name" == "$BOOT_DISK" ]]; then
continue
fi
# Extract serial from by-id path (ata- or nvme-)
serial=$(echo "$line" | grep -oP '(ata|nvme)-[^ ]+' || echo "N/A")
# Get disk size in human-readable format
size=$(lsblk -dn -o SIZE "/dev/$base_disk_name" 2>/dev/null | head -n1)
DISK_LIST+=("$base_disk_name")
DISK_INFO+=("$i) $base_disk_name (Size: $size, Serial: $serial)")
((i++))
done < <(find /dev/disk/by-id/ -type l | grep -E 'ata-|nvme-' | xargs -I{} ls -l {} | grep -v -E '[0-9]$' | sort -k11)
echo ""
if [ ${#DISK_LIST[@]} -eq 0 ]; then
echo -e "${RED}No disks found for passthrough (other than boot disk).${NC}"
exit 1
elif [ ${#DISK_LIST[@]} -eq 1 ]; then
echo "Only one disk available for passthrough: ${DISK_LIST[0]}"
exit 1
fi
echo ""
echo "Available disks for passthrough:"
for info in "${DISK_INFO[@]}"; do
echo "$info"
done
echo ""
read -p "Enter the number of the disk to passthrough: " disk_choice
PASSTHROUGH_DISK="${DISK_LIST[$((disk_choice-1))]}"
# Ask for VM or container ID
echo ""
echo "Here are the VMs and Containers on the system:"
qm list
pct list
echo ""
read -p "Enter the VM or Container ID you want to passthrough the disk to: " VMID
if ! qm status $VMID &> /dev/null && ! pct status $VMID &> /dev/null; then
echo "VM or Container ID $VMID does not exist. Exiting."
exit 1
fi
# Confirm
COMMAND="qm set $VMID -scsi2 /dev/disk/by-id/$serial"
echo ""
echo -e "You are about to passthrough ${YELLOW}/dev/$PASSTHROUGH_DISK ${NC}to VM/CT ${YELLOW}$VMID${NC}."
echo -e "${RED}The following command will be executed to passthrough the disk:${NC}"
echo -e "${BLUE}$COMMAND${NC}"
echo ""
# Ask for confirmation
read -p "Are you sure? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo -e "${RED}Operation cancelled.${NC}"
exit 1
fi
# Confirm
# Print the command in yellow
# Execute the passthrough command
echo -e "${BLUE}Executing command...${NC}"
# $COMMAND
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment