Proxmox VEXXVI · Windows GuestsFleets of Windows guests
Windows templates and sysprep
What you'll learn
- State what sysprep /generalize removes and why Microsoft does not support duplication without it
- Build a Proxmox Windows template using audit mode, an answer file and qm template
- Choose between /mode:vm and a full generalize, and know the constraint /mode:vm imposes
- Explain the failures a non-generalised clone produces and how long they stay hidden
- Configure cloudbase-init against the Proxmox Cloud-Init drive, including the citype default that ostype selects
Prerequisites
Verified against Proxmox VE 9.2.4 · Proxmox Backup Server 4.2.5 · Ceph Squid / Tentacle · Debian 13 (Trixie) · Linux kernel 7.0 (PVE 9.2 default) · 2026-08-12
Cloning a Linux guest is a rename, a new SSH host key, a new machine-id and a new IP. Cloud-Init does all four before the first login prompt appears, and if you forget, you get a duplicate hostname and an SSH warning, both of which announce themselves immediately.
Cloning a Windows guest without generalising it produces machines that are externally distinct — different names, different addresses — and internally identical. Nothing warns you. The estate works. Then, somewhere between three weeks and two years later, patching stops reporting, or activation collapses, or a domain join starts failing for reasons that make no sense, and the common cause is a template built in an afternoon by someone who cloned a working server.
What Microsoft actually says
This is not a best practice. It is a support boundary, and the wording in the sysprep command-line reference is unusually direct:
Moving or copying a Windows image to a different computer without running the
Sysprep /generalizecommand is not supported. You must use theSysprep /generalizecommand to generalize a complete Windows installation before you can use the installation for deployment to a new computer, whether you use imaging, hard disk duplication, or another method.
“Hard disk duplication, or another method” includes qm clone. A Proxmox full
clone or linked clone is hard disk duplication with a new VMID attached.
And the reference is equally clear that identical hardware does not exempt you:
If you intend to create an image of an installation for deployment to a different computer, you must run the Sysprep command together with the /generalize option, even if the other computer has the same hardware configuration.
That sentence exists because the objection it answers is so natural. Every clone of a Proxmox template has exactly the same virtual hardware. It makes no difference: the things sysprep removes are not hardware-derived.
What /generalize removes
Three items, given verbatim:
Prepares the Windows installation to be imaged. Sysprep removes all unique system information from the Windows installation. Sysprep resets the security ID (SID), clears any system restore points, and deletes event logs.
The next time that the computer starts, the specialize configuration pass runs. The configuration pass creates a new security ID (SID).
The SID is the one that matters, and the reason it matters is not the SID itself. It is everything derived from it.
| Derived from the machine identity | What a duplicate looks like | How long it stays hidden |
|---|---|---|
| Machine SID | Local accounts and groups on every clone share RIDs | Indefinitely, until something enumerates across machines |
| Computer name | A name conflict on the network, or a rename that leaves stale references | Days |
| WSUS/Update client identifier | Clones collapse into one entry in the update server; patch compliance is fiction | Weeks — until an audit |
| KMS client machine identifier | The KMS host counts many machines as one, and the activation count never reaches its threshold | Weeks to months |
| Remote Desktop self-signed certificate | Every clone presents the same RDP certificate | Until somebody looks |
| Domain machine account material | Rejoin and trust failures that read as network faults | Variable, and always at the worst time |
The middle two are the ones that cost money and reputation. Patch compliance reporting that silently covers a fraction of the estate is worse than no reporting, because it is believed. And a KMS host that never reaches its activation threshold produces an estate of machines in grace periods, all expiring around the same time.
Building the template
The workflow has five phases. Phase four is the one that is easy to get wrong.
1. Build the base
Install Windows following Lesson 1 — win11 or win10 ostype, q35, OVMF,
virtio-scsi-single, and the storage driver loaded during setup. Install the
VirtIO bundle, the guest agent and the balloon service from Lesson 2. Patch
fully. Do not join the domain. A template should never carry a machine
account.
2. Customise in audit mode
Audit mode exists exactly for this. It bypasses OOBE so you reach a desktop without creating a user account, and you can install applications and drivers there:
Audit mode enables you to add customizations to Windows images. Audit mode does not require that you apply settings in OOBE. By bypassing OOBE, you can access the desktop more quickly and perform your customizations.
Enter it with Sysprep /audit /reboot, or configure it in an answer file with
the reseal mode setting. Everything you install in audit mode survives
generalisation.
3. Write the answer file
An unattend.xml is what turns a template into unattended provisioning. It is
processed in configuration passes; the two you care about for a Proxmox template
are:
specialize— runs on first boot after generalisation, on the new machine. Computer name, domain join, product key. The reference notes that to activate automatically you specify a valid product key in the Microsoft-Windows-Shell-SetupProductKeysetting during this pass.oobeSystem— runs immediately before OOBE. Locale, time zone, administrator password, skipping the OOBE screens.
Build it with Windows System Image Manager against your exact install media, not by editing a file from a blog post — the schema is version-specific and a setting that does not apply to your image is silently ignored, which is the worst possible failure mode for an answer file.
4. Generalise
# Full generalize, using an answer file, then shut down
C:\Windows\System32\Sysprep\Sysprep.exe /generalize /oobe /shutdown /quiet /unattend:C:\Windows\System32\Sysprep\unattend.xml/quiet is not optional on Server Core. The reference is emphatic: you must run
sysprep from the command line and specify /quiet on a Server Core
installation, “Otherwise, the Sysprep UI won’t show, and the process will fail
silently.”
There is a second mode worth knowing about, and a constraint that comes with it:
/mode:vm— Generalizes a Virtual Hard Disk (VHD) so that you can deploy the VHD as a VHD on the same Virtual Machine (VM) or hypervisor. After the VM restarts, the VM can boot to OOBE.The only additional switches that apply to VM mode are
/reboot,/shutdown, and/quit. You must deploy the VHD on a Virtual Machine (VM) or hypervisor with the same hardware profile. […] Deploying the VHD to a different VM with a different hardware profile might cause unexpected issues.You can only run VM mode from inside a VM.
/mode:vm skips hardware re-detection, which makes first boot considerably
faster. The price is the hardware-profile constraint, and for a Proxmox estate
that constraint has teeth: the machine version pinned at creation (Lesson 1) is
part of the hardware profile, and so is the firmware and the disk controller. A
template generalised with /mode:vm on a pc-q35-10.0 OVMF guest is a template
for pc-q35-10.0 OVMF guests.
Use /mode:vm when your estate is genuinely homogeneous and you clone from the
template onto identical configuration. Use a plain /generalize when the
template has to serve several profiles, or when you might move it to another
hypervisor. If you are not sure, use the plain form; the cost is one slower
first boot per clone.
5. Turn it into a Proxmox template
VMID=9100
# The guest must be stopped, and stopped because sysprep shut it down
qm status "$VMID"
# Remove the install ISO and the VirtIO ISO from the template
qm set "$VMID" --delete ide2
qm set "$VMID" --ide0 none,media=cdrom
# Add a Cloud-Init drive; configdrive2 is the default for a Windows ostype
qm set "$VMID" --ide1 local-lvm:cloudinit
qm template "$VMID"Cloud-Init on Windows: cloudbase-init
Part IX covered Cloud-Init for Linux templates. Windows has a reimplementation, and Proxmox supports it directly. From the Cloud-Init support documentation:
The default depends on the configured operating system type (
ostype). We use thenocloudformat for Linux, andconfigdrive2for windows.
So citype has three allowed values — configdrive2, nocloud and
opennebula — and the correct one for Windows is selected automatically by
the ostype you set in Lesson 1. This is the second time that field has paid
for itself: a Windows guest left at ostype: other gets a nocloud drive that
cloudbase-init, configured with its default ConfigDrive service, will not
consume. The symptom is a clone that boots with none of its Cloud-Init settings
applied and no error anywhere.
The pieces:
- Install cloudbase-init in the guest during audit mode, before generalising. Its installer offers to run sysprep at the end with its own unattend file; whether you use that or your own is a matter of taste, but use one or the other, not both.
- Attach a Cloud-Init drive to the template, as in the command block above.
- Set the per-clone values on the clone, not the template:
TEMPLATE=9100
NEWID=151
qm clone "$TEMPLATE" "$NEWID" --name app-win-01 --full 1
qm set "$NEWID" \
--ciuser Administrator \
--ipconfig0 ip=192.0.2.61/24,gw=192.0.2.1 \
--nameserver 192.0.2.10 \
--searchdomain example.com
# Inspect exactly what the guest will be handed, before starting it
qm cloudinit dump "$NEWID" user
qm cloudinit dump "$NEWID" networkqm cloudinit dump is the verification step that can fail. It shows the
generated configuration for user, network or meta without starting the
guest, so a wrong gateway or a missing nameserver is visible before first boot
rather than after a console session.
Key takeaways
- Microsoft does not support moving or copying a Windows image to a different
computer without
Sysprep /generalize, explicitly including hard disk duplication, and explicitly including identical hardware.qm cloneis duplication. /generalizeresets the SID, clears restore points and deletes event logs. The new SID is minted by thespecializepass on first boot, which is why the generalised image is safe to copy.- Non-generalised clones fail late and quietly: update-client collapse, KMS counts that never reach threshold, and patch compliance reports that are believed.
/mode:vmis faster but binds the image to a hardware profile — which on Proxmox includes the pinned machine version, firmware and controller.qm templateis one-way. Keep a pre-sysprep VM so that iterating on a template does not consume the rearm counter and does not require a rebuild.- Windows Cloud-Init means cloudbase-init, and Proxmox selects
configdrive2automatically from a Windowsostype. A guest left atostype: othergets anoclouddrive and silently applies nothing.
Knowledge check
Knowledge check · 4 questions
Q1. A colleague argues that sysprep is unnecessary for a Proxmox template because every clone gets identical virtual hardware, a unique name and a unique domain account. What is the strongest correct response?
Q2. Ninety Windows servers were cloned over six months from a VM that was never generalised. The update server shows twelve clients. What is the correct assessment of the remediation?
Q3. Which of these belong in a Windows template before it is generalised? Select all that apply.
Q4. On Proxmox, the Cloud-Init format handed to a Windows guest is selected automatically from the ostype field, and a Windows guest left at ostype other will receive a format cloudbase-init does not consume by default.
Passing score: 75%. Answers are checked in this browser.