I once spent an entire morning setting up Windows machines one by one for the dev team — copying ISOs, reinstalling from scratch, activating, installing drivers, configuring timezones… After that experience, I decided to do it once and reuse it forever with VM Templates and Sysprep.
I run a homelab with Proxmox VE managing 12 VMs and containers — it’s my playground for testing everything before pushing to production. This template workflow saves an enormous amount of time: cloning a clean Windows VM takes under 2 minutes, and the hostname and SID are freshly generated every time.
Quick Start: Create a Template in 5 Steps
This is the core flow — get this down and you can do it immediately:
- Create a new VM → install Windows from ISO
- Install VirtIO drivers (storage, network, balloon, guest agent)
- Run Sysprep with the Generalize + Shutdown option
- After the VM shuts down → Right-click → Convert to Template
- Clone when needed → fresh new VM with a newly generated SID
Prepare two things before you start:
- Windows ISO: Windows 10/11 or Windows Server
- VirtIO ISO: download
virtio-win.isofrom the Fedora repo (stable release)
Creating the Base VM for the Template
Optimal VM Configuration from the Start
When creating a VM in Proxmox, a few choices have long-term impact on your template:
- Machine type: choose
q35(PCIe support, better than i440fx) - BIOS: OVMF (UEFI) for Secure Boot; SeaBIOS for legacy compatibility
- Storage controller: VirtIO Block or SCSI with VirtIO SCSI single
- Network: VirtIO (paravirtualized) — best throughput
- CPU:
hostorx86-64-v2-AESfor optimal performance
One important note: don’t set RAM/CPU too high in the template. You’ll adjust those per use case when cloning — the template only needs enough to install Windows and run Sysprep.
Attach Both ISOs to the VM at Once
Before installing Windows, attach both ISOs to the VM:
VM → Hardware → Add → CD/DVD Drive
IDE 0: Windows.iso (primary boot drive)
IDE 2: virtio-win.iso (secondary CD/DVD Drive)
If you’re using a VirtIO storage controller, the Windows installer won’t see the hard disk when selecting an installation location. At that point, click Load driver and point it to the VirtIO disc: viostor\amd64\w10 (or w11/2k22 depending on your Windows version).
Installing VirtIO Drivers: A Step You Cannot Skip
Many people skip this step and then find their cloned VM has no network connection or runs unusually slow. After Windows finishes installing, opening Device Manager will reveal a pile of devices with missing drivers — those are VirtIO devices that Windows hasn’t recognized yet.
Install All Drivers at Once with Guest Tools
The fastest approach: run the all-in-one installer from the VirtIO disc:
D:\virtio-win-guest-tools.exe
This single file installs everything: balloon driver, network adapter (NetKVM), storage controller (viostor/vioscsi), memory ballooning, QEMU guest agent, serial driver… One shot, no need to install each component separately.
Enable QEMU Guest Agent in Proxmox
After installing the guest tools, go to Proxmox → VM → Options → QEMU Guest Agent → Enable. This feature lets Proxmox read the VM’s IP address, perform a clean shutdown from the host, and freeze the filesystem during backups — essential for production environments.
# Check whether the guest agent service is running
Get-Service QEMU-GA
Sysprep: Creating the “Mold” for Every Cloned VM
Sysprep is Microsoft’s tool for generalizing Windows — it strips machine-specific information (SID, hostname, activation state, event logs, etc.) so that each clone is a completely fresh identity. Without this step, cloning 10 machines still leaves them all with the same SID — domain joins will fail or cause conflicts.
Quick Method: GUI Sysprep
C:\Windows\System32\Sysprep\sysprep.exe
Select the following options:
- System Cleanup Action: Enter System Out-of-Box Experience (OOBE)
- Generalize: ✅ must be checked
- Shutdown Options: Shutdown
Once Sysprep finishes, the VM shuts down automatically. At this point, do not boot the VM again — convert it to a template immediately.
Automation Method: Unattend.xml
If you want to skip the OOBE entirely (the initial setup screen after cloning), create an unattend.xml file:
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral" versionScope="nonSxS">
<TimeZone>Tokyo Standard Time</TimeZone>
</component>
</settings>
<settings pass="oobeSystem">
<component name="Microsoft-Windows-Shell-Setup"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral" versionScope="nonSxS">
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideLocalAccountSetupPage>true</HideLocalAccountSetupPage>
<ProtectYourPC>3</ProtectYourPC>
<SkipMachineOOBE>true</SkipMachineOOBE>
</OOBE>
</component>
</settings>
</unattend>
Place this file at C:\Windows\System32\Sysprep\unattend.xml, then run Sysprep from the command line:
C:\Windows\System32\Sysprep\sysprep.exe /oobe /generalize /shutdown /unattend:unattend.xml
Converting the VM to a Template and Cloning
# From the Proxmox Shell — use your actual VM ID
qm template 101
# Or via the Web UI:
# Right-click VM → Convert to Template
The VM switches to a different icon (a document/page icon) and can no longer be started directly — it can only be cloned. When you need to deploy:
# Full clone — completely independent VM (recommended for production)
qm clone 101 201 --name win10-dev-01 --full true --storage local-lvm
# Linked clone — faster, saves disk space but depends on the template
qm clone 101 202 --name win10-test-01
Advanced: Batch Deployment Automation with Python
When you need to deploy 5–10 VMs at once, use the Proxmox API instead of clicking through the UI:
import requests
import urllib3
urllib3.disable_warnings()
PVE_HOST = "https://proxmox.local:8006"
TOKEN_ID = "root@pam!deploy"
TOKEN_SECRET = "your-api-token-secret"
headers = {
"Authorization": f"PVEAPIToken={TOKEN_ID}={TOKEN_SECRET}"
}
def clone_vm(template_id: int, new_id: int, name: str, storage: str = "local-lvm"):
url = f"{PVE_HOST}/api2/json/nodes/pve/qemu/{template_id}/clone"
data = {
"newid": new_id,
"name": name,
"full": 1,
"storage": storage,
}
resp = requests.post(url, headers=headers, json=data, verify=False)
resp.raise_for_status()
return resp.json()
# Deploy 5 VMs in parallel
for i in range(1, 6):
result = clone_vm(101, 200 + i, f"win10-worker-{i:02d}")
print(f"Cloning VM {200 + i}: task {result['data']}")
Create an API token in Proxmox under: Datacenter → API Tokens → Add. Use tokens instead of passwords — you can scope permissions tightly and revoke them easily.
Practical Tips from Daily Use
1. Keep the Template Lean
Only install what every VM will need: VirtIO drivers, QEMU Guest Agent, and essential Windows Updates. VM-specific software (Visual Studio, SQL Server, IIS, etc.) should be installed after cloning — don’t bloat the template.
2. Use Versioned Template Names
win10-22h2-virtio-v1-template
win2022-dc-virtio-v2-template
When you rebuild the template (after a major patch cycle), keep the old version around. Rolling back is easy if the new version has issues.
3. Take a “pre-sysprep” Snapshot Before Generalizing
# Take a snapshot before running Sysprep
qm snapshot 101 pre-sysprep --description "Drivers installed, before sysprep"
If Sysprep fails or you want to add something to the template, roll back to this snapshot — no need to reinstall from scratch.
4. Run Windows Update Before Sysprep
Apply all patches, reboot as many times as needed, then run Sysprep. A template that’s already up to date means cloned VMs need fewer updates — a significant time saver when deploying multiple machines at once.
5. Test a Clone Before Going Live
Clone one VM and boot it up to verify everything before using the template in production:
# Check the SID — it must be DIFFERENT from the original template's SID
whoami /user
# Verify the hostname has changed
hostname
# Confirm the network adapter has the VirtIO driver
Get-NetAdapter
I once forgot to check Generalize in Sysprep — cloned 10 machines all sharing the same SID, every domain join failed. It cost me half a day to sort out. Test thoroughly once, save yourself many headaches later.
6. Linked Clones for Labs, Full Clones for Production
Linked clones are created in seconds and save disk space — great for temporary test VMs in a homelab. Full clones are completely independent — use them for production or any VM that needs to persist long-term. Never use linked clones in production: deleting the template wipes out every VM cloned from it.

