Cloning VMs in ESXi - without a vCenter server

Intro

One of the issues you may encounter with ESXi is the fact that it doesn't offer an option to natively clone VMs. Of course, you can just copy necessary files and then just register the VM in ESXi, but that will still use file names that are in the original VM. I don't like this, so I am going to show you all the steps in 'cloning' the VM (basically just copying the necessary files from one location to another) and then making changes to the .vmx and .vmdk files to reflect new naming convention.

This whole process is extremely useful when you make a golden template of a VM that you want to use in the future. This way you can avoid installing the VMs whenever you need a new instance up and running. Make sure that you set everything up in your VM and once done, follow this process.

ESXi VM cloning

Let's start by selecting Storage on the left side of the screen, and then selecting Datastore browser. That will open another window where we will click on Create directory:

Enter the name of the new directory and press CREATE DIRECTORY:

Once the directory is created, position yourself in the directory that holds your existing VM that you want to use a source. Select the .vmx file first, right click and click on Copy:

This will open another window where you need to select a destination folder for this file:

We will select previously created folder and then click on COPY. Copying .vmx file is very quick and you may not even notice status bar at the bottom of the screen changing.

Next, we will copy .vmdk file. This is basically your whole hard disk. This process may take some time, depending on your infrastructure (whether you're using SSDs or NVMe drives or regular HDDs).

Same as before, select your source .vmdk file, right click and select COPY:

Next, select the directory where you want to copy the file to:

As I mentioned, this may take some time, so watch your status bar for progress:

If you don't want to deal with changing the name of the files and references in the files you copied, you can jump to the end of this post where I explain on how to register the newly copied VM. If you want to make sure that everything is named exactly as you want it, continue reading.

Changes in the CLI

Before you continue, make sure that SSH server is running on your ESXi instance:

If it's not, just click on Host -> Manage -> Services and select TSM-SSH service and click on Start.

SSH service will now be enabled, until the restart of the ESXi server. It will complain about SSH running next time you log into the system, but you can ignore that message. VMWare insists that running SSH on a server is not a great idea, so I will leave it up to you if you want to leave it running or disabling it after you're done with the changes we are about to make.

So, with this all out of the way, let's connect to our ESXi via SSH. Use root credentials to log in and you will be greeted with prompt like this:

╰─>$ ssh [email protected]
([email protected]) Password:
The time and date of this login have been sent to the system logs.

WARNING:
   All commands run on the ESXi shell are logged and may be included in
   support bundles. Do not provide passwords directly on the command line.
   Most tools can prompt for secrets or accept them from standard input.

VMware offers powerful and supported automation tools. Please
see https://developer.vmware.com for details.

The ESXi Shell can be disabled by an administrative user. See the
vSphere Security documentation for more information.
[root@esxi-ntg:~]

Position yourself in the directory you previously created in GUI:

[root@esxi-ntg:~] cd /vmfs/volumes/datastore1/Debian\ Desktop\ -\ 02/
[root@esxi-ntg:/vmfs/volumes/64b6cb2a-91d9a1d5-800b-005056902e9a/Debian Desktop - 02]

Datastores are available under /vmfs/volumes, followed by the datastore name - in my case that is datastore1, which is the default name given by ESXi when you install the system. If you changed the name of the datastore, this will be different for you. After that, comes the name of the directory you created.

Let's list all the files in that directory:

[root@esxi-ntg:/vmfs/volumes/64b6cb2a-91d9a1d5-800b-005056902e9a/Debian Desktop - 02] ls -alh
total 9016448
drwxr-xr-x    1 root     root       72.0K Jul 23 20:12 .
drwxr-xr-t    1 root     root       76.0K Jul 23 19:59 ..
-rw-------    1 root     root      100.0G Jul 23 20:01 Debian Desktop - 01-flat.vmdk
-rw-------    1 root     root         564 Jul 23 20:12 Debian Desktop - 01.vmdk
-rw-------    1 root     root        3.4K Jul 23 20:00 Debian Desktop - 01.vmx

You will notice that there are actually 3 files there, not only 2 that we used to copy everything - a .vmx file, a .vmdk file and a new one called -flat.vmdk. Notice that the size of the -flat.vmdk file is actually the size of the HDD you assigned to your VM when you created it.

Let's customize this a little bit. Instead of having names of the previous VM, we will rename them so that it reflects the names of the new VM. I'm going to make this simple, so I will just choose Debian Desktop - 02, but you can choose whatever name you want.

We will simply rename files:

[root@esxi-ntg:/vmfs/volumes/64b6cb2a-91d9a1d5-800b-005056902e9a/Debian Desktop - 02] mv 'Debian Desktop - 01-flat.vmdk' 'Debian Desktop - 02-flat
.vmdk'
[root@esxi-ntg:/vmfs/volumes/64b6cb2a-91d9a1d5-800b-005056902e9a/Debian Desktop - 02] mv 'Debian Desktop - 01.vmdk' 'Debian Desktop - 02.vmdk'
[root@esxi-ntg:/vmfs/volumes/64b6cb2a-91d9a1d5-800b-005056902e9a/Debian Desktop - 02] mv 'Debian Desktop - 01.vmx' 'Debian Desktop - 02.vmx'

Note that I'm using single quotes ' between names, as there are spaces in the names of the files so this way I'm sure that right files are being renamed. Make sure that you use the quote before and after the name of the file!

Finally, we have our new file names:

[root@esxi-ntg:/vmfs/volumes/64b6cb2a-91d9a1d5-800b-005056902e9a/Debian Desktop - 02] ls -alh
total 9016448
drwxr-xr-x    1 root     root       72.0K Jul 23 20:41 .
drwxr-xr-t    1 root     root       76.0K Jul 23 19:59 ..
-rw-------    1 root     root      100.0G Jul 23 20:01 Debian Desktop - 02-flat.vmdk
-rw-------    1 root     root         564 Jul 23 20:12 Debian Desktop - 02.vmdk
-rw-------    1 root     root        3.4K Jul 23 20:00 Debian Desktop - 02.vmx

Now, with this out of the way, the easy part is done. We still need to change name references in .vmdk and .vmx files. DO NOT try to open -flat.vmdk file in your editor!

Unfortunately, ESXi only comes with vi editor - if you're not familiar with it, you can always go back to GUI and download these two files from ESXi server to your local computer and edit them in Notepad or whatever you're using on your local computer and edit it there. Since this is not a vi tutorial, we will do exactly that, but if you're comfortable in using it, you can just make changes there directly and save them. I believe that most homelabers are not, so I will show you how to download the files, make changes and the upload it back to the server.

So, open your datastore browser and download both .vmdk and .vmx files. Once again, make sure that you're not selecting the -flat.vmdk file while doing this!

Open the .vmdk file and there is only one reference to the name of the -flat.vmdk inside of double quotes ". Make your change there and save the file:

Now, open the second file (.vmx) and look for any references of the old name. Depending on your VM, it will have a few variables that need to be changed. In my case, these were the variables that needed to be changed to reflect the new names:

  • scsi0:0.fileName = "Debian Desktop - 02.vmdk"
  • displayName = "Debian Server - 02"
  • vmxstats.filename = "Debian Desktop - 02.scoreboard"
  • sched.swap.derivedName = "/vmfs/volumes/64b6cb2a-91d9a1d5-800b-005056902e9a/Debian Desktop - 02/Debian Desktop - 02-b2f95b67.vswp"
  • migrate.hostLog = "./Debian Desktop - 02-e139194a.hlog"

Save the file and upload both files back to your ESXi server. After the upload, let's check if everything is as it should be:

[root@esxi-ntg:/vmfs/volumes/64b6cb2a-91d9a1d5-800b-005056902e9a/Debian Desktop - 02] cat 'Debian Desktop - 02.vmdk'
# Disk DescriptorFile
version=1
encoding="UTF-8"
CID=018c325a
parentCID=ffffffff
createType="vmfs"

# Extent description
RW 209715200 VMFS "Debian Desktop - 02-flat.vmdk"

# The Disk Data Base
#DDB

ddb.adapterType = "buslogic"
ddb.deletable = "true"
ddb.geometry.cylinders = "13054"
ddb.geometry.heads = "255"
ddb.geometry.sectors = "63"
ddb.longContentID = "f1d4fc7a314c3e2b047794eb018c325a"
ddb.thinProvisioned = "1"
ddb.toolsInstallType = "4"
ddb.toolsVersion = "12352"
ddb.uuid = "60 00 C2 91 5c 00 a6 1e-89 32 0d de 86 e7 c2 0d"
ddb.virtualHWVersion = "14"

Notice that it shows the new name of the file, so this file was successfully uploaded. Let's check the .vmx file:

[root@esxi-ntg:/vmfs/volumes/64b6cb2a-91d9a1d5-800b-005056902e9a/Debian Desktop - 02] cat 'Debian Desktop - 02.vmx'
.encoding = "UTF-8"
config.version = "8"
virtualHW.version = "20"
vmci0.present = "TRUE"
floppy0.present = "FALSE"
svga.vramSize = "268435456"
numvcpus = "4"
memSize = "4096"
bios.bootRetry.delay = "10"
powerType.suspend = "soft"
tools.upgrade.policy = "manual"
sched.cpu.units = "mhz"
sched.cpu.affinity = "all"
vm.createDate = "1689709598501053"
scsi0.virtualDev = "pvscsi"
scsi0.present = "TRUE"
sata0.present = "TRUE"
usb.present = "TRUE"
ehci.present = "TRUE"
scsi0:0.deviceType = "scsi-hardDisk"
scsi0:0.fileName = "Debian Desktop - 02.vmdk"
sched.scsi0:0.shares = "normal"
sched.scsi0:0.throughputCap = "off"
scsi0:0.present = "TRUE"
ethernet0.virtualDev = "vmxnet3"
ethernet0.networkName = "VLAN200-Business"
ethernet0.addressType = "generated"
ethernet0.wakeOnPcktRcv = "FALSE"
ethernet0.uptCompatibility = "TRUE"
ethernet0.present = "TRUE"
displayName = "Debian Server - 02"
guestOS = "debian12-64"
toolScripts.afterPowerOn = "TRUE"
toolScripts.afterResume = "TRUE"
toolScripts.beforeSuspend = "TRUE"
toolScripts.beforePowerOff = "TRUE"
tools.syncTime = "FALSE"
uuid.bios = "56 4d d6 f4 b8 53 be 03-ec 2c 98 cb ae 7a b6 03"
uuid.location = "56 4d d6 f4 b8 53 be 03-ec 2c 98 cb ae 7a b6 03"
vc.uuid = "52 0b 71 c4 41 49 dd 21-af ac e6 8d 81 52 62 87"
sched.cpu.min = "0"
sched.cpu.shares = "normal"
sched.mem.min = "0"
sched.mem.minSize = "0"
sched.mem.shares = "normal"
ethernet0.generatedAddress = "00:0c:29:7a:b6:03"
vmci0.id = "-657187315"
cleanShutdown = "TRUE"
cpuid.coresPerSocket = "1"
tools.guest.desktop.autolock = "TRUE"
nvram = "Debian Desktop - 02.nvram"
svga.present = "TRUE"
pciBridge0.present = "TRUE"
pciBridge4.present = "TRUE"
pciBridge4.virtualDev = "pcieRootPort"
pciBridge4.functions = "8"
pciBridge5.present = "TRUE"
pciBridge5.virtualDev = "pcieRootPort"
pciBridge5.functions = "8"
pciBridge6.present = "TRUE"
pciBridge6.virtualDev = "pcieRootPort"
pciBridge6.functions = "8"
pciBridge7.present = "TRUE"
pciBridge7.virtualDev = "pcieRootPort"
pciBridge7.functions = "8"
hpet0.present = "TRUE"
RemoteDisplay.maxConnections = "-1"
sched.cpu.latencySensitivity = "normal"
vmxstats.filename = "Debian Desktop - 02.scoreboard"
numa.autosize.cookie = "40012"
numa.autosize.vcpu.maxPerVirtualNode = "4"
sched.swap.derivedName = "/vmfs/volumes/64b6cb2a-91d9a1d5-800b-005056902e9a/Debian Desktop - 02/Debian Desktop - 02-b2f95b67.vswp"
pciBridge0.pciSlotNumber = "17"
pciBridge4.pciSlotNumber = "21"
pciBridge5.pciSlotNumber = "22"
pciBridge6.pciSlotNumber = "23"
pciBridge7.pciSlotNumber = "24"
scsi0.pciSlotNumber = "160"
usb.pciSlotNumber = "32"
ethernet0.pciSlotNumber = "192"
ehci.pciSlotNumber = "33"
sata0.pciSlotNumber = "34"
scsi0.sasWWID = "50 05 05 64 b8 53 be 00"
vmotion.checkpointFBSize = "4194304"
vmotion.checkpointSVGAPrimarySize = "268435456"
vmotion.svga.mobMaxSize = "268435456"
vmotion.svga.graphicsMemoryKB = "262144"
ethernet0.generatedAddressOffset = "0"
monitor.phys_bits_used = "45"
softPowerOff = "TRUE"
usb:1.speed = "2"
usb:1.present = "TRUE"
usb:1.deviceType = "hub"
usb:1.port = "1"
usb:1.parent = "-1"
svga.guestBackedPrimaryAware = "TRUE"
guestInfo.detailed.data = "architecture='X86' bitness='64' distroAddlVersion='12 (bookworm)' distroName='Debian GNU/Linux' distroVersion='12' familyName='Linux' kernelVersion='6.1.0-10-amd64' prettyName='Debian GNU/Linux 12 (bookworm)'"
tools.remindInstall = "FALSE"
migrate.hostLog = "./Debian Desktop - 02-e139194a.hlog"
scsi0:0.redo = ""
usb:0.present = "TRUE"
usb:0.deviceType = "hid"
usb:0.port = "0"
usb:0.parent = "-1"

It seems that this file is also OK now, so let's go and import our new/old VM into ESXi.

Importing the cloned VM to ESXi

Click on Virtual Machines in the menu on the left side of the screen, then click on Create / Register VM and select Register an existing virtual machine:

Click NEXT and in the next window click on SELECT:

Datastore browser window will open, so position yourself in the directory of your new VM and select the .vmx file and click on SELECT after that:

This will take you back to the previous window, with the newly selected VM in the list. Click on NEXT:

Finally, your new VM is ready to be imported:

Click on FINISH to finalise the process.

Your new VM should show up on the list of VMs and you should be able to start it by clicking on the Power on button at the top.

A message will appear immediately after you press on Power on button:

As you can see, it asks you what you actually did with the VM, and by default I Copied It is selected. Since we made changes in all the necessary files, we can actually select I Moved It, as there are no changes necessary to be made:

If you didn't follow this whole procedure and jumped to the bottom of this post immediately after copying the .vmx and .vmdk files at the beginning, make sure to choose I Copied It option, as this will allow ESXi to make necessary changes to the .vmx and .vmdk files - although, if you go back to CLI, you will notice that the naming convention is still using old names from the source VM, just the references in the files have changed.

One last thing - when your new VM has started, make sure to change the IP address (if assigned statically) and the name of the VM.

And this is the end of this article. Hopefully you find it useful, and if you have any questions or comments, leave them in the comments section.