Danger: The original guide contains a huge script to achieve the same thing as the following:
- With Qube Manager:
- select all the qubes for which you want to change the net qube
- open the context menu (i.e.: right click)
- In Network, select the new net qube
- Or, in a terminal in dom0: replace
OLD_NET_QUBE_NAMEandNEW_NET_QUBE_NAMEwith the desired value in something like this:for qube in $( qvm-ls --raw-list --netvm-is OLD_NET_QUBE_NAME ); do qvm-prefs "$qube" netvm NEW_NET_QUBE_NAME; done
That’s all! Presented here, for reference, the original guide is a very good example of bad practices leading to potential security issues: it relies on the user copying and running a huge script in dom0, just to achieve something very easy.
It could be very tempting for an attacker to change a few lines (in more than 300 lines) and create a malicious script, as this guide is a wiki post on the forum. Also, experienced users probably would not use this script nor review it either, so only beginners might be tempted to use it, and miss a malicious change.
Original guide (LLM generated?)
PURPOSE
This script automates the replacement of netvm configurations across multiple appvms and templates. This is essential when a network VM becomes corrupted and needs to be replaced, since you cannot delete a netvm that is still assigned to other VMs.
Netvms can break, and for repairs this can be extremely work-intensive if dozens or hundreds of appvms, dvms, standalonem disposals, or named disposals use the same netvm.
For example, it’s common for sys-whonix and sys-firewall to grant programs, and these are configured to be used by multiple machines in Qubes.
Maybe you want to create an improved sys-firewall or sys-whonix, or a custom one, and then replace it in bulk—doing the work manually is absurdly time-consuming and slow!
I solved this time problem for a broken sys-whonix across multiple VMs using this script and method.
In 3 minutes it was fixed, but if I had to maintain and remove sys-whonix as a netvm for dozens of appvms, it would take more than 1 hour.
USE CASE EXAMPLE
sys-whonixbecomes problematic/broken.- 40+ appvms have
sys-whonixconfigured as their netvm. - You cannot delete
sys-whonixuntil all references are removed manually. - The manual process would take hours.
- THIS SCRIPT solves it by automating the replacement in seconds.
RECOMMENDED WORKFLOW
- Create a temporary netvm (e.g.,
sys-whonix-temporary). - Run the script to replace the old netvm → temporary netvm.
- Delete the broken original netvm (now no longer referenced).
- Create/rebuild the repaired netvm.
- Run the script again to replace temporary → repaired netvm.
- Done! All VMs now use the working netvm.
OPTIONS
- APPVMS — Scan and modify network VM settings in all appvms.
- TEMPLATES — Scan and modify network VM settings in all templates. For some reasons, for tests, the user can set netvms for some templates.
- HELP — Display this help information.
How to Use
Copy netvm-mass-replacement.sh script to dom0.
For example, place it in your appvm vault at /home/your-user/. Inside the appvm vault, it will be located at this path:
/home/your-user/netvm-mass-replacement.sh
On dom0, run:
qvm-run --pass-io vault 'cat "/home/your-user/netvm-mass-replacement.sh"' > /home/your-user/netvm-mass-replacement.sh;
On dom0, run:
chmod +x netvm-mass-replacement.sh
Execute:
./netvm-mass-replacement.sh
netvm-mass-replacement.sh
#!/bin/bash
###############################################################################
# Qubes OS Mass NetVM Replacement Script
# Purpose: Replace netvm configuration across multiple appvms/templates
###############################################################################
#set -euo pipefail
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
show_help() {
cat << 'EOF'
Qubes OS Mass NetVM Replacement Script
======================================
PURPOSE:
This script automates the replacement of netvm configurations across multiple
appvms and templates. This is essential when a network VM becomes corrupted
and needs to be replaced, as you cannot delete a netvm that is still assigned
to other VMs.
USE CASE EXAMPLE:
1. sys-whonix becomes problematic/broken
2. 40+ appvms have sys-whonix configured as their netvm
3. Cannot delete sys-whonix until all references are removed manually
4. Manual process would take hours
5. THIS SCRIPT solves this by automating the replacement in seconds
RECOMMENDED WORKFLOW:
1. Create a temporary netvm (e.g., sys-whonix-temporary)
2. Run script to replace old netvm → temporary netvm
3. Delete the broken original netvm (now no longer referenced)
4. Create/rebuild the repaired netvm
5. Run script again to replace temporary → repaired netvm
6. Done! All VMs now use the working netvm
OPTIONS:
1. APPVMS - Scan and modify network VM settings in all appvms
2. TEMPLATES - Scan and modify network VM settings in all templates
3. HELP - Display this help information
EOF
}
check_permissions() {
if ! command -v qvm-prefs &> /dev/null; then
log_error "Qubes OS tools (qvm-prefs) not found. Are you running inside Qubes?"
exit 1
fi
}
get_netvm() {
local vm_name="$1"
qvm-prefs "$vm_name" netvm 2>/dev/null || echo ""
}
set_netvm() {
local vm_name="$1"
local new_netvm="$2"
if qvm-prefs --set "$vm_name" netvm "$new_netvm"; then
log_info "✓ Updated '$vm_name': netvm changed to '$new_netvm'"
return 0
else
log_error "✗ Failed to update '$vm_name'"
return 1
fi
}
validate_vm_name() {
local name="$1"
if [[ -z "$name" ]]; then
return 1
fi
if [[ ! "$name" =~ ^[a-zA-Z][a-zA-Z0-9_-]*$ ]]; then
return 1
fi
return 0
}
scan_appvms() {
local target_netvm="$1"
local new_netvm="$2"
local count=0
local failures=0
local checked=0
log_info "Scanning appvms from /var/lib/qubes/appvms..."
log_info "Target netvm to replace: '$target_netvm'"
log_info "New netvm to assign: '$new_netvm'"
echo ""
local appvm_dir="/var/lib/qubes/appvms"
if [[ ! -d "$appvm_dir" ]]; then
log_error "Appvms directory not found: $appvm_dir"
return 1
fi
log_info "Starting full scan of all appvms..."
echo "----------------------------------------"
for vm_dir in "$appvm_dir"/*; do
[[ -d "$vm_dir" ]] || continue
local vm_name
vm_name=$(basename "$vm_dir")
((checked++))
log_info "Checking VM #$checked: '$vm_name'..."
local current_netvm
current_netvm=$(get_netvm "$vm_name")
if [[ -z "$current_netvm" ]]; then
log_warn " → No netvm configured (or inaccessible)"
continue
elif [[ "$current_netvm" == "None" ]]; then
log_warn " → No netvm set (None)"
continue
elif [[ "$current_netvm" == "$target_netvm" ]]; then
log_info " → MATCH FOUND! Current netvm='$current_netvm'"
if set_netvm "$vm_name" "$new_netvm"; then
((count++))
else
((failures++))
fi
else
log_info " → Skipping (current netvm='$current_netvm', not matching '$target_netvm')"
fi
done
echo "----------------------------------------"
log_info "=== SCAN COMPLETE ==="
log_info "Total VMs checked: $checked"
log_info "VMs updated: $count"
if [[ $failures -gt 0 ]]; then
log_warn "Failures: $failures"
fi
if [[ $count -eq 0 && $checked -gt 0 ]]; then
log_warn "No VMs had the target netvm ('$target_netvm') configured."
fi
return 0
}
scan_templates() {
local target_netvm="$1"
local new_netvm="$2"
local count=0
local failures=0
local checked=0
log_info "Scanning templates from /var/lib/qubes/vm-templates..."
log_info "Target netvm to replace: '$target_netvm'"
log_info "New netvm to assign: '$new_netvm'"
echo ""
local templates_dir="/var/lib/qubes/vm-templates"
if [[ ! -d "$templates_dir" ]]; then
log_error "Templates directory not found: $templates_dir"
return 1
fi
log_info "Starting full scan of all templates..."
echo "----------------------------------------"
for vm_dir in "$templates_dir"/*; do
[[ -d "$vm_dir" ]] || continue
local vm_name
vm_name=$(basename "$vm_dir")
((checked++))
log_info "Checking template #$checked: '$vm_name'..."
local current_netvm
current_netvm=$(get_netvm "$vm_name")
if [[ -z "$current_netvm" ]]; then
log_warn " → No netvm configured (or inaccessible)"
continue
elif [[ "$current_netvm" == "None" ]]; then
log_warn " → No netvm set (None)"
continue
elif [[ "$current_netvm" == "$target_netvm" ]]; then
log_info " → MATCH FOUND! Current netvm='$current_netvm'"
if set_netvm "$vm_name" "$new_netvm"; then
((count++))
else
((failures++))
fi
else
log_info " → Skipping (current netvm='$current_netvm', not matching '$target_netvm')"
fi
done
echo "----------------------------------------"
log_info "=== SCAN COMPLETE ==="
log_info "Total templates checked: $checked"
log_info "Templates updated: $count"
if [[ $failures -gt 0 ]]; then
log_warn "Failures: $failures"
fi
if [[ $count -eq 0 && $checked -gt 0 ]]; then
log_warn "No templates had the target netvm ('$target_netvm') configured."
fi
return 0
}
main_menu() {
echo ""
echo "=========================================="
echo " Qubes NetVM Mass Replacement Tool"
echo "=========================================="
echo " Select operation:"
echo " 1) Scan and modify APPVMS"
echo " 2) Scan and modify TEMPLATES"
echo " 3) Show HELP/USAGE information"
echo " 4) Exit"
echo "=========================================="
echo -n " Enter choice [1-4]: "
}
run_script() {
check_permissions
local choice=""
while true; do
main_menu
read -r choice
case "$choice" in
1)
echo ""
log_info "=== APPVMS MODE ==="
echo -n " Enter the netvm NAME TO REPLACE (e.g., sys-whonix): "
read -r netvm_to_change
if ! validate_vm_name "$netvm_to_change"; then
log_error "Invalid VM name format. Exiting."
exit 1
fi
echo -n " Enter the NEW netvm NAME (e.g., sys-whonix-temporary): "
read -r new_netvm
if ! validate_vm_name "$new_netvm"; then
log_error "Invalid VM name format. Exiting."
exit 1
fi
echo ""
log_warn "WARNING: You are about to change netvm for ALL appvms"
log_warn "that currently use '$netvm_to_change' to '$new_netvm'"
echo -n " Confirm? (yes/no): "
read -r confirm
if [[ "$confirm" == "yes" ]]; then
scan_appvms "$netvm_to_change" "$new_netvm"
else
log_info "Operation cancelled."
fi
;;
2)
echo ""
log_info "=== TEMPLATES MODE ==="
echo -n " Enter the netvm NAME TO REPLACE (e.g., sys-firewall): "
read -r netvm_to_change
if ! validate_vm_name "$netvm_to_change"; then
log_error "Invalid VM name format. Exiting."
exit 1
fi
echo -n " Enter the NEW netvm NAME (e.g., sys-firewall-new): "
read -r new_netvm
if ! validate_vm_name "$new_netvm"; then
log_error "Invalid VM name format. Exiting."
exit 1
fi
echo ""
log_warn "WARNING: You are about to change netvm for ALL templates"
log_warn "that currently use '$netvm_to_change' to '$new_netvm'"
echo ""
log_warn "Note: Templates are typically offline - changes apply next boot"
echo -n " Confirm? (yes/no): "
read -r confirm
if [[ "$confirm" == "yes" ]]; then
scan_templates "$netvm_to_change" "$new_netvm"
else
log_info "Operation cancelled."
fi
;;
3)
show_help
;;
4)
log_info "Exiting. Stay safe!"
exit 0
;;
*)
log_error "Invalid choice. Please enter 1-4."
;;
esac
echo ""
echo "Press Enter to continue..."
read -r
done
}
run_script
QUESTION
Does Qubes OS have any factory program or script that does exactly this?
Since netvms break and this is very common, and it’s necessary to fix it quickly with good usability, having an official tool would be great.