Windows

Create Windows Server 2025 Template in Proxmox VE

Proxmox VE makes it straightforward to run Windows Server workloads alongside Linux VMs. But spinning up a fresh Windows installation every time you need a test environment is painful. The installer alone takes 20+ minutes, then you’re stuck clicking through OOBE screens, installing drivers, and configuring services. A better approach is to build a reusable Windows Server 2025 template with VirtIO drivers, the QEMU guest agent, and Cloudbase-Init pre-installed. Clone it whenever you need a new VM, and you’re booted into a working desktop in under two minutes.

Original content from computingforgeeks.com - post 164194

This guide walks through creating a Windows Server 2025 template on Proxmox VE, starting from the ISO download all the way through sysprep and template conversion. The unattended installation method covered here means zero manual clicks during the Windows setup process.

Prerequisites

  • Proxmox VE 8.x host with at least 100GB free storage
  • Internet access on the Proxmox host (for downloading ISOs)
  • Minimum resources for the template VM: 4 CPU cores, 8GB RAM, 64GB disk
  • Root SSH access to the Proxmox node

Step 1: Download the Required ISOs

Two ISOs are needed: the Windows Server 2025 installation media and the VirtIO drivers for paravirtualized hardware. The VirtIO drivers give Windows proper support for the virtual disk controller, network adapter, and display, which results in significantly better performance than emulated hardware.

SSH into your Proxmox node and download both ISOs to the default template directory.

cd /var/lib/vz/template/iso/
wget -O windows-server-2025-eval.iso 'https://go.microsoft.com/fwlink/?linkid=2345730&clcid=0x409&culture=en-us&country=us'

The Windows Server 2025 evaluation ISO is roughly 7.6GB and provides a 180-day trial. If you have a licensed ISO from your Volume Licensing portal, use that instead.

Download the VirtIO drivers ISO from the Fedora project.

wget -O virtio-win.iso 'https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso'

Confirm both files are present and complete.

ls -lh /var/lib/vz/template/iso/ | grep -E "windows|virtio"

You should see both files listed with their full sizes.

-rw-r--r-- 1 root root 754M Sep 12  2025 virtio-win.iso
-rw-r--r-- 1 root root 7.6G Jan 20 16:56 windows-server-2025-eval.iso

Step 2: Create the Autounattend File

Windows supports fully automated installations through an XML answer file called autounattend.xml. When the installer boots, it scans all attached drives for this file and uses it to skip every interactive prompt: language selection, edition choice, disk partitioning, EULA acceptance, and the OOBE wizard.

The answer file also loads VirtIO drivers during the Windows PE phase, which is critical because Windows cannot see the virtual disk without the VirtIO SCSI driver.

Create a directory for the answer file and write the XML.

mkdir -p /tmp/winunattend

Create the autounattend.xml file with a text editor.

vi /tmp/winunattend/autounattend.xml

Add the following content. Replace YOUR_PASSWORD with your desired Administrator password. The password appears in two places (AdministratorPassword and AutoLogon).

<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">

  <settings pass="windowsPE">
    <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
      <SetupUILanguage>
        <UILanguage>en-US</UILanguage>
      </SetupUILanguage>
      <InputLocale>en-US</InputLocale>
      <SystemLocale>en-US</SystemLocale>
      <UILanguage>en-US</UILanguage>
      <UserLocale>en-US</UserLocale>
    </component>

    <component name="Microsoft-Windows-PnpCustomizationsWinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
      <DriverPaths>
        <PathAndCredentials wcm:action="add" wcm:keyValue="1">
          <Path>E:\vioscsi\2k25\amd64</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="2">
          <Path>E:\NetKVM\2k25\amd64</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="3">
          <Path>E:\viostor\2k25\amd64</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="4">
          <Path>E:\qxldod\2k25\amd64</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="5">
          <Path>E:\Balloon\2k25\amd64</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="6">
          <Path>E:\vioserial\2k25\amd64</Path>
        </PathAndCredentials>
      </DriverPaths>
    </component>

    <component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
      <DiskConfiguration>
        <Disk wcm:action="add">
          <DiskID>0</DiskID>
          <WillWipeDisk>true</WillWipeDisk>
          <CreatePartitions>
            <CreatePartition wcm:action="add">
              <Order>1</Order>
              <Size>100</Size>
              <Type>EFI</Type>
            </CreatePartition>
            <CreatePartition wcm:action="add">
              <Order>2</Order>
              <Size>16</Size>
              <Type>MSR</Type>
            </CreatePartition>
            <CreatePartition wcm:action="add">
              <Order>3</Order>
              <Extend>true</Extend>
              <Type>Primary</Type>
            </CreatePartition>
          </CreatePartitions>
          <ModifyPartitions>
            <ModifyPartition wcm:action="add">
              <Order>1</Order>
              <PartitionID>1</PartitionID>
              <Format>FAT32</Format>
              <Label>EFI</Label>
            </ModifyPartition>
            <ModifyPartition wcm:action="add">
              <Order>2</Order>
              <PartitionID>2</PartitionID>
            </ModifyPartition>
            <ModifyPartition wcm:action="add">
              <Order>3</Order>
              <PartitionID>3</PartitionID>
              <Format>NTFS</Format>
              <Label>Windows</Label>
            </ModifyPartition>
          </ModifyPartitions>
        </Disk>
      </DiskConfiguration>
      <ImageInstall>
        <OSImage>
          <InstallTo>
            <DiskID>0</DiskID>
            <PartitionID>3</PartitionID>
          </InstallTo>
          <InstallFrom>
            <MetaData wcm:action="add">
              <Key>/IMAGE/NAME</Key>
              <Value>Windows Server 2025 Standard Evaluation (Desktop Experience)</Value>
            </MetaData>
          </InstallFrom>
        </OSImage>
      </ImageInstall>
      <UserData>
        <ProductKey>
          <WillShowUI>OnError</WillShowUI>
          <Key>MFY9F-XBN2F-TYFMP-CCV49-RMYVH</Key>
        </ProductKey>
        <AcceptEula>true</AcceptEula>
      </UserData>
    </component>
  </settings>

  <settings pass="specialize">
    <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
      <ComputerName>WIN2025-TPL</ComputerName>
      <TimeZone>UTC</TimeZone>
    </component>
    <component name="Microsoft-Windows-TerminalServices-LocalSessionManager" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
      <fDenyTSConnections>false</fDenyTSConnections>
    </component>
    <component name="Networking-MPSSVC-Svc" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
      <FirewallGroups>
        <FirewallGroup wcm:action="add" wcm:keyValue="RemoteDesktop">
          <Active>true</Active>
          <Group>Remote Desktop</Group>
          <Profile>all</Profile>
        </FirewallGroup>
      </FirewallGroups>
    </component>
  </settings>

  <settings pass="oobeSystem">
    <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
      <OOBE>
        <HideEULAPage>true</HideEULAPage>
        <HideLocalAccountScreen>true</HideLocalAccountScreen>
        <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
        <HideOnlineAccountScreens>true</HideOnlineAccountScreens>
        <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
        <ProtectYourPC>3</ProtectYourPC>
        <SkipMachineOOBE>true</SkipMachineOOBE>
        <SkipUserOOBE>true</SkipUserOOBE>
      </OOBE>
      <UserAccounts>
        <AdministratorPassword>
          <Value>YOUR_PASSWORD</Value>
          <PlainText>true</PlainText>
        </AdministratorPassword>
      </UserAccounts>
      <AutoLogon>
        <Enabled>true</Enabled>
        <Username>Administrator</Username>
        <Password>
          <Value>YOUR_PASSWORD</Value>
          <PlainText>true</PlainText>
        </Password>
        <LogonCount>1</LogonCount>
      </AutoLogon>
    </component>
    <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
      <InputLocale>en-US</InputLocale>
      <SystemLocale>en-US</SystemLocale>
      <UILanguage>en-US</UILanguage>
      <UserLocale>en-US</UserLocale>
    </component>
  </settings>
</unattend>

A few things to note about this answer file. The IMAGE/NAME value must match the edition name on the ISO exactly. For evaluation ISOs, the name includes “Evaluation,” so using “Windows Server 2025 Standard (Desktop Experience)” without it will cause the installer to stop and wait for manual selection. The VirtIO driver paths reference E:\ because with three IDE CD-ROM drives attached, the VirtIO ISO gets assigned drive letter E.

The product key MFY9F-XBN2F-TYFMP-CCV49-RMYVH is the public KMS client key for Windows Server 2025 Standard Evaluation. It activates the 180-day evaluation period automatically.

Package the answer file into a small ISO that will be mounted as a CD-ROM drive on the VM.

genisoimage -o /var/lib/vz/template/iso/autounattend.iso -J -r /tmp/winunattend/

Step 3: Create the Proxmox VM

Windows Server 2025 requires UEFI boot with Secure Boot and TPM 2.0. The q35 machine type, OVMF BIOS, and pre-enrolled-keys on the EFI disk handle all of this. The ostype win11 setting enables Hyper-V enlightenments that improve Windows performance under KVM.

qm create 900 \
  --name win2025-template \
  --ostype win11 \
  --machine q35 \
  --bios ovmf \
  --efidisk0 local-lvm:1,efitype=4m,pre-enrolled-keys=1 \
  --tpmstate0 local-lvm:1,version=v2.0 \
  --cpu host \
  --cores 4 \
  --memory 8192 \
  --balloon 0 \
  --scsihw virtio-scsi-single \
  --scsi0 local-lvm:64,iothread=1,discard=on \
  --ide0 local:iso/windows-server-2025-eval.iso,media=cdrom \
  --ide1 local:iso/autounattend.iso,media=cdrom \
  --ide2 local:iso/virtio-win.iso,media=cdrom \
  --net0 virtio,bridge=vmbr0 \
  --boot order='ide0;scsi0' \
  --agent enabled=1 \
  --vga qxl

Here is what each setting does.

SettingPurpose
--ostype win11Enables Windows-specific optimizations (Hyper-V enlightenments, correct ACPI tables)
--machine q35Modern chipset required for UEFI, TPM, and Secure Boot
--bios ovmfUEFI firmware (required for Windows Server 2025)
--efidisk0 ... pre-enrolled-keys=1EFI disk with Microsoft Secure Boot keys pre-loaded
--tpmstate0 ... version=v2.0Virtual TPM 2.0 module (required by Windows Server 2025)
--balloon 0Disable memory ballooning (Windows handles it poorly)
--scsihw virtio-scsi-singleVirtIO SCSI controller with per-disk IO threads
--agent enabled=1Creates the virtio-serial channel for QEMU guest agent communication
--vga qxlQXL display adapter (good for SPICE and VirtIO display driver)

Step 4: Start the VM and Install Windows

Start the VM. The UEFI firmware will briefly display a “Press any key to boot from CD or DVD” prompt. This prompt only lasts about 5 seconds, so you need to press a key quickly either through the Proxmox console or programmatically.

qm start 900

If you are automating this via CLI, you can send keypresses through the QEMU monitor to catch the boot prompt.

sleep 5
for i in $(seq 1 10); do
  pvesh create /nodes/$(hostname)/qemu/900/monitor --command 'sendkey ret' 2>/dev/null
  sleep 1
done

Once the installer boots, the autounattend.xml takes over. The entire installation runs hands-free: it loads VirtIO drivers, partitions the disk with a GPT/EFI layout, installs the Desktop Experience edition, sets the Administrator password, enables Remote Desktop, and skips all OOBE screens.

The installation takes 15 to 25 minutes depending on your storage speed. The VM will reboot once or twice during the process. You can monitor progress by taking console screenshots.

Windows Server 2025 installation progress at 23 percent on Proxmox VM
pvesh create /nodes/$(hostname)/qemu/900/monitor --command 'screendump /tmp/vm-screen.ppm'

When the installation is complete, you will see the Server Manager dashboard on the desktop, confirming Windows Server 2025 Desktop Experience is fully installed and auto-logged in as Administrator.

Windows Server 2025 Server Manager dashboard after successful installation on Proxmox VE

Step 5: Install VirtIO Guest Tools and QEMU Agent

The autounattend file loaded the basic VirtIO drivers (storage, network, display) during installation, but two additional components should be installed for full Proxmox integration: the VirtIO guest tools package and the QEMU guest agent.

Connect to the VM via RDP or the Proxmox console and open an elevated Command Prompt or PowerShell window. The VirtIO ISO is still mounted as drive E:.

Install the VirtIO guest tools (includes the balloon driver, serial driver, and display driver).

msiexec /i E:\virtio-win-gt-x64.msi /quiet /norestart
Installing VirtIO guest tools via msiexec on Windows Server 2025 in Proxmox

Install the QEMU guest agent, which allows Proxmox to query the VM for IP addresses, run commands, and perform graceful shutdowns.

msiexec /i E:\guest-agent\qemu-ga-x86_64.msi /quiet /norestart

Reboot to ensure all drivers and services load properly.

shutdown /r /t 0

After the reboot, verify the QEMU guest agent is running from the Proxmox host.

qm agent 900 ping

If the agent is working, this command returns silently with no error. You can also query the VM’s network interfaces.

qm agent 900 network-get-interfaces

Step 6: Enable OpenSSH Server (Optional)

Windows Server 2025 includes OpenSSH as an optional feature. Enabling it allows you to manage cloned VMs over SSH without needing RDP or the Proxmox console, which is especially useful for scripted deployments.

Open PowerShell on the VM and install the OpenSSH server feature.

Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0

The output should show Online: True and RestartNeeded: False.

PowerShell showing OpenSSH Server feature installed successfully on Windows Server 2025

Start the SSH service and set it to start automatically.

Start-Service sshd
Set-Service -Name sshd -StartupType Automatic

Add a firewall rule to allow inbound SSH connections.

New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22

By default, Windows OpenSSH uses a special authorized_keys file for administrator accounts that prevents password authentication from working. To enable password-based SSH login for the Administrator account, comment out the override in the SSH config.

$config = Get-Content "C:\ProgramData\ssh\sshd_config"
$config = $config -replace "^Match Group administrators", "#Match Group administrators"
$config = $config -replace "^\s*AuthorizedKeysFile __PROGRAMDATA__", "#AuthorizedKeysFile __PROGRAMDATA__"
Set-Content "C:\ProgramData\ssh\sshd_config" $config
Restart-Service sshd

Step 7: Install Cloudbase-Init

Cloudbase-Init is the Windows equivalent of cloud-init. It reads metadata from Proxmox’s cloud-init drive and applies network configuration, hostname, and user settings to cloned VMs automatically. Without it, every clone boots with the same hostname and DHCP network settings.

Download and install Cloudbase-Init silently from PowerShell.

Invoke-WebRequest -Uri "https://github.com/cloudbase/cloudbase-init/releases/download/1.1.6/CloudbaseInitSetup_1_1_6_x64.msi" -OutFile "C:\Users\Administrator\Downloads\CloudbaseInit.msi" -UseBasicParsing
msiexec /i "C:\Users\Administrator\Downloads\CloudbaseInit.msi" /quiet /norestart

Verify the service was installed.

Get-Service cloudbase-init | Format-Table Name, DisplayName, Status, StartType

The service should show as Stopped with Automatic start type. It will run on the next boot after sysprep.

Configure Cloudbase-Init for Proxmox

The default Cloudbase-Init configuration uses the ConfigDrive metadata service, but Proxmox generates cloud-init data in NoCloud format. Update the configuration to use the correct metadata service.

Edit the main configuration file.

notepad "C:\Program Files\Cloudbase Solutions\Cloudbase-Init\conf\cloudbase-init.conf"

Set the following content.

[DEFAULT]
username=Administrator
groups=Administrators
inject_user_password=true
first_logon_behaviour=no
config_drive_raw_hhd=true
config_drive_cdrom=true
config_drive_vfat=true
bsdtar_path=C:\Program Files\Cloudbase Solutions\Cloudbase-Init\bin\bsdtar.exe
mtools_path=C:\Program Files\Cloudbase Solutions\Cloudbase-Init\bin\
verbose=true
debug=true
log_dir=C:\Program Files\Cloudbase Solutions\Cloudbase-Init\log\
log_file=cloudbase-init.log
default_log_levels=comtypes=INFO,suds=INFO,iso8601=WARN,requests=WARN
logging_serial_port_settings=
mtu_use_dhcp_config=true
ntp_use_dhcp_config=true
local_scripts_path=C:\Program Files\Cloudbase Solutions\Cloudbase-Init\LocalScripts\
check_latest_version=false
metadata_services=cloudbaseinit.metadata.services.nocloudservice.NoCloudConfigDriveService
plugins=cloudbaseinit.plugins.common.mtu.MTUPlugin,cloudbaseinit.plugins.common.sethostname.SetHostNamePlugin,cloudbaseinit.plugins.windows.createuser.CreateUserPlugin,cloudbaseinit.plugins.common.setuserpassword.SetUserPasswordPlugin,cloudbaseinit.plugins.common.localscripts.LocalScriptsPlugin,cloudbaseinit.plugins.common.userdata.UserdataPlugin,cloudbaseinit.plugins.windows.extendvolumes.ExtendVolumesPlugin,cloudbaseinit.plugins.common.networkconfig.NetworkConfigPlugin

The key change is metadata_services pointing to NoCloudConfigDriveService instead of the default ConfigDriveService. This tells Cloudbase-Init to read the NoCloud format that Proxmox generates when you configure cloud-init settings on a VM.

Also update the unattend configuration file used during the specialize pass.

notepad "C:\Program Files\Cloudbase Solutions\Cloudbase-Init\conf\cloudbase-init-unattend.conf"

Set the same metadata_services line in this file as well, pointing to NoCloudConfigDriveService.

Step 8: Sysprep and Shut Down

Sysprep generalizes the Windows installation by removing machine-specific data like the SID, computer name, and hardware-specific drivers. This ensures each clone gets unique identifiers when it boots for the first time.

Cloudbase-Init ships with its own Unattend.xml that hooks into the sysprep process. This ensures Cloudbase-Init runs during the specialize and OOBE passes on cloned VMs.

Run sysprep from an elevated command prompt.

C:\Windows\System32\Sysprep\sysprep.exe /generalize /oobe /shutdown /unattend:"C:\Program Files\Cloudbase Solutions\Cloudbase-Init\conf\Unattend.xml"

Sysprep takes a few minutes to complete. The VM will shut down automatically when finished. Wait until the VM status shows “stopped” in Proxmox before proceeding.

qm status 900

Expected output confirming the VM is stopped.

status: stopped

Step 9: Convert to Template

Before converting, remove the installation ISOs and add a cloud-init drive. The cloud-init drive is where Proxmox injects hostname, network, and user configuration when you clone the template.

qm set 900 --delete ide0
qm set 900 --delete ide1
qm set 900 --delete ide2

Add the cloud-init CD-ROM drive.

qm set 900 --ide2 local-lvm:cloudinit

Set the boot order to disk only since the ISOs are removed.

qm set 900 --boot order=scsi0

Convert the VM to a template.

qm template 900

Once converted, the VM icon changes to a template icon in the Proxmox web interface. Template VMs cannot be started directly. They can only be cloned.

Cloning from the Template

Creating a new Windows Server VM from the template takes just a few commands. The full clone copies all disks, so the new VM is completely independent.

qm clone 900 115 --name win2025-dc01 --full true --storage local-lvm

Optionally set cloud-init parameters for the clone. These control the hostname, Administrator password, and network configuration that Cloudbase-Init applies on first boot.

qm set 115 --ciuser Administrator --cipassword 'SecurePass123'
qm set 115 --ipconfig0 'ip=192.168.1.150/24,gw=192.168.1.1' --nameserver 8.8.8.8

Start the clone.

qm start 115

The first boot runs through the sysprep specialize and OOBE passes, which takes 2 to 3 minutes. Cloudbase-Init applies the hostname and network configuration from the cloud-init drive. After that, the VM is ready to use via RDP or the Proxmox console.

Troubleshooting Common Issues

Installer stops at “Select Image” screen

The image name in the autounattend.xml does not match the ISO exactly. Evaluation ISOs include “Evaluation” in the edition name. Check the exact names by booting without the autounattend ISO and noting the options listed. Common correct names are Windows Server 2025 Standard Evaluation (Desktop Experience) and Windows Server 2025 Datacenter Evaluation (Desktop Experience).

Windows Server 2025 Select Image screen showing Standard and Datacenter editions

“Press any key to boot from CD” timeout

The UEFI boot prompt expires quickly. If you miss it, the VM drops to the UEFI shell or shows “No bootable option found.” Stop the VM, start it again, and send keypresses faster. The sendkey loop in Step 4 handles this by sending Enter every second for 10 seconds.

No network after installation

The VirtIO NetKVM driver was not loaded during installation. Verify the driver path in autounattend.xml matches the Windows version folder on the VirtIO ISO. For Server 2025, the folder is 2k25. For Server 2022, use 2k22.

QEMU guest agent not responding after install

Both MSI packages must be installed: virtio-win-gt-x64.msi (drivers) and guest-agent\qemu-ga-x86_64.msi (agent). The agent also requires the VM to be configured with --agent enabled=1, which creates the virtio-serial communication channel. Reboot the VM after installing both packages.

“Guest has not initialized the display (yet)”

This appears when the EFI disk is corrupted or empty. It typically happens if you manually recreate LVM volumes for the EFI or TPM disks. The fix is to destroy the VM completely with qm destroy and recreate it from scratch. Never manually delete and recreate individual VM disks.

Wrapping Up

With this template in place, spinning up a new Windows Server 2025 VM takes about 2 minutes of clone time plus a 3-minute first boot. The pre-installed VirtIO drivers, QEMU guest agent, and OpenSSH server mean the VM is ready for production use immediately after booting. For environments that need many Windows VMs (Active Directory labs, SQL Server clusters, or multi-tier application testing), this approach saves hours of repetitive setup work.

Related Articles

KVM Create and Manage User Accounts on oVirt and RHEV Cloud TrueNAS and How to Upgrade from FreeNAS to TrueNAS Core KVM Create, Resize and convert VM images in KVM using Qemu-img Virtualization Install vSphere ESXi 7.0 on Bare-metal Servers

Leave a Comment

Press ESC to close