How To

Configure Guestfish to Modify VM Disk Images on Linux

Guestfish lets you open, inspect, and modify virtual machine disk images without booting the VM. Need to inject an SSH key into a cloud image, fix a broken /etc/fstab, reset a root password, or pull log files from a crashed guest? Guestfish handles all of that from the hypervisor host, with the VM powered off.

Original content from computingforgeeks.com - post 90786

It’s part of the libguestfs toolkit and works with qcow2, raw, VMDK, VHD, and any format QEMU understands. This guide covers installation, practical use cases, and every operation you’ll actually need when managing KVM virtual machines.

Tested March 2026 | guestfish 1.48.6 on Proxmox VE 8.4, Rocky Linux 10.1 guest image (qcow2)

Install Guestfish

Guestfish is part of the libguestfs toolkit. Install it on the KVM host (not inside the guest). The package name differs between distributions.

On Ubuntu/Debian/Linux Mint:

sudo apt install -y libguestfs-tools

On Rocky Linux/AlmaLinux/RHEL 10:

sudo dnf install -y guestfs-tools

Note the different package names: libguestfs-tools on Debian-based, guestfs-tools on RHEL-based. Both install the same guestfish binary and companion tools (virt-cat, virt-ls, virt-edit, etc.).

Verify the installation:

guestfish --version

On our Debian-based Proxmox host this returned guestfish 1.48.6. On Rocky Linux 10.1 it installed version 1.56.1.

Before You Start

The VM must be shut down before modifying its disk image. Running guestfish against a live VM’s disk will corrupt the filesystem. Shut it down first:

sudo virsh shutdown myvm

Find the disk image path:

virsh dumpxml myvm | grep "source file"

This returns the full path to the qcow2 or raw image. On Proxmox with LVM-thin storage, the path looks like /dev/pve/vm-100-disk-0.

Opening a Disk Image

Guestfish can open images in read-only or read-write mode. The -i flag auto-detects the OS and mounts all partitions.

Read-only (safe for inspection, no changes possible):

sudo guestfish --ro -i -a /path/to/disk.qcow2

Read-write (for modifications):

sudo guestfish --rw -i -a /path/to/disk.qcow2

You can also reference a libvirt domain by name instead of the image path:

sudo guestfish --ro -i -d myvm

After connecting, the guestfish shell displays the detected OS and mounted partitions:

Welcome to guestfish, the guest filesystem shell for
editing virtual machine filesystems and disk images.

Type: 'help' for help on commands
      'man' to read the manual
      'quit' to quit the shell

Operating system: Rocky Linux 10.1 (Red Quartz)
/dev/sda4 mounted on /
/dev/sda3 mounted on /boot
/dev/sda2 mounted on /boot/efi

><fs>

The ><fs> prompt is where you type commands. One important difference from a regular shell: there is no concept of a current working directory. You cannot cd into a directory. All paths must be absolute, starting with /. Tab completion works for paths.

Inspect the Guest OS

Read the OS release info:

cat /etc/os-release

On our Rocky Linux 10.1 test image:

NAME="Rocky Linux"
VERSION="10.1 (Red Quartz)"
ID="rocky"
ID_LIKE="rhel centos fedora"
VERSION_ID="10.1"
PRETTY_NAME="Rocky Linux 10.1 (Red Quartz)"

Query OS metadata programmatically (useful in scripts):

inspect-get-distro /dev/sda4
rocky

inspect-get-product-name /dev/sda4
Rocky Linux 10.1 (Red Quartz)

inspect-get-hostname /dev/sda4
guestfish-test

inspect-get-type /dev/sda4
linux

List Devices, Partitions, and Filesystems

See all block devices:

list-devices
/dev/sda

List partitions:

list-partitions
/dev/sda1
/dev/sda2
/dev/sda3
/dev/sda4

Map partitions to filesystem types:

list-filesystems
/dev/sda1: unknown
/dev/sda2: vfat
/dev/sda3: xfs
/dev/sda4: xfs

Check disk usage inside the guest:

df-h

This shows the same output you’d get from running df -h inside the VM:

Filesystem      Size  Used Avail Use% Mounted on
/dev/sda4        19G  953M   18G   6% /sysroot
/dev/sda3       936M  187M  750M  20% /sysroot/boot
/dev/sda2       200M   13M  187M   7% /sysroot/boot/efi

For images with LVM, use pvs, vgs, and lvs to list physical volumes, volume groups, and logical volumes.

Browse and Read Files

List directory contents:

ls /root
.bash_logout
.bash_profile
.bashrc
.cshrc
.ssh
.tcshrc

For detailed output with permissions and sizes, use ll instead:

ll /root

Read a file:

cat /etc/hostname

Modify Files and Directories

These operations require read-write mode (--rw). The most common real-world use cases:

Inject an SSH Key

This is probably the most frequent reason to use guestfish. Inject a public key so you can SSH into the VM after boot. Run this from the host (not inside the guestfish shell):

sudo guestfish --rw -i -a /path/to/disk.qcow2 \
  mkdir-p /root/.ssh : \
  write /root/.ssh/authorized_keys "ssh-ed25519 AAAAC3... your-key" : \
  chmod 0700 /root/.ssh : \
  chmod 0600 /root/.ssh/authorized_keys

The colon (:) separates multiple commands on the guestfish command line. Verify the key was written:

sudo guestfish --ro -i -a /path/to/disk.qcow2 cat /root/.ssh/authorized_keys

Change the Hostname

Overwrite the hostname file from within the guestfish shell:

write /etc/hostname "new-hostname
"
cat /etc/hostname
new-hostname

Edit Configuration Files

The edit command downloads the file, opens it in your local $EDITOR, then uploads it back:

edit /etc/fstab

This is ideal for fixing a broken /etc/fstab that prevents a VM from booting.

Create Files and Directories

Create a directory structure and write a script into it:

mkdir-p /opt/scripts
write /opt/scripts/hello.sh "#!/bin/bash
echo Hello from guestfish
"
chmod 0755 /opt/scripts/hello.sh

Verify the file:

cat /opt/scripts/hello.sh
#!/bin/bash
echo Hello from guestfish

Delete a directory with rmdir (must be empty) or rm-rf (recursive delete).

Copy Files Between Host and Guest

Upload a File to the Guest

Push a file from the host into the guest filesystem:

upload /tmp/host-file.txt /root/from-host.txt

Verify it landed:

cat /root/from-host.txt
Uploaded from host at Thu Mar 26 15:18:34 EAT 2026

Download a File from the Guest

Pull a file from the guest to the host:

download /etc/os-release /tmp/guest-os-release

After exiting guestfish, the file is on the host at /tmp/guest-os-release.

Copy Entire Directories

Pull a directory from the guest to the host:

copy-out /var/log /tmp/guest-logs

Push a local directory into the guest:

copy-in /tmp/configs /etc/

Pack and Download as Tarball

Export a guest directory as a compressed tar archive:

tar-out /var/log /tmp/guest-logs.tar.gz compress:gzip

To unpack after exiting guestfish:

tar xzf /tmp/guest-logs.tar.gz

The reverse operation pushes a local tarball into the guest:

tar-in backups.tar.gz /tmp compress:gzip

Quick Commands Without the Interactive Shell

The libguestfs package includes several standalone commands that skip the interactive shell entirely. These are faster for scripting and one-off tasks.

Read a single file:

sudo virt-cat -a /path/to/disk.qcow2 /etc/hostname

List a directory:

sudo virt-ls -a /path/to/disk.qcow2 /root/

List all filesystems with sizes:

sudo virt-filesystems -a /path/to/disk.qcow2 --all --long --human-readable

On our test image:

Name       Type        VFS      Label  Size   Parent
/dev/sda1  filesystem  unknown  -      2.0M   -
/dev/sda2  filesystem  vfat     EFI    200M   -
/dev/sda3  filesystem  xfs      BOOT   936M   -
/dev/sda4  filesystem  xfs      rocky  18G    -
/dev/sda   device      -        -      20G    -

Edit a file in one command (opens in $EDITOR):

sudo virt-edit -a /path/to/disk.qcow2 /etc/fstab

Copy files into a guest image:

sudo virt-copy-in -a /path/to/disk.qcow2 /tmp/local-file.txt /root/

Copy files out of a guest image:

sudo virt-copy-out -a /path/to/disk.qcow2 /etc/os-release /tmp/

SELinux Considerations (RHEL/Rocky/Alma)

When you modify files in a RHEL-family guest using guestfish, the new files may not have the correct SELinux labels. The guest will relabel them on next boot if autorelabel is enabled, but this adds boot time.

To trigger a relabel on the next boot, create the /.autorelabel file inside the guestfish shell:

touch /.autorelabel

Alternatively, use virt-customize which handles SELinux relabeling automatically. This runs from the host:

sudo virt-customize -a /path/to/disk.qcow2 \
  --run-command 'echo modified > /etc/hostname' \
  --selinux-relabel

Guestfish Command Reference

Common commands inside the guestfish shell:

CommandDescription
ls /pathList directory contents
ll /pathLong listing with permissions
cat /fileDisplay file contents
write /file "content"Write string to file
edit /fileEdit file in local $EDITOR
upload local remoteCopy file from host to guest
download remote localCopy file from guest to host
copy-in /local /guestCopy directory from host to guest
copy-out /guest /localCopy directory from guest to host
mkdir-p /pathCreate directory (with parents)
chmod 0755 /fileChange file permissions
chown uid gid /fileChange file ownership
rm /fileDelete a file
rm-rf /pathRecursive delete
tar-out /dir file.tar.gz compress:gzipExport directory as tarball
tar-in file.tar.gz /dir compress:gzipImport tarball into guest
df-hDisk usage
list-filesystemsList all filesystems
list-devicesList block devices
inspect-get-distro /devGet OS distribution name
help commandGet help on a command
exitQuit guestfish

For the full list of all available commands:

help --list

Related tools for KVM image management:

Related Articles

KVM KVM Host Network Configurations using Virt-Manager Virtualization Customize Linux Qcow2 or Raw image with virt-customize Proxmox How To Install Proxmox VE 8 on OVH Dedicated Server Debian How To Upgrade From Proxmox VE 7 To Proxmox VE 8

Leave a Comment

Press ESC to close