Command-Line Scripts for Virtual Machine Management with Libvirt/KVM/QEMU
Published:
This post provides command-line scripts and tools for managing virtual machines with Libvirt, KVM, and QEMU. These scripts help automate common VM operations and streamline virtualization workflows.
- Overview
- A script for vm creation
- to mount folders front host
- script for some app installation (skip if you do not want)
- Prerequisites
- Basic VM Operations
- Script Examples
- Advanced Operations
- Best Practices
- Troubleshooting
- Summary
- References
Overview
Libvirt provides a unified API for managing various virtualization technologies, including KVM/QEMU. This post covers:
- VM lifecycle management: create, start, stop, delete
- Resource monitoring: CPU, memory, disk usage
- Network configuration: bridges, NAT, host networking
- Storage management: disk images, snapshots, backups
- Automation scripts: batch operations, monitoring, maintenance
A script for vm creation
#!/usr/bin/env bash
# Sample commands:
# ./setup_kvm.sh my-vm 16384 8 2048
# ./setup_kvm.sh my-vm 16384 8 2048 --force # force to overwrite existing vm files/disk
# ./setup_kvm.sh my-vm 262144 64 4096 # 256GB RAM, 64 vCPUs (268435456 KiB = 262144 MB)
#
# Arguments:
# $1: VM name
# $2: RAM in MB
# $3: vCPUs
# $4: Disk size in GB (sparse qcow2)
# $5: Optional --force or --clean flag
# sudo apt install -y qemu-kvm libvirt-bin virtinst cloud-image-utils
# sudo usermod -aG libvirt $USER
# newgrp libvirt # or log out/in
# wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
set -euo pipefail
# -----------------------------
# ARG PARSE
# -----------------------------
if [[ $# -lt 4 ]]; then
echo "Usage: $0 <vm-name> <ram-mb> <vcpus> <disk-size-gb> [--force|--clean]"
echo ""
echo "Examples:"
echo " $0 my-vm 16384 8 2048"
echo " $0 my-vm 16384 8 2048 --force"
echo " $0 my-vm 262144 64 4096 # 256GB RAM, 64 vCPUs"
exit 1
fi
VM_NAME="$1"
VM_RAM="$2"
VM_VCPUS="$3"
VM_DISK_SIZE="$4" # GB (sparse qcow2)
FORCE=false
if [[ "${5:-}" == "--force" || "${5:-}" == "--clean" ]]; then
FORCE=true
fi
# -----------------------------
# VALIDATE ARGUMENTS
# -----------------------------
if ! [[ "$VM_RAM" =~ ^[0-9]+$ ]]; then
echo "Error: RAM must be a positive integer (got: $VM_RAM)"
exit 1
fi
if ! [[ "$VM_VCPUS" =~ ^[0-9]+$ ]]; then
echo "Error: vCPUs must be a positive integer (got: $VM_VCPUS)"
exit 1
fi
if ! [[ "$VM_DISK_SIZE" =~ ^[0-9]+$ ]]; then
echo "Error: Disk size must be a positive integer (got: $VM_DISK_SIZE)"
exit 1
fi
# -----------------------------
# CONFIG
# -----------------------------
VM_DISK="${VM_NAME}.qcow2"
CLOUD_IMG="noble-server-cloudimg-amd64.img"
NETWORK="default"
SSH_PUBKEY="${HOME}/.ssh/id_rsa.pub" # industrial standard
CLOUD_INIT_DIR="./cloud-init/${VM_NAME}"
SEED_ISO="${CLOUD_INIT_DIR}/seed.iso"
# -----------------------------
# PRECHECKS
# -----------------------------
for cmd in virt-install virsh cloud-localds qemu-img; do
command -v "$cmd" >/dev/null || {
echo "Missing $cmd"
echo "Install: sudo apt install -y qemu-kvm libvirt-daemon-system virtinst cloud-image-utils"
exit 1
}
done
[[ -f "$CLOUD_IMG" ]] || { echo "Cloud image not found"; exit 1; }
[[ -f "$SSH_PUBKEY" ]] || { echo "SSH public key not found: $SSH_PUBKEY"; exit 1; }
# -----------------------------
# CLEANUP
# -----------------------------
if $FORCE; then
virsh destroy "$VM_NAME" &>/dev/null || true
virsh undefine "$VM_NAME" &>/dev/null || true
rm -f "$VM_DISK"
rm -rf "$CLOUD_INIT_DIR"
fi
if virsh dominfo "$VM_NAME" &>/dev/null; then
echo "VM already exists. Use --force"
exit 1
fi
# -----------------------------
# CLOUD-INIT
# -----------------------------
mkdir -p "$CLOUD_INIT_DIR"
cat > "${CLOUD_INIT_DIR}/meta-data" <<EOF
instance-id: ${VM_NAME}
local-hostname: ${VM_NAME}
EOF
cat > "${CLOUD_INIT_DIR}/user-data" <<EOF
#cloud-config
users:
- name: ubuntu
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
ssh_authorized_keys:
- $(cat "$SSH_PUBKEY")
ssh_pwauth: false
disable_root: true
package_update: true
packages:
- python-is-python3
- nfs-common
final_message: "Cloud-init finished. SSH ready."
EOF
cloud-localds "$SEED_ISO" \
"${CLOUD_INIT_DIR}/user-data" \
"${CLOUD_INIT_DIR}/meta-data"
# -----------------------------
# DISK
# -----------------------------
mkdir -p "$(dirname "$VM_DISK")"
qemu-img create \
-f qcow2 \
-b "$CLOUD_IMG" \
-F qcow2 \
"$VM_DISK" \
"${VM_DISK_SIZE}G"
# -----------------------------
# VM CREATE
# -----------------------------
# --os-variant ubuntu24.04 not supported by virt-install 1.5.1
virt-install \
--name "$VM_NAME" \
--memory "$VM_RAM" \
--vcpus "$VM_VCPUS" \
--disk path="$VM_DISK",bus=virtio,cache=none,io=native,discard=unmap \
--disk path="$SEED_ISO",device=cdrom \
--import \
--network network="$NETWORK",model=virtio \
--graphics none \
--console pty,target_type=serial \
--noautoconsole
echo
echo "========================================"
echo "VM CREATED SUCCESSFULLY"
echo "========================================"
echo "Get IP:"
echo " virsh domifaddr $VM_NAME"
echo
echo "Login:"
echo " ssh ubuntu@<VM_IP>"
echo
echo "Console (debug only):"
echo " virsh console $VM_NAME"
echo "you should wait 3-5 minutes for the VM to set up for the first !!! Otherwise, you cannot access via ssh"
to mount folders front host
server
/etc/exports
/work 192.168.122.0/24(rw,sync,no_subtree_check,no_root_squash)
/nfs 192.168.122.0/24(rw,sync,no_subtree_check,no_root_squash)
client
/etc/fstab
192.168.122.1:/work /work nfs rw,exec,_netdev,hard,intr,timeo=600,retrans=5 0 0
192.168.122.1:/nfs /nfs nfs rw,exec,_netdev,hard,intr,timeo=600,retrans=5 0 0
script for some app installation (skip if you do not want)
TZ=Asia/Shanghai
sudo ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ | sudo tee /etc/timezone
sudo sed -i 's|archive.ubuntu.com|mirrors.tuna.tsinghua.edu.cn|g' /etc/apt/sources.list.d/ubuntu.sources \
&& sudo sed -i 's|security.ubuntu.com|mirrors.tuna.tsinghua.edu.cn|g' /etc/apt/sources.list.d/ubuntu.sources \
&& sudo apt update
# ------------------------------------------------------------------- basic tools
sudo apt install -y git wget netcat-openbsd tmux curl
# ------------------------------------------------------------------- python
sudo apt install -y python3 python3-pip python3-virtualenv
sudo pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
export BIOPY=$HOME/pyenvs/bio
virtualenv $BIOPY
echo "source $HOME/pyenvs/bio/bin/activate" >> $HOME/.bashrc
# ------------------------------------------------------------------- jupyter
$BIOPY/bin/pip install jupyterlab jupyter-collaboration
export JUPYTER_CONFIG_DIR=$HOME/.jupyter
mkdir -p $JUPYTER_CONFIG_DIR
echo 'c.ServerApp.password = "......' >> $JUPYTER_CONFIG_DIR/jupyter_server_config.py
# ------------------------------------------------------------------- R core
sudo apt install -y r-base r-base-dev
# Speed up R compilation
echo "MAKEFLAGS=-j$(nproc)" | sudo tee -a /etc/R/Makeconf
export MAKEFLAGS="-j$(nproc)" # Fixed: added export with $(nproc)
# the default repos sometimes cause download error, the tsinghua is very stable
R_VERSION=$(Rscript -e 'cat(paste0(R.version$major, ".", strsplit(R.version$minor, "[.]")[[1]][1]))') && \
mkdir -p ~/R/x86_64-pc-linux-gnu-library/${R_VERSION} && \
echo ".libPaths('~/R/x86_64-pc-linux-gnu-library/${R_VERSION}')" >> ~/.Rprofile
echo 'options(repos = c(CRAN = "https://mirrors.tuna.tsinghua.edu.cn/CRAN/"))' >> ~/.Rprofile
Rscript -e 'install.packages("IRkernel"); IRkernel::installspec(user = TRUE)'
# ------------------------------------------------------------------- R application
# required by Seurat (as the installation message hint)
sudo apt install -y libcurl4-openssl-dev libssl-dev
# Matrix is already bundled with R-base
Rscript -e 'install.packages("Seurat")'
Rscript -e 'install.packages(c("ggplot2", "data.table"))'
sudo apt install -y libudunits2-dev libssl-dev cmake libgdal-dev libgeos-dev libproj-dev libsqlite3-dev libudunits2-dev
Rscript -e 'install.packages("sf")'
Rscript -e 'install.packages("R.utils")'
# ------------------------------------------------------------------ docker
sudo apt-get install -y ca-certificates curl gnupg \
&& sudo install -m 0755 -d /etc/apt/keyrings \
&& curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/debian/gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://mirrors.aliyun.com/docker-ce/linux/debian trixie stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null \
&& sudo apt-get update \
&& sudo apt install -y docker.io
- jupyter service
mkdir -p ~/.config/systemd/user/
vi ~/.config/systemd/user/jupyter.service
[Unit]
Description=Jupyter Notebook
[Service]
Type=simple
ExecStart=/home/ubuntu/pyenvs/bio/bin/jupyter lab --ip=0.0.0.0 --port=8888 --no-browser --notebook-dir=/ --collaborative
WorkingDirectory=/home/ubuntu
[Install]
WantedBy=default.target
# setup
loginctl enable-linger
systemctl --user enable jupyter
systemctl --user start jupyter
| below is generated by AI |
Prerequisites
# Install required packages (Ubuntu/Debian)
sudo apt-get update
sudo apt-get install -y qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virt-manager
# Verify installation
virsh list --all
Basic VM Operations
List All VMs
# List all VMs (running and stopped)
virsh list --all
# List only running VMs
virsh list
# List with more details
virsh list --all --details
Start/Stop/Restart VMs
# Start a VM
virsh start <vm-name>
# Stop a VM (graceful shutdown)
virsh shutdown <vm-name>
# Force stop a VM
virsh destroy <vm-name>
# Restart a VM
virsh reboot <vm-name>
VM Information
# Get VM details
virsh dominfo <vm-name>
# Get VM configuration
virsh dumpxml <vm-name>
# Get VM statistics
virsh domstats <vm-name>
Script Examples
Script 1: VM Status Checker
#!/bin/bash
# vm-status.sh - Check status of all VMs
echo "=== Virtual Machine Status ==="
echo ""
virsh list --all | tail -n +3 | while read line; do
if [ -n "$line" ]; then
vm_name=$(echo $line | awk '{print $2}')
state=$(echo $line | awk '{print $3}')
echo "VM: $vm_name - State: $state"
fi
done
Script 2: Batch VM Operations
#!/bin/bash
# batch-vm-ops.sh - Perform operations on multiple VMs
ACTION=$1
VM_LIST=("vm1" "vm2" "vm3")
if [ -z "$ACTION" ]; then
echo "Usage: $0 <start|stop|restart|status>"
exit 1
fi
for vm in "${VM_LIST[@]}"; do
echo "Processing $vm..."
case $ACTION in
start)
virsh start $vm
;;
stop)
virsh shutdown $vm
;;
restart)
virsh reboot $vm
;;
status)
virsh dominfo $vm | grep "State:"
;;
esac
done
Script 3: VM Resource Monitor
#!/bin/bash
# vm-monitor.sh - Monitor VM resource usage
VM_NAME=$1
if [ -z "$VM_NAME" ]; then
echo "Usage: $0 <vm-name>"
exit 1
fi
while true; do
clear
echo "=== Resource Monitor for $VM_NAME ==="
echo ""
# CPU usage
echo "CPU Usage:"
virsh domstats $VM_NAME --cpu | grep -E "cpu.time|cpu.usage"
# Memory usage
echo ""
echo "Memory Usage:"
virsh domstats $VM_NAME --balloon | grep -E "balloon.current|balloon.maximum"
# Disk I/O
echo ""
echo "Disk I/O:"
virsh domstats $VM_NAME --block | grep -E "block.*rd.bytes|block.*wr.bytes"
sleep 2
done
Advanced Operations
Snapshot Management
# Create snapshot
virsh snapshot-create-as <vm-name> --name snapshot1 --description "Before update"
# List snapshots
virsh snapshot-list <vm-name>
# Revert to snapshot
virsh snapshot-revert <vm-name> snapshot1
# Delete snapshot
virsh snapshot-delete <vm-name> snapshot1
Network Configuration
# List networks
virsh net-list --all
# Network information
virsh net-info default
# Start/stop network
virsh net-start default
virsh net-destroy default
Best Practices
- Backup VM configurations: Regularly export VM XML configurations
- Monitor resources: Set up alerts for CPU, memory, and disk usage
- Use snapshots: Create snapshots before major changes
- Network isolation: Use separate networks for different VM groups
- Storage management: Monitor disk space and implement cleanup policies
Troubleshooting
Common Issues
# Check libvirt service status
sudo systemctl status libvirtd
# Restart libvirt service
sudo systemctl restart libvirtd
# Check VM logs
virsh console <vm-name>
# View VM errors
virsh dominfo <vm-name> | grep -i error
Summary
These command-line scripts and tools provide a foundation for managing virtual machines with Libvirt/KVM/QEMU. They can be extended and customized based on specific requirements and environments.
