Independent tools and practical guides. · Free app downloads with an account.
NeuroDynamic.TechAccount

Take snapshots and clone a virtual machine

Advanced

Tested setup: I ran every command in this guide top-to-bottom on a fresh Ubuntu 24.04 virtual machine (4 CPUs, 8 GB RAM) on 13 August 2026. The run continued directly from the end state of Create virtual machines from the terminal. Versions at test time: Ubuntu 24.04.4 LTS, libvirt 10.0.0, QEMU 8.2.2, virt-clone 4.1.0, virt-sysprep 1.52.0.

Found a problem? Tell me via the contact page.

Placeholders: anything written in CAPS-WITH-DASHES, like YOUR-VM-IP-HERE or YOUR-VM2-IP-HERE, is a value you replace with your own — here that means the addresses from your lease table. $USER is different — leave it exactly as written; the shell fills in your username for you.

What you'll end up with

Take a snapshot and restore it, then clone the VM. The clone test demonstrates conflicting machine identities and the steps used to change them.

The finished state from my test run — two independent machines, born a few minutes apart from one image:

$ virsh net-dhcp-leases default
 Expiry Time           MAC address         Protocol   IP address           Hostname   Client ID or DUID
------------------------------------------------------------------------------------------------------
 2026-08-13 18:03:49   52:54:00:04:09:f8   ipv4       192.168.122.82/24    vm1        ff:56:...:20:99
 2026-08-13 18:15:37   52:54:00:42:32:0e   ipv4       192.168.122.153/24   vm2        ff:56:...:dd:d0

Who this is for

Advanced. You have done Create virtual machines from the terminal and kept its end state: vm1 running, reachable over SSH with the vm-lab-key. This guide starts exactly there.

Time and cost

The commands for this guide and the headless guide took me about 7 minutes of measured run time in total, as one continuous run. Budget 45 minutes for this half. Cost: nothing; every tool is in Ubuntu's archive.

The test host was itself a VM, so the guests used nested virtualisation. Timings depend on the host hardware, storage and network.

Words you'll meet

  • Internal snapshot — a saved moment (disk, and RAM if running) stored inside the qcow2 file. There are also external snapshots, discussed below. (basics)
  • machine-id — a unique identity string every Linux install carries in /etc/machine-id. Clones inherit it, which causes real trouble. (basics)
  • DUID — the identifier a machine presents to DHCP. On Ubuntu it is derived from the machine-id — which is why cloned machine-ids poison DHCP. (basics)
  • DHCP lease — the network's record of "this machine has this IP". virsh net-dhcp-leases is how you find a headless guest. (basics)

Before you start

The steps

Step 1 — snapshots: save a moment, ruin things, rewind

Libvirt supports two snapshot styles.

Internal snapshots live inside the qcow2 file: one file to manage, one command each way, and they can include RAM while the guest runs. The trade-offs: qcow2-only, slower on big disks, and some tooling (block commit, incremental backup workflows) does not support them.

External snapshots write changes to a new overlay file. Faster and more flexible — but you manage a chain of files, and merging them back is on you.

This guide uses internal snapshots for a small practice VM. It does not cover external-snapshot cleanup or production backup design.

Take a snapshot of the running guest — RAM included:

virsh snapshot-create-as vm1 clean-state --description "Fresh vm1 before experiments"

The important line is:

Domain snapshot clean-state created

Two seconds, in my test — the guest never stopped running.

Now damage the future: create a file that the snapshot knows nothing about (from the host, in one line):

ssh -i ~/.ssh/vm-lab-key ubuntu@YOUR-VM-IP-HERE "echo do-not-lose-me > ~/precious.txt; ls -l ~/precious.txt"

Your output will vary, but look for:

-rw-rw-r-- 1 ubuntu ubuntu 15 Aug 13 17:02 /home/ubuntu/precious.txt

List snapshots:

virsh snapshot-list vm1

You should see something like:

 Name          Creation Time               State
----------------------------------------------------
 clean-state   2026-08-13 17:02:18 +0000   running

Rewind:

virsh snapshot-revert vm1 clean-state

A passing run includes:

Domain snapshot clean-state reverted

Three seconds, and the guest is running again immediately — reverting to a running snapshot restores the RAM too, no reboot.

Check it worked. The file made after the snapshot must be gone:

ssh -i ~/.ssh/vm-lab-key ubuntu@YOUR-VM-IP-HERE "ls -l ~/precious.txt"

You should see something like:

ls: cannot access '/home/ubuntu/precious.txt': No such file or directory

That error is the expected result. Time travel, demonstrated. And the obvious warning made explicit: everything after a snapshot dies when you revert — snapshots are a lab tool and a pre-upgrade parachute, not a backup.

Step 2 — clone it, and watch the clone break

You want a second VM. Copying feels free — one command, no download. Do it, and then watch closely, because what happens next is the most valuable part of this guide.

Know the shape of the problem before you meet it. A clone is a full copy — including its identity. Making it a genuinely new machine means fixing four things: the machine-id, the netplan MAC pin, the hostname, and the SSH host keys. This step springs the trap; the next step fixes all four.

virt-clone needs the source shut off:

virsh shutdown vm1

Clone (8 seconds in my test):

virt-clone --original vm1 --name vm2 --auto-clone

The important line is:

Clone 'vm2' created successfully.

Two notes from my test before we boot anything. The snapshot did not come along — virsh snapshot-list vm2 is empty. And the seed CD was not copied — vm2 points at vm1's seed file (it is read-only, so virt-clone shares it). Both matter later.

Boot both:

virsh start vm1
virsh start vm2

In the recorded test, vm1 returned on its previous address, but vm2 did not appear in the network lease list:

virsh net-dhcp-leases default

You should see something like:

 Expiry Time           MAC address         Protocol   IP address          Hostname   Client ID or DUID
------------------------------------------------------------------------------------------------------
 2026-08-13 18:03:49   52:54:00:04:09:f8   ipv4       192.168.122.82/24   vm1        ff:56:...:20:99

One lease. I waited over four minutes in testing — vm2 never asked for an address at all.

Here is the diagnosis, exactly as I ran it. Read the clone's inherited network config through vm1 (they are identical clones, after all):

ssh -i ~/.ssh/vm-lab-key ubuntu@YOUR-VM-IP-HERE "sudo cat /etc/netplan/50-cloud-init.yaml"

Your output will vary, but look for:

network:
  version: 2
  ethernets:
    enp1s0:
      match:
        macaddress: "52:54:00:04:09:f8"
      dhcp4: true

There it is. Cloud-init pinned the network card by MAC address on first boot. virt-clone — correctly — gave vm2 a fresh MAC:

virsh domiflist vm2

Your output will vary, but look for:

 Interface   Type      Source    Model    MAC
-------------------------------------------------------------
 vnet4       network   default   virtio   52:54:00:42:32:0e

Different MAC. The clone's netplan matches no interface, so it configures nothing — it never even asks for DHCP.

And lurking one layer down: the clone also inherited vm1's /etc/machine-id, from which Ubuntu derives its DHCP DUID. Even with netplan fixed, the two machines would present the same identity to the DHCP server. A clone is not a new machine until you make it one.

Step 3 — give the clone its own identity

Shut the clone down; we operate on its disk from outside:

virsh shutdown vm2

Install libguestfs — the toolkit for editing guest disks without booting them (110 packages, 42 seconds in my test):

sudo apt-get install -y libguestfs-tools

Eject the seed CD it shares with vm1 — the clone must not re-read vm1's first-boot config:

virsh change-media vm2 sda --eject --config

The important line is:

Successfully ejected media.

Reset the machine-id and set the hostname. One deliberate flag choice here: --enable machine-id,customize runs only those operations. virt-sysprep's default operation set also wipes authorized_keys — which would lock us out of our own clone:

sudo virt-sysprep -d vm2 --enable machine-id,customize --hostname vm2

A passing run includes:

[   0.0] Examining the guest ...
[   8.1] Performing "machine-id" ...
[   8.1] Performing "customize" ...
[   8.2] Setting the machine ID in /etc/machine-id
[   8.2] Setting the hostname: vm2

Un-pin the network card — rewrite the clone's netplan to plain "DHCP on enp1s0, any MAC":

sudo virt-customize -d vm2 --run-command "printf 'network:\n  version: 2\n  ethernets:\n    enp1s0:\n      dhcp4: true\n' > /etc/netplan/50-cloud-init.yaml && chmod 600 /etc/netplan/50-cloud-init.yaml"

A passing run includes:

[   0.0] Examining the guest ...
[  19.8] Running: printf [...snipped...]
[  19.9] Finishing off

Boot it:

virsh start vm2

Check it worked. 34 seconds later in my test:

virsh net-dhcp-leases default

You should see something like:

 Expiry Time           MAC address         Protocol   IP address           Hostname   Client ID or DUID
------------------------------------------------------------------------------------------------------
 2026-08-13 18:03:49   52:54:00:04:09:f8   ipv4       192.168.122.82/24    vm1        ff:56:...:20:99
 2026-08-13 18:15:37   52:54:00:42:32:0e   ipv4       192.168.122.153/24   vm2        ff:56:...:dd:d0

Two leases, two hostnames, two different DUIDs — that last column differing is the machine-id reset doing its job. Prove it end to end:

ssh -i ~/.ssh/vm-lab-key ubuntu@YOUR-VM2-IP-HERE "hostname; cat /etc/machine-id"

Your output will vary, but look for:

vm2
2336d8cbf5e0a8015956a0b344b0a4ea

vm1 reported b93b68cff1c749298b89ce14210e5df1 in my test — different identity, as required.

One last inherited secret: the clone still carries vm1's SSH host keys (I checksummed them — byte-identical). For a disposable lab this is acceptable; for anything real, regenerate them — otherwise two servers claim the same cryptographic identity. Regenerate inside vm2:

ssh -i ~/.ssh/vm-lab-key ubuntu@YOUR-VM2-IP-HERE "sudo rm /etc/ssh/ssh_host_*; sudo dpkg-reconfigure -f noninteractive openssh-server; sudo systemctl restart ssh; sudo md5sum /etc/ssh/ssh_host_ed25519_key.pub"

You should see something like:

4bfddc6cc86a97a47acccf89392dde2d  /etc/ssh/ssh_host_ed25519_key.pub

Different checksum from vm1's (mine was a36e8aa9...). Now they are genuinely two machines.

Something went wrong?

Everything below happened in my recorded run — most of it on purpose.

  • Your clone is running but never gets an IP → it means netplan is pinned to the original's MAC (and the machine-id is cloned underneath) → this is Steps 2-3's whole story; apply Step 3.
  • You see error: ... cannot delete inactive domain with 1 snapshots when undefining → it means snapshot metadata blocks the delete → add --snapshots-metadata (the headless guide's Undo shows it).

Undo all of this

Tested setup — as one combined undo with Create virtual machines from the terminal. Here is the part that removes what this guide added; that guide's undo then clears vm1, the packages, and the folders.

Force the clone off and delete it:

virsh destroy vm2
virsh undefine vm2 --remove-all-storage

(No snapshots on vm2, so no extra flag needed.)

Two things this guide added live on vm1's side, and the headless guide's undo handles both. The clean-state snapshot goes when vm1 is undefined — that is why its undo carries --snapshots-metadata; I captured the refusal without it. And libguestfs-tools, installed here in Step 3, is included in that guide's package purge.

Check it worked.

virsh list --all

Only vm1 remains in the list. Then run Create virtual machines from the terminal's Undo; I ran the two back-to-back as one sequence and ended with a completely clean machine.

Where to go next


Last tested: 13 August 2026 on Ubuntu 24.04.4 LTS. Versions: libvirt 10.0.0, QEMU 8.2.2, virt-clone 4.1.0, libguestfs/virt-sysprep 1.52.0. Tested as one continuous run with the headless VMs guide; transcripts live in ../guide-09-headless-vms/.

Display settings

Changes apply immediately and stay in this browser.

Colour theme