Build a custom Windows qcow2 image with do-win-image-builder on a one-time Linux builder droplet, upload it to DigitalOcean Custom Images, and launch Windows droplets from it. The Windows installation is fully automated — no browser interaction is required.
Important: Keep your session alive with
screenThe Windows image build can take a long time (package installs, ISO download, OS installation, sysprep, and image upload). If your SSH connection drops, the build process will stop. Run the entire workflow inside a
screensession on the builder droplet:# After SSH into the builder droplet: screen -S windows-build # Run all setup steps inside this screen session. # If disconnected, reattach with: screen -r windows-buildImage building typically takes up to 1 hour. Uploading the finished image to DigitalOcean Custom Images takes around 1–3 hours depending on image size and region.
- Overview
- Build workflow
- Step 1 — Create the Linux builder droplet
- Step 2 — Start a screen session and run the build script
- Step 3 — Provide the Windows ISO
- Step 4 — Automated Windows installation
- Step 5 — Upload the custom image to DigitalOcean
- Step 6 — Launch a Windows droplet from the custom image
- Step 7 — Initial access and RDP setup
- Important notes
- Advanced reference
- Create a one-time Linux builder droplet
- Run the Windows image build script on that droplet (inside
screen) - Windows installs automatically via
autounattend.xml— noVNC is optional for monitoring or troubleshooting - Upload the finished image to DigitalOcean using an API token
- Launch Windows droplets from the custom image
- Connect via SSH (initial setup) and RDP (desktop access)
This is a one-time image-build step. After the custom image exists, you can reuse it to launch Windows droplets like any other custom image.
flowchart LR
A["screen + setup.sh"] --> B["Prompts: upload, ISO, edition"]
B --> C["Unattended QEMU install"]
C --> D["sysprep + shutdown"]
D --> E["HTTP serve + DO upload"]
C -.->|optional| F["noVNC monitor"]
- In the DigitalOcean control panel, go to Create → Droplets.
- Choose your target region (e.g. NYC1 for the builder). Custom images are region-specific — upload and launch in the same region.
- Select a Linux distribution (Ubuntu was used in the demo).
- Choose a droplet size with enough CPU, memory, and disk for the Windows build:
- At least 16 GB RAM and a dedicated CPU droplet are recommended.
- Allow enough disk for the ISO plus the qcow2 image (default 64 GB).
- Use password or SSH key authentication for the builder.
- Name the droplet (e.g.
windows-builder) and click Create Droplet.
SSH into the builder droplet, start a screen session, and run the build script as root:
screen -S windows-build
# Option A — one-liner
curl -sSL https://raw.githubusercontent.com/DO-Solutions/do-win-image-builder/dev/setup.sh | bash
# Option B — clone the repository
git clone --branch dev https://github.com/DO-Solutions/do-win-image-builder
cd do-win-image-builder
sudo bash setup.shThe script installs required packages (QEMU/KVM, noVNC, dependencies).
Shortly after dependencies install, the script asks whether to upload to DigitalOcean when the build completes. If yes, provide the region, image name, and API token now so you can disconnect during the 1–3 hour upload. Pre-set these with environment variables for a fully unattended run:
export DO_UPLOAD=y DO_REGION=nyc3 DIGITALOCEAN_TOKEN="your-do-api-token"
sudo -E bash setup.shDetach without stopping the build: press Ctrl+A, then D. Reattach after reconnecting: screen -r windows-build.
- Host your Windows ISO in a DigitalOcean Spaces bucket (recommended) or any web-accessible URL.
- When the script prompts for the ISO path/URL, paste the direct download link (or provide a local path).
- If the ISO contains multiple editions, select Pro, Enterprise, Education, or Server (Home editions are excluded automatically).
- The script downloads the ISO, VirtIO drivers, and Cloudbase-Init, then starts the virtual install.
After you provide the ISO (and edition/locale if prompted), the script boots a QEMU VM and installs Windows fully unattended:
autounattend.xmlhandles partitioning, VirtIO driver injection, edition selection, and OOBE settings.firstlogon.ps1installs OpenSSH, Cloudbase-Init, and RDP, then runs sysprep and shuts down the VM.- The script waits for the VM to exit, then continues automatically.
The script prints a noVNC URL and VNC password. Open it only if you want to monitor progress or troubleshoot — no browser interaction is required for a successful build. If everything is working, ignore noVNC and let the build continue inside your screen session.
Do not destroy the builder droplet until the upload completes.
Upload preferences are collected at the start of the build (Step 2). When the VM shuts down, the script serves the qcow2 over HTTP and starts the scheduled upload automatically.
Option A — API upload (recommended)
- Generate a DigitalOcean API token (Control panel → Account → API → Generate New Token).
- For testing, full access works; for production, use the narrowest scope that allows custom image upload.
- Answer
yat the start-of-build prompt, or setDO_UPLOAD=yandDIGITALOCEAN_TOKENbeforehand. - The script uploads the image and polls until it is
available. - After upload, the image appears under Backups & Snapshots → Custom Images.
Option B — Manual import
Skip API upload at the prompt. When the build finishes, the script prints manual instructions:
- Open Backups & Snapshots → Custom Images
- Click Import via URL
- Paste:
http://<builder-droplet-ip>:8080/image.qcow2
The HTTP server keeps running until you stop it.
- Go to Create → Droplets.
- Select the same region where the custom image was uploaded.
- Under Choose an image, select Custom images and pick your Windows image.
- Choose a droplet plan with enough disk for the image (undersized disks will fail).
- Authentication: use an SSH key — password login is not supported for droplets from this image.
- Create the droplet.
On first boot, Cloudbase-Init creates the doadmin administrator account and injects the SSH key you provided at droplet creation.
SSH into the new Windows droplet:
ssh -i /path/to/your/private_key doadmin@<droplet_public_ip>Reset the administrator or doadmin password for RDP:
net user administrator <YourNewPassword>Open your Remote Desktop client and connect to the droplet public IP using doadmin or Administrator and the password you set.
- One-time build: The Linux builder droplet is only needed to create the image. Delete it after upload succeeds.
- Golden image: Customize the PowerShell scripts in
scripts/(e.g.firstlogon.ps1,Set-AdminSSHKeys.ps1) before running the build to bake in software and configuration. - Region: Custom images are tied to the upload region. Build and launch in the same region.
- SSH key required: Windows droplets from this image require an SSH key at creation time.
- Sizing: Pick adequate CPU, RAM, and disk for both the builder and the final Windows droplet.
- Use screen: Always run the build inside
screenso a dropped SSH session does not abort the process. - Unattended install: The build is automated end to end. noVNC is a troubleshooting aid, not a required step.
- Ubuntu/Debian builder droplet — 16 GB RAM minimum, dedicated CPU, run as root
screen -S windows-build, then runsetup.sh- Provide ISO URL; answer edition/locale prompts
- Walk away — install and sysprep are automated (~1 hour)
- Upload runs automatically if configured at start (~1–3 hours)
- noVNC URL is optional — use only to monitor or debug
- Automated Windows setup: Generates
autounattend.xmlwith VirtIO drivers, RDP, and Cloudbase-Init - SSH preconfigured: OpenSSH Server is installed, enabled, and port 22 is opened during the build
- First-boot cloud-init: Cloudbase-Init sets the server hostname, injects SSH keys, and runs UserData and LocalScripts startup scripts when a droplet launches from the image
- Edition detection: Reads WIM metadata, filters Home editions, and injects KMS generic keys for testing
- Optional noVNC console: Monitor the unattended install or debug setup issues
- DigitalOcean-compatible image: Produces a qcow2 image ready for DigitalOcean Custom Images (< 100 GB)
- HTTP download: Serves the finished image over HTTP when the build completes
- API upload: Optionally imports the image into DigitalOcean Custom Images via the API
Custom configuration — export environment variables before running setup.sh. When using sudo, pass them through with -E:
export HTTP_PORT=9090 QCOW2_SIZE=32G
sudo -E bash setup.shOr inline with the curl one-liner:
HTTP_PORT=9090 QCOW2_SIZE=32G curl -sSL https://raw.githubusercontent.com/DO-Solutions/do-win-image-builder/dev/setup.sh | bash| Parameter | Description | Default |
|---|---|---|
TMP_DIR |
Working directory for the build (qcow2, ISOs, logs) | ./do-win-image-builder |
QCOW2_IMAGE |
Output disk image path | $TMP_DIR/image.qcow2 |
QCOW2_SIZE |
Size of the qcow2 image (must stay under 100 GB for DO custom images) | 64G |
HOST_RESERVED_MB |
RAM left for the host; the rest goes to the VM (minimum 2560) | 4096 |
HTTP_PORT |
Port for serving the finished image | 8080 |
VIRTIO_URL |
Download URL for the VirtIO drivers ISO | Fedora virtio-win stable URL |
CLOUDBASE_INIT_URL |
Download URL for Cloudbase-Init | Cloudbase.it stable MSI URL |
REPO_URL |
Git repo cloned when running the curl one-liner | https://github.com/DO-Solutions/do-win-image-builder |
REPO_BRANCH |
Branch checked out for setup scripts | dev |
DO_METADATA |
Metadata endpoint for droplet IP and region | http://169.254.169.254/metadata/v1 |
DO_UPLOAD |
Upload via API when build completes (y/n); skips the upload prompt when set |
(prompted at start) |
DO_REGION |
Region slug for custom image import; defaults to droplet region | (prompted at start) |
DO_IMAGE_NAME |
Name for the custom image in DigitalOcean | win-custom-YYYYMMDD |
DIGITALOCEAN_TOKEN |
API token for uploading the finished image (skips the token prompt) | (unset) |
QCOW2_SIZE must remain under 100 GB uncompressed for DigitalOcean Custom Images.
| Version | Detected slug | Notes |
|---|---|---|
| Windows 10 | w10 |
Pro, Enterprise, Education |
| Windows 11 | w11 |
Pro, Enterprise, Education |
| Windows Server 2016 | 2k16 |
Standard, Datacenter |
| Windows Server 2019 | 2k19 |
Standard, Datacenter |
| Windows Server 2022 | 2k22 |
Standard, Datacenter |
| Windows Server 2025 | 2k25 |
Standard, Datacenter |
Home editions are excluded automatically. The script reads WIM metadata from your ISO, prompts you to pick an edition when multiple are available, and injects the matching KMS generic setup key when known.
Example output
── Step 1: Install Dependencies ──
[ OK ] Dependencies installed.
── Step 2: DigitalOcean Custom Image ──
Upload to DigitalOcean via API when the build completes? [y/N] y
[ OK ] Will upload to DigitalOcean when the build completes (region: nyc3, name: win-custom-20260703).
── Step 3: Prepare Working Directory ──
[ OK ] Image created: ./do-win-image-builder/image.qcow2
── Step 4: Installation ISO ──
[INFO] Detected: Windows Server 2022
[ OK ] Edition: Windows Server 2022 Standard (WIM index 2, KMS key injected)
── Step 5: Boot QEMU VM ──
[ OK ] QEMU running (PID 12345).
── Step 6: noVNC Web Console ──
[ OK ] noVNC is running.
Open in your browser (optional — for monitoring/troubleshooting):
http://<droplet-ip>:6080/vnc.html?autoconnect=true&password=<generated>
── Step 7: Serve Image ──
[INFO] Windows is installing unattended.
[INFO] Waiting for the VM to shut down...
[ OK ] VM has shut down.
[ OK ] Image ready: ./do-win-image-builder/image.qcow2 (12G)
[ OK ] HTTP server running (PID 12346)
[INFO] Starting scheduled DigitalOcean upload (region: nyc3, name: win-custom-20260703)...
[ OK ] Custom image "win-custom-20260703" is ready in nyc3.