BashVM — Documentation

Table of Contents

Overview

BashVM is an interactive, text-menu wrapper around the virsh (libvirt) command-line tools. Instead of typing long virsh, virt-clone, ufw, and iptables invocations, you pick a numbered item from a menu and answer a the prompts.

Running the Script

On a fresh host, run the 12.1 Installer first:

chmod +x bashvm-installer.sh sudo ./bashvm-installer.sh

Once the installer finishes, run the main script:

# make it executable, then run as root chmod +x bashvm.sh sudo ./bashvm.sh

On launch you are shown the main menu:

============================ Main Menu ============================ 1. Virtual Machines 2. Storage Pools 3. Networks 4. Snapshots 5. Edit Properties 6. Firewall Settings 7. Port Forwarding 8. VNC / Console Access 9. System Monitor 10. VM Monitor 11. SQLite Editor q. Exit Enter your choice:

Type the number (or letter) of the area you want and press Enter. The helper scripts (e.g. the 1.1 Create a new / existing VM option) are expected to live in the same working directory as the main script or somewhere on your PATH.

Top-level menu choices
KeySectionPurpose
1Virtual MachinesShow, start, reboot, shut down, clone, rename, delete, console into VMs
2Storage PoolsManage libvirt storage pools and volumes; download ISOs
3NetworksCreate NAT / macvtap / routed networks; DHCP reservations
4SnapshotsList, create, delete, revert VM snapshots
5Edit PropertiesOpen the raw XML editor for VMs, pools, networks, snapshots
6Firewall Settingsufw status, allow/deny ports and ranges, delete rules
7Port ForwardingNAT port-forward rules via the libvirt qemu hook
8VNC / Console AccessShow assigned VNC ports; add/remove VNC with password
9System MonitorLaunches btop
10VM MonitorPer-VM vCPU, memory, disk and network usage
11SQLite EditorEdit the BashVM database
q—Exit the script

1. Virtual Machines

============================== Manage Virtual Machine ============================== s. Show all virtual machines 1. Show more details of a VM 2. Start a VM 3. Reboot a VM 4. Shutdown a VM (graceful) 5. Shutdown a VM (force) 6. Enable autostart of a VM 7. Disable autostart of a VM 8. Create a new / existing VM 9. Undefine a VM 10. Create a new VM (Automated) 11. Console into a VM 12. Change resources of a VM 13. Delete a VM 14. Clone a VM 15. Clone multiple VM's 16. Rename a VM q. Back to main menu
Virtual Machine sub-menu options
KeyActionUnderlying command / scriptPrompts
s List all VMs, running and shut off virsh list --all none
1 Show detailed info + block-device info for one VM virsh dominfo, virsh domblkinfo --all --human VM name
2 Start a VM virsh start VM name
3 Reboot a running VM (ACPI) virsh reboot VM name
4 Graceful shutdown (ACPI signal to guest) virsh shutdown VM name
5 Forced shutdown — pulls the virtual power cord virsh destroy VM name
6 Enable autostart at host boot virsh autostart VM name
7 Disable autostart virsh autostart --disable VM name
8 Guided creation of a new VM (or import an existing disk) 1.1 Create a new / existing VM Name, memory, vCPUs, ISO choice, disk, network, MAC
9 Undefine (unregister) a VM. Refuses if the VM is running; destroys it first if shut off. virsh destroy, virsh undefine VM name
10 Automated cloud-image VM creation with DHCP reservation and port forwarding 1.2 Create a new VM (Automated) Hostname, memory, vCPUs, disk, user, password, OS
11 Attach to the VM's serial console (exit with Ctrl+]) virsh console VM name
12 Change vCPU, memory, disk, and network attachments 1.3 Change resources of a VM Sub-menu — see script reference
13 Delete a VM, its disks, snapshots, DHCP reservation, DB entry, and forwarded ports 1.4 Delete a VM VM name, type DELETE to confirm
14 Clone a single VM to a new name, auto-generating disk names virt-clone --original NAME --name NEW --auto-clone Source VM, new VM name
15 Clone one source VM multiple times; names are auto-assigned by virt-clone virt-clone --original NAME --auto-clone in a loop Source VM, number of clones
16 Rename an existing VM definition virsh domrename Current name, new name
qReturn to main menu—none

Example — start a VM and confirm

Enter your choice: 2 Enter the name of the virtual machine to start: web-server Domain 'web-server' started

Example — clone a VM

Enter your choice: 14 Enter the name of the VM to clone (e.g., test-vm): template-debian Enter the name for the new cloned VM: web-server Allocating 'template-debian-clone.qcow2' [100%] Clone 'web-server' created successfully.

1.1 Create a new / existing VM (menu 1 → 8)

Interactive VM creation from an installation ISO, or import of an existing disk. Builds a complete libvirt XML definition with Q35 machine type, host-passthrough CPU, virtio disk, and a VNC listener on 0.0.0.0, then defines the domain.

Prompts

PromptNotes
Name for the new / existing VMUsed for the XML filename in /etc/libvirt/qemu/
Memory in MBe.g. 1024 for 1 GiB
Number of vCPUse.g. 2
ISO choice If iso.bashvm.com is reachable, you get five curated ISOs: Debian 13.2, Ubuntu 24.04, AlmaLinux 10.1, OpenMediaVault 7.4, TrueNAS SCALE 25.10. Press Enter with no choice to use your own ISO or none.
Windows based OS? (y/n)Only asked when using your own ISO. Switches disk bus to SATA and NIC model to e1000.
Pool containing the ISOOnly asked when using your own ISO. Lists volumes in the pool first, then asks for the full ISO path.
Create a new disk volume? (y/n) If yes: volume name, capacity (e.g. 10GB), pool — runs virsh vol-create-as … --format qcow2.
If no: lists pool volumes and asks for a full disk path.
Network nameDefault default
MAC addressLeave empty to auto-generate a random 52:54:xx:xx:xx:xx MAC.

What it does

1.2 Create a new VM (Automated) (menu 1 → 10)

Fully automated VM deployment using a cloud image and cloud-init. In one pass it downloads the base image, writes a cloud-init user-data file, runs virt-install, waits for the VM's MAC, reserves a DHCP IP, and installs port-forwarding rules into the qemu hook.

Prompts

PromptNotes
Hostname of the VMUsed for the domain name and cloud-init hostname
Memory in MBe.g. 1024
Number of vCPUse.g. 2
Disk space in GBe.g. 50
Username for the new VMCreated with passwordless sudo
Password for the new VMStored as plain_text_passwd in cloud-init and printed at the end
OS choice 1 = Debian 13, 2 = Ubuntu 24.04, 3 = AlmaLinux 10, 4 = openSUSE Leap 15.6

What it does

  1. Writes bashvm-cloudinit.yaml with the chosen user, password, SSH password auth, qemu-guest-agent package, and a runcmd: shutdown -P 0 so the VM powers off after first boot (ready for the hook).
  2. Moves the cloud-init file to /var/lib/libvirt/images.
  3. Downloads the cloud image to /var/lib/libvirt/images if not already present.
  4. Ensures the default network is active and autostarted.
  5. Runs virt-install --disk=size=…,backing_store=… --cloud-init user-data=… --network bridge=virbr0 --noautoconsole.
  6. Waits for a virtio MAC to appear, then generates a unique IPv4 in 192.168.122.x (excluding .0, .1, .255) and a port range starting at 49152, stored in bashvm.db.
  7. Adds a DHCP reservation to the default network.
  8. Appends iptables DNAT rules between #vm_name# and ###vm_name### markers in /etc/libvirt/hooks/qemu (SSH plus the port range, TCP and UDP).
  9. Restarts libvirtd if it was the only running VM (so the hook loads cleanly).
  10. Prints the assigned IPv4, SSH port, port range, username, and password.

1.3 Change resources of a VM (menu 1 → 12)

A second-level menu for adjusting an existing VM's vCPU count, memory, disk size, and network attachments. Most operations require the VM to be shut off.

=================================== Manage Resources =================================== s. Show resources of a VM 1. Add disk space to a VM 2. Shrink disk space of a VM 3. Change the number of vcpu in a VM 4. Change the memory of a VM 5. Attach a disk to a VM 6. Detach a disk from a VM 7. Attach Network to a VM 8. Detach Network from a VM q. Back to main menu
Resource management options
KeyActionCommand / behaviour
s Show resources virsh dominfo, virsh domfsinfo, virsh domblkinfo --all --human
1 Grow a disk virsh vol-resize <vm>.qcow2 <size> --pool <pool> — requires VM shut off. Uses the VM name + .qcow2 as the volume name.
2 Shrink a disk Same as above with --shrink. Destructive — ensure the filesystem inside is small enough first.
3 Change vCPU count Sets maximum then current count with setvcpus --config. Requires VM shut off.
4 Change memory setmaxmem --current then setmem --current. Requires VM shut off.
5 Attach a disk virsh attach-disk <vm> <path> <target> --current --subdriver qcow2
6 Detach a disk virsh detach-disk <vm> <path> --current
7 Attach a network Builds a temporary interface XML (e1000 model, type='network') and calls virsh attach-device --config.
8 Detach a network Same temporary XML, but with virsh detach-device --config.
qReturn to VM menuNote: returns to the main menu in the current script flow.

1.4 Delete a VM (menu 1 → 13)

Deletes a VM and everything attached to it: DHCP reservation, CD-ROM attachment, all storage volumes, snapshot metadata, SQLite port-forwarding record, and the iptables block in the qemu hook.

Prompts

What it does

  1. Refuses to run if the VM is running.
  2. Removes the DHCP reservation by matching the VM's MAC and IP in virsh net-dumpxml.
  3. Detaches any CD-ROM device (looks for cdrom followed by source file in the XML).
  4. Runs virsh undefine <vm> --remove-all-storage --snapshots-metadata — this deletes the qcow2 disks.
  5. Removes the block between #<vm># and ###<vm>### in /etc/libvirt/hooks/qemu.
  6. Deletes the VM's row from the network's table in bashvm.db if present.

2. Storage Pools

============================== Manage Storage Pool ============================== s. Show all storage pools 1. Show all volumes in a pool 2. Activate a storage pool 3. Deactivate a storage pool 4. Create a storage pool 5. Delete a storage pool 6. Create a storage volume 7. Delete a storage volume 8. Clone a storage volume 9. Download a iso / file q. Back to main menu
Storage pool sub-menu options
KeyActionNotes
svirsh pool-list --detailsShows state, autostart, persistence, capacity, allocation, available.
1List volumes in a poolvirsh vol-list --pool <pool>. Pool must exist.
2Activate (start) a poolvirsh pool-start
3Deactivate (destroy) a poolvirsh pool-destroy — the definition is kept.
4Create a new poolPrompts for name, type (dir, logical, fs, …) and target path/source. Runs pool-define-as, pool-start, pool-autostart.
5Delete a poolpool-destroy then pool-undefine. Aborts if the pool does not exist.
6Create a qcow2 volumePrompts for pool, volume name and capacity (e.g. 10G). The .qcow2 extension is appended automatically.
7Delete a volumevirsh vol-delete --pool <pool> <name>.qcow2
8Clone a volumevirsh vol-clone <src>.qcow2 <dst>.qcow2 --pool <pool>
9Download an ISO/file into a poolPrompts for URL and pool; derives the filename from the URL; skips if the file already exists; wget --tries=2 --timeout=10.
qReturn to main menu

Example — create a 20 GB volume in the default pool

Enter your choice: 6 Enter the name of the storage pool to use [default]: Enter the name of the new storage volume (e.g., new-vm): web-server Enter the size of the volume (e.g., 10G): 20G Volume 'web-server.qcow2' created

Example — download an ISO

Enter your choice: 9 Enter the full URL to download from (e.g., https://example/image.iso): https://example.org/debian-12.iso Enter the storage pool name that the file will download to [default]: ...wget output... You may need to stop and start the pool to see the new image within bashvm You can check to see if the file is there at /var/lib/libvirt/images

3. Networks

============================================== Manage Network ============================================== s. Show all networks 1. Show more details of a network 2. Start a network 3. Stop a network 4. Create a NAT network 5. Create a macvtap network 6. Create a routed IPv4 network 7. Create a routed IPv6 network 8. Delete a network 9. Add a IPv4 reservation 10. Remove a IPv4 reservation 11. Add DHCPv6 to a NAT network 12. Add a IPv6 reservation 13. Edit a network q. Back to main menu
Network sub-menu options
KeyActionPrompts / Behaviour
svirsh net-list --allLists every defined network with state and autostart.
1Network info + current DHCP leasesnet-info, net-dhcp-leases
2Start a network and mark it autostartnet-start, net-autostart
3Stop a networknet-destroy
4Create an isolated NAT networkName, bridge name, gateway IP, netmask, DHCP start, DHCP end. XML written to /etc/libvirt/qemu/networks/<name>.xml, then defined, started and autostarted.
5Create a macvtap (bridge-forward) networkNetwork name + physical interface (e.g. eth0). VMs appear directly on the physical LAN.
6Create a routed IPv4 networkName, bridge, gateway IPv4, netmask. No NAT, no DHCP — static configuration only. Also attempts to enable net.ipv4.ip_forward.
7Create a routed IPv6 network3.3 Create a routed IPv6 network
8Delete a network (optionally drop its SQLite table)3.1 Delete a network
9Add a static DHCPv4 reservationVM name, MAC, IP, network. Uses virsh net-update … add ip-dhcp-host … --live --config.
10Remove a DHCPv4 reservationLooks up MAC and IP from the network XML by VM name, then deletes the host entry.
11Add DHCPv6 service to a NAT network3.2 Add DHCPv6 to a NAT network
12Add a DHCPv6 reservationDesired IPv6 address, network name, then the VM's DUID (listed with net-dhcp-leases).
13Edit a network's XMLvirsh net-edit <name>
qReturn to main menu

Example — create a NAT network

Enter your choice: 4 Enter the new network name (e.g., natbr0): natbr0 Enter the virtual bridge name (e.g., natbr0): natbr0 Enter the new gateway ip address (e.g., 192.168.100.1): 192.168.100.1 Enter the new subnet mask (e.g., 255.255.255.0): 255.255.255.0 Enter the starting ip for the DHCP range (e.g., 192.168.100.2): 192.168.100.2 Enter the ending ip for the DHCP range (e.g., 192.168.100.254): 192.168.100.254 Network natbr0 defined from /etc/libvirt/qemu/networks/natbr0.xml Network natbr0 started Network natbr0 marked as autostart

Example — reserve an IP for a VM

Enter your choice: 9 Enter the virtual machines name: web-server Enter the virtual machines mac address: 52:54:00:aa:bb:cc Enter the new ip address for the virtual machine: 192.168.100.50 Enter the network name [default]: Setting DHCP reservation... You may need to start / stop the vm for the changes to take effect

3.1 Delete a network (menu 3 → 8)

Deletes a libvirt network and, optionally, its associated table in bashvm.db.

Prompts

What it does

  1. If confirmed, drops the table <network>_table from bashvm.db (only if it exists).
  2. Runs virsh net-destroy <network> then virsh net-undefine <network>.

3.2 Add DHCPv6 to a NAT network (menu 3 → 11)

Adds DHCPv6 service to an existing NAT network. The script refuses to run on non-NAT networks.

Prompts

PromptNotes
Network nameDefault default. Must be a NAT network.
Main interface namee.g. eth0 — the host's uplink for ndppd proxying
IPv6 gateway addresse.g. fd00::1 or a globally routed prefix
Prefix lengthe.g. 64
Starting rangee.g. fd00::2
Ending rangee.g. fd00::300

What it does

  1. Aborts if virsh net-dumpxml does not contain <forward mode='nat'>.
  2. Aborts if the network XML already contains ipv6 — you must remove the existing DHCPv6 config first.
  3. Adds net.ipv6.conf.<iface>.accept_ra = 2 to /etc/sysctl.conf if not present, runs sysctl -p, and creates a systemd unit bashvm-ipv6-accept-ra.service to re-apply on boot.
  4. Strips the closing </ip> and </network> tags from the network XML with sed and appends a new IPv6 <ip> block with a <dhcp> range.
  5. Stops, redefines, starts, and autostarts the network.
  6. Writes /etc/ndppd.conf with a proxy rule for the configured prefix and enables/restarts ndppd.

3.3 Create a routed IPv6 network (menu 3 → 7)

Creates a routed (non-NAT, no DHCP) IPv6 network. Useful for static IPv6 addressing on guests where you control the upstream routing.

Prompts

PromptNotes
New network nameMust not already exist — the script validates with virsh net-info
Main physical interfacee.g. eth0 — used for accept_ra and ndppd
IPv6 network addresse.g. 2001:db8:: — this address is the gateway for guests
IPv6 prefix lengthe.g. 64

What it does

  1. Aborts if a network with that name already exists.
  2. Adds net.ipv6.conf.<iface>.accept_ra = 2 to /etc/sysctl.conf if not already present, applies it, and creates the same bashvm-ipv6-accept-ra.service persistence unit.
  3. Generates a network XML with <forward mode='route'/> and a random bridge name in the form virbrNNN (three random digits).
  4. Defines, starts, and autostarts the network.
  5. Writes /etc/ndppd.conf with a proxy rule for the chosen prefix and enables/restarts ndppd.

4. Snapshots

========================= Manage Snapshot ========================= s. Show all snapshots of a VM 1. Create a snapshot 2. Delete a snapshot 3. Revert to a snapshot q. Back to main menu
Snapshot sub-menu options
KeyActionCommand
sList snapshots for a VMvirsh snapshot-list <vm>
1Create a named snapshotvirsh snapshot-create-as <vm> <name>
2Delete a snapshotvirsh snapshot-delete <vm> <name>
3Revert the VM to a snapshotvirsh snapshot-revert <vm> <name>
qReturn to main menu

Example

Enter your choice: 1 Enter the name of the virtual machine: web-server Name Creation Time State ------------------------------------------------------------ pre-upgrade 2025-01-14 09:12:03 +0000 shutoff Enter the name for the new snapshot: post-upgrade Domain snapshot post-upgrade created

5. Edit Properties

====================== Edit Properties ====================== 1. Edit a VM 2. Edit a storage pool 3. Edit a network 4. Edit a snapshot q. Back to main menu
XML editing options
KeyTargetCommandPrompts
1VM definitionvirsh edit <vm>VM name
2Storage pool definitionvirsh pool-edit <pool>Pool name (default: default)
3Network definitionvirsh net-edit <net>Network name (default: default)
4Snapshot metadatavirsh snapshot-edit --snapshotname <s> --domain <vm>VM name, snapshot name
qReturn to main menu

6. Firewall Settings

===================== Firewall Settings ===================== s. Show ufw status 1. Show listening ports 2. Allow port range 3. Deny port range 4. Allow single port 5. Deny single port 6. Delete a rule 7. Enable and reload ufw 8. Disable and reset ufw q. Back to main menu
Firewall (ufw) options
KeyActionCommandPrompts
sNumbered ufw rule listufw status numberednone
1Listening TCP/UDP socketsnetstat -l | grep "tcp\|udp"none
2Allow a port range on TCP and UDPufw allow START:END/tcp + /udp, then ufw reloadStart port, end port
3Deny a port range on TCP and UDPufw deny START:END/tcp + /udp, then ufw reloadStart port, end port
4Allow a single portufw allow <port>, ufw reloadPort
5Deny a single portufw deny <port>, ufw reloadPort
6Delete a rule by numberufw delete <n>, ufw reloadRule number (see s first)
7Enable the firewall and reload rulesufw enable, ufw reloadnone
8Disable the firewall and wipe all rulesufw disable, ufw resetConfirmation (ufw asks)
qReturn to main menu

Example — open a port range for a web stack

Enter your choice: 2 Enter starting port: 8000 Enter ending port: 8100 Rules updated Rules updated (v6) Firewall reloaded

7. Port Forwarding

==================================== Manage Port Forwarding ==================================== s. Show port forwarding rules 1. List DHCP leases from a network 2. Add port forwarding to a VM 3. Remove port forwarding from a VM 4. Edit port forwarding rule file q. Back to main menu

Port forwarding for VMs behind a NAT network is implemented with the libvirt qemu hook at /etc/libvirt/hooks/qemu. When a VM starts, the hook adds iptables DNAT rules; when it stops, they are removed. Forwarding records are also stored in the SQLite database bashvm.db.

Port forwarding options
KeyActionDetails
s Show current NAT rules Runs iptables -t nat -L -n -v and then 7.3 Show port forwarding rules to dump all tables in bashvm.db.
1 List DHCP leases on a network virsh net-dhcp-leases <network> — use this to find the VM's IP before forwarding to it.
2 Add a forwarding rule to a VM 7.1 Add port forwarding to a VM
3 Remove a forwarding rule from a VM 7.2 Remove port forwarding from a VM
4 Edit the hook script directly Opens /etc/libvirt/hooks/qemu in nano (falls back to vim).
qReturn to main menu

Typical port-forwarding workflow

  1. 1 — list DHCP leases to confirm the guest's IP address.
  2. 2 — add the forwarding rule (host port → guest IP:guest port).
  3. Restart the VM so the qemu hook runs.
  4. s — verify the DNAT rule appears in the NAT table.
  5. 3 — remove the rule when it is no longer needed.

7.1 Add port forwarding to a VM (menu 7 → 2)

Adds a new port-forwarding rule set for a VM behind a NAT network. The first port in the range is reserved for SSH (forwarded to guest port 22); the remaining ports are forwarded 1:1 to the same guest port numbers.

Prompts

PromptValidation
VM nameMust be non-empty
IP of the VMMust match x.x.x.x
Start port rangeNumeric; becomes the SSH port
Ending portNumeric, ≥ start port

What it does

  1. Resolves the VM's network and bridge name from virsh dumpxml / virsh net-dumpxml (defaults to default / virbr0).
  2. Validates that the IP, start port, and end port are well-formed and ordered.
  3. Creates bashvm.db and the network's table if missing.
  4. Rejects the request if the VM name or IP already exists in the table, or if any of the three ports is not strictly greater than the highest port already recorded.
  5. Inserts (vm_name, ipv4, ssh_port, start_port, end_port) into the table.
  6. Appends a block to /etc/libvirt/hooks/qemu between #<vm># and ###<vm>### markers, containing iptables delete rules for the stopped/reconnect branch and insert rules for the start/reconnect branch. Both TCP and UDP are covered for SSH and every port in the range.
  7. Makes the hook executable and restarts libvirtd if it was the only running VM.
  8. Prints the SSH port and the forwarded port range.

7.2 Remove port forwarding from a VM (menu 7 → 3)

Removes a VM's port-forwarding rule set from both bashvm.db and /etc/libvirt/hooks/qemu.

Prompts

What it does

  1. Resolves the network and bridge (defaults default / virbr0).
  2. Aborts if bashvm.db does not exist.
  3. Aborts if the VM has no row in the network's table.
  4. Aborts if the hook file contains no reference to the VM.
  5. Deletes the row from the table.
  6. Removes the block between #<vm># and ###<vm>### from the hook.

7.3 Show port forwarding rules (menu 7 → s)

Dumps every table in bashvm.db with column headers. Used by the port-forwarding menu to give a human-readable view of all reserved ports.

What it does

  1. Lists all tables from sqlite_master.
  2. For each table, prints the table name and then SELECT * with -column -header formatting.
table: default_table vm_name ipv4 ssh_port start_port end_port ----------- --------------- -------- ---------- -------- web-server 192.168.122.50 49152 49153 49172 db-server 192.168.122.51 49173 49174 49193

8. VNC / Console Access

======================== VNC / Console Access ======================== s. Show assigned VNC ports 1. Add VNC port with password 2. Remove VNC port 3. Console into a vm q. Back to main menu
VNC / console options
KeyActionDetails
s Show VNC port per VM Iterates over all VMs, reports not running for stopped domains, otherwise maps the VNC display number N to TCP port 5900 + N, or shows none if VNC is not configured.
1 Add VNC access with a password Prompts for VM name and password, then rewrites the VM XML to inject a full device block: QEMU guest agent channel, USB tablet, PS/2 mouse and keyboard, <graphics type='vnc' port='-1' autoport='yes' listen='0.0.0.0' passwd='…'>, ICH9 sound, QXL video, virtio balloon and virtio RNG. Redefines the domain.
2 Remove VNC access Rewrites the XML with the same device block but without the <graphics> element, removing the VNC listener.
3 Open a serial console virsh console <vm> — exit with Ctrl+].
qReturn to main menu

Example — check ports

Enter your choice: s VM: web-server, VNC Port: 5901 db-server is not running (state: shut off) VM: test-vm, VNC Port: none

Connecting

# from your workstation vncviewer 192.0.2.10:5901

9. System Monitor

Selecting 9 immediately launches btop — a full-screen terminal resource monitor showing CPU, memory, disks, network, and processes for the host.

Enter your choice: 9 # btop opens full screen; press q to quit and return to the BashVM main menu

10. VM Monitor

==================================== VM Monitor ==================================== s. Show all virtual machines 1. VCPU usage of a VM 2. Memory usage of a VM 3. Disk usage of a VM 4. Network usage of a VM 5. All usage metrics of a VM q. Back to main menu
VM monitoring options
KeyMetricMethod
sVM inventoryvirsh list --all
1 Per-vCPU utilisation Reads vcpu.N.time from virsh domstats, sleeps 1 s, reads again, divides the delta by the elapsed time, and prints a percentage for each vCPU.
2 Memory utilisation Ratio of balloon.rss to balloon.rss + balloon.available from virsh domstats.
3 Disk utilisation of the guest filesystem virsh guestinfo fields fs.0.used / fs.0.total.
4 Network throughput Finds the VM's vNIC with virsh domiflist, then ifstat -i <iface> 1 1.
5 Combined metrics dashboard 10.1 All usage metrics of a VM — runs all of the above in one pass.
qReturn to main menu

Example — per-vCPU usage

Enter your choice: 1 Enter the name of the VM: web-server CPU 0 Usage: 12.43% CPU 1 Usage: 3.07%

10.1 All usage metrics of a VM (menu 10 → 5)

Combined per-VM metrics dashboard. Equivalent to running options 1–4 of the VM Monitor menu in sequence for a single VM.

Prompts

What it reports

  1. Per-vCPU utilisation — loops from vCPU 0 to count-1, sleeping 1 s between domstats reads and normalising by 10⁹ ns.
  2. Memory usage — balloon.rss / (balloon.rss + balloon.available) × 100.
  3. Disk usage — fs.0.used / fs.0.total × 100 from virsh guestinfo.
  4. Network throughput — ifstat -i <vNIC> 1 1.
Enter the VM name: web-server CPU 0 Usage: 12.43% CPU 1 Usage: 3.07% Memory Usage: 41.20% Disk Usage: 58.71% eth0 KB/s in KB/s out 0.00 0.00

11. SQLite Editor

The SQLite Editor provides menu-driven access to the per-network tables in bashvm.db (typically holding VM name, IPv4 address, SSH port, and a port range used for port-forwarding bookkeeping). The main script's option 11 simply launches this editor.

Enter your choice: 11 # The SQLite editor takes over; quit it to return to the main menu

Startup prompts

The chosen network's table is created if it does not already exist.

Editor menu

============================ SQLite Editor Menu ============================ s. View all entries 1. Add new VM entry 2. Delete VM entry 3. Update VM entry 4. Delete current table q. Exit
SQLite editor options
KeyActionDetails
s View all entries Column-aligned table with headers, ordered by vm_name.
1 Add VM entry Prompts for VM name (non-empty), IPv4 (validated x.x.x.x), SSH port, start port, end port. Ports accept blank for NULL; non-numeric input is rejected.
2 Delete VM entry Confirms the row exists before deleting.
3 Update VM entry Choose field 1–4 (IPv4, SSH port, start port, end port); validators re-run for the new value.
4 Delete current table Drops the whole table. Requires a y/yes confirmation. Cannot be undone.
qExitReturns to the main menu.

12. Installation & Uninstallation

Two standalone scripts handle first-time setup and complete removal of the packages used by BashVM. They are not invoked from the main menu; run them directly from the shell before the first bashvm.sh launch (installer) or when decommissioning the host (uninstaller).

12.1 Installer (run first)

A fully automated setup script for Debian/Ubuntu hosts. Installs every package BashVM needs, initializes the default storage pool and network, and configures VNC over TLS. It does not prompt for input.

Running

chmod +x bashvm-installer.sh sudo ./bashvm-installer.sh

Packages installed

What it does

  1. Installs every package with apt install -y and exits on the first failure.
  2. Starts and enables libvirtd if it is not already active.
  3. Defines, starts, and autostarts the default storage pool at /var/lib/libvirt/images if missing or inactive.
  4. Defines, starts, and autostarts the default network if missing or inactive.
  5. Generates a TLS CA and server certificate in /etc/pki/libvirt-vnc:
    • ca-cert.pem, ca-key.pem (CA)
    • server-cert.pem, server-key.pem (server, with all detected IPv4/IPv6 addresses in the SAN)
    • 10-year validity, private keys chmod 600
  6. Backs up /etc/libvirt/qemu.conf to a timestamped .bak file, then sets:
    • vnc_tls = 1
    • vnc_tls_x509_cert_dir = "/etc/pki/libvirt-vnc"
  7. Restarts libvirtd if no VMs are currently running. If VMs are running, it prints the restart command and a warning instead.
  8. Prints the final hint: "If no errors above then you can run bashvm with bash bashvm.sh".

12.2 Uninstaller (destructive)

Removes every package that the installer would have installed. It does not touch user data (VM disks, bashvm.db, network XML, or the qemu hook) — those are left for you to remove manually.

Running

chmod +x bashvm-uninstaller.sh sudo ./bashvm-uninstaller.sh

Prompts

What it does

  1. Removes each package with apt remove -y and exits on the first failure: qemu-kvm, libvirt-daemon-system, libvirt-clients, bridge-utils, qemu-utils, virt-manager, cloud-init, net-tools, ufw, btop, bc, ifstat, ndppd, sqlite3.
  2. Runs apt autoremove -y to clean up now-orphaned dependencies.
  3. Prints a completion message.

Common Workflows

First-time host setup

  1. 12.1 — run the installer to install packages and configure VNC TLS.
  2. Confirm systemctl status libvirtd is active.
  3. 1 → 1 — list VMs (should be empty).
  4. 2 → s — confirm the default pool is active.
  5. 3 → s — confirm the default network is active.

Create a new VM from scratch (ISO install)

  1. 2 → 9 — download the installation ISO into the default pool (or let 1.1 Create a new / existing VM do it).
  2. 2 → 6 — create a qcow2 volume for the VM's disk.
  3. 1 → 8 — create the VM definition interactively.
  4. 1 → 11 — console in to run the installer, or use VNC (menu 8).
  5. 1 → 6 — enable autostart so it comes back after a host reboot.

Deploy a cloud-image VM in one step

  1. 1 → 10 — 1.2 Create a new VM (Automated) handles download, cloud-init, DHCP reservation, and port forwarding.
  2. Note the printed IPv4, SSH port, username, and password.
  3. 1 → 11 — console in to verify the guest is healthy.
  4. 7 → s — confirm the forwarding rules landed in the hook.

Duplicate an existing VM

  1. 1 → 4 — gracefully shut down the source VM (important: virt-clone needs a consistent disk).
  2. 1 → 14 — clone to a new name (or 1 → 15 for several copies).
  3. 1 → 2 — start the clone.
  4. 8 → 1 — add a VNC password if you need graphical access.

Expose a guest web server to the LAN

  1. 3 → 4 — make sure the VM sits on a NAT network with a known subnet.
  2. 3 → 9 — reserve a fixed IP for the VM's MAC address.
  3. 7 → 1 — confirm the guest picked up the reserved lease.
  4. 7 → 2 — forward host port 8080 to <guest-ip>:80.
  5. 6 → 4 — allow port 8080 through ufw.
  6. 7 → s — verify the DNAT rule is present.

Roll back a risky change

  1. 4 → 1 — snapshot the VM before the change.
  2. Apply the change.
  3. 4 → 3 — revert to the snapshot if something breaks.
  4. 4 → 2 — clean up the snapshot when you are happy.

Add DHCPv6 to an existing NAT network

  1. Back up the network XML: cp /etc/libvirt/qemu/networks/default.xml ~/default.xml.bak.
  2. 3 → 11 — run 3.2 Add DHCPv6 to a NAT network and supply the interface, gateway, prefix, and range.
  3. Verify with virsh net-dumpxml default | grep ipv6.
  4. Restart guest VMs so they pick up a DHCPv6 address.

Delete a VM completely

  1. 1 → 4 — graceful shutdown (or 1 → 5 to force).
  2. 1 → 13 — 1.4 Delete a VM; type DELETE to confirm.
  3. Check the network no longer has a reservation: 3 → 1.
  4. Check the hook no longer has a block: 7 → 4 and search for the VM name.

Decommission a host

  1. Back up bashvm.db, network XML, and the qemu hook.
  2. 12.2 — run the uninstaller; type remove to confirm.
  3. Manually remove user data left behind (VM disks, /etc/pki/libvirt-vnc, etc.).

Troubleshooting & Notes

Common issues and resolutions
SymptomLikely causeResolution
command not found: bashvm-*.sh Helper scripts are not in the working directory or on PATH. Run the main script from the directory containing all helper scripts, or add that directory to PATH.
error: failed to connect to the hypervisor libvirtd not running, or the script is not run as root / not in the libvirt group. Run 12.1 Installer, or sudo systemctl enable --now libvirtd, then run as root.
No pool with this name was found The pool name typed does not exist. Press s in the storage menu to see exact pool names; remember the prompt defaults to default on an empty line.
Please shutdown the vm before running this again Undefine (1 → 9) or delete (1 → 13) was attempted against a running VM. Shut it down with 1 → 4 (graceful) or 1 → 5 (force), then retry.
Disk usage shows errors or blank values virsh guestinfo returns nothing. Install and start qemu-guest-agent inside the guest.
Downloaded ISO not visible in BashVM Pool metadata not refreshed. Deactivate and reactivate the pool (2 → 3 then 2 → 2), then list volumes with 2 → 1.
Port forward has no effect qemu hook missing, not executable, or the VM was not restarted. Check /etc/libvirt/hooks/qemu (7 → 4), chmod +x it, restart the VM, verify with 7 → s.
ssh_port, start_port and end_port must all be higher… New rule set overlaps or undercuts an existing range in bashvm.db. Use higher port numbers, or inspect/clean the table via menu 11.
The vm name 'x' already exists in the database A port-forwarding record already exists for that VM. Remove it with 7 → 3 first, or choose a different VM name.
This function only works on NAT networks currently 3.2 Add DHCPv6 to a NAT network was run against a routed or macvtap network. Create or select a NAT network first (menu 3 → 4).
VNC TLS connection fails after installer The VNC client is not configured for TLS, or does not trust the generated CA. Configure the client to use TLS and import /etc/pki/libvirt-vnc/ca-cert.pem. Existing VMs need a stop/start before the new qemu.conf settings apply.
VM will not start after editing XML Invalid device addresses or duplicate MACs introduced via menu 5. Restore from a virsh dumpxml backup or re-run virsh edit to correct the offending element.
Invalid choice. Please enter a valid option. Typo or a key that does not exist in the current menu. Re-enter the key exactly as listed; q always leaves the current menu.

General notes