This repository provides tooling and automation for building customized Windows Server disk images on modern Linux hosts, designed for upload and use with the Oxide platform.
This project has been designed to handle both Illumos and Linux as build platforms, however this README is focused on the Linux build process. The illumos build process is documented in a separate README.
The imgbuild.sh script is the main entry point for building Windows images on Linux. It orchestrates the entire process, from validating inputs to creating the final image.
To build images successfully on Linux, the following prerequisites must be met:
-
Operating System: Ubuntu 20.04+ or related distributions (others may work but are not tested).
-
Nested Virtualization: Required. Your system must support
vmx
(Intel) orsvm
(AMD). On cloud VMs (e.g. AWS/GCP), this usually requires special instance types that expose nested virtualization. -
Privileges: Depending on how your system is configured, scripts may require
sudo
access to create VMs and configure bridges. The script tries to check for this and will prompt if needed. -
Tools: See
install_prerequisites.sh
to install: -
qemu-kvm
,virt-install
,libvirt-daemon
,libguestfs-tools
,genisoimage
,virtio-win
, etc. -
Internet Access: Required to download updates and dependencies during install.
Create and edit the environment file - these variables must be set in your imgbuild.env
file before running the builder. Each controls a key part of the image creation process. A sample file is provided for reference and can be copied to start:
cp imgbuild.env.sample imgbuild.env
Variable | Description |
---|---|
|
Path to a scratch workspace directory used for temporary files, log output, and intermediate image artifacts. |
|
Full path to the resulting raw disk image ( |
|
Path to the Windows Server installation ISO (2019 or 2022 recommended). |
|
Path to the VirtIO driver ISO, used to inject storage and network drivers during Windows setup. |
|
Directory containing your |
|
Path to the OVMF firmware file (e.g. |
ℹ️
|
All paths must be absolute. Relative paths may not resolve correctly within virtualized builds. |
Each stage in the imgbuild.d/
directory is responsible for a key phase of the build:
Script | Description |
---|---|
|
Virtual module that runs all of the submodules in order. |
|
Verifies all required tools are present and working ( |
|
Ensures |
|
Compiles the Rust-based |
|
The main action:
- Spins up a temporary VM using |
To run a specific phase individually:
./imgbuild.d/build_image.sh phase
(Ensure your environment is sourced beforehand.)
This process produces a .raw
file in raw disk image format.
❗
|
The Oxide rack only accepts raw disk images for upload. Other formats like VMDK or QCOW2 will not work. |
Your output will be something like:
output/windows-2022.raw
❗
|
This image will be fairly large, roughly 13-15GB for most basic installations. |
Use the ISO for installation and evaluation; this is what we use for testing. Licensing is the responsibility of the user.
Ensure this is accessible at the path defined in your .env
file.
To fully automate Windows installation, this project uses Microsoft’s Autounattend.xml system.
Example configuration lives in:
unattend/Autounattend.xml
To customize:
-
Set timezone, locale, keyboard, disk layout.
-
Add user credentials.
-
Configure product key injection.
Resources for learning and modifying:
-
Microsoft Docs: https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/
-
Answer file generator: https://www.windowsafg.com
The wimsy tool runs an unattended Windows Setup session using the files found in the directory specified by the --unattend-dir
argument. These files can be modified directly, or you can pass flags to alter behavior dynamically
--unattend-image-index
: Overrides the ImageIndex in the Autounattend.xml. This allows you to choose a specific Windows edition (e.g., Standard vs. Datacenter, with or without Desktop Experience).
--windows-version
: Rewrites driver paths in Autounattend.xml to match a specific Windows version, ensuring the correct VirtIO drivers are used.
--vga-console
: (Linux only) Starts QEMU with VGA output so you can watch or interact with the Windows installation via console.
|
You need to have a DISPLAY configured to use the VGA console option. This will silently fail if you set it and do not have a DISPLAY. |
ℹ️
|
If you are using the ingbuild.sh script you will need to adjust the build-image.sh module. This module is responsible for calling wimsy and passing the correct arguments.
|
The images created by wimsy and this build system are fully generalized: What is Generalization?
They can be reused across multiple VMs. The default image includes:
Drivers:
-
virtio-net for networking support
-
virtio-block for disk support
User Accounts:
-
The default Administrator account is disabled.
-
A local user named oxide is created and added to the Administrators group.
-
SSH keys from Oxide instance metadata are injected into oxide’s authorized_keys.
-
No password is set by default. You can assign one later using:
net user oxide *
Remote Access:
-
EMS (Emergency Management Services) is enabled on COM1. Accessible via the Oxide console (Web or CLI).
-
OpenSSH is installed via PowerShell (Add-WindowsCapability) or from GitHub if needed.
-
RDP is enabled and firewall rules are pre-configured to allow port 3389.
ℹ️
|
Instance-level firewall rules must also allow access to port 3389 for RDP to function externally. |
In-Guest Agents: Installs an Oxide-compatible fork of Cloudbase-Init:
-
Metadata is read from the NoCloud config drive.
-
Hostname is set automatically to match the Oxide instance name.
-
SSH key injection is configured for the oxide user.
-
The system drive is auto-expanded to match the VM disk size at boot.
Each Windows ISO may contain multiple editions (e.g., Standard, Datacenter, Core). You must set the correct image index in Autounattend.xml.
To inspect available indexes:
# On a Debian/Ubuntu-based host
sudo apt-get install wimtools p7zip-full
7z e '-ir!install.wim' /path/to/windows.iso
wiminfo install.wim
Use the /IMAGE/INDEX that corresponds to your desired edition.
Once the .raw file is generated, it can be uploaded to your Oxide silo as a custom image.
oxide disk import \
--project yourproject \
--path yourimage.raw \
--disk disk-name \
--disk-block-size 512 \
--description "Windows on Oxide" \
--snapshot win-2022 \
--image win-2022 \
--image-description "Windows with Oxide"
--image-os windows --image-version 2022
-
Visit your Oxide console.
-
Navigate to the "Images" section.
-
Click "Import Disk".
-
Provide a name, description, os type, and version. Then add your .raw file and import the image.
-
Now you can deploy an instance using this image.
More information: https://oxide.computer/docs/ui/image-import
While this project has pivoted toward a Linux-first experience, full support for illumos-based systems continues.
If you’re using an illumos host (such as SmartOS or OmniOS), please refer to the dedicated documentation: