Windows application developers are currently adding support for the Arm architecture because they want to enable native app performance on new Windows on Arm Copilot+ PCs.
If you are looking for ways to build and test Windows on Arm software, you have several options to choose from. You can run Windows on Arm on a laptop, you can use cloud-based virtual machines, or you can leverage CI/CD platforms like GitHub Actions to access Windows on Arm.
Another option is to create Windows on Arm virtual machines directly on Amazon EC2 instances running AWS Graviton processors.
Read on to find out how to install and run Windows on Arm virtual machines on a Graviton-based EC2 instance. I have created two bash scripts to streamline the creation and management of your virtual machines.
Prepare your system for Windows on Arm virtual machines
Here are the steps you can use to run Windows on Arm on an Amazon EC2 instance:
- Create an EC2 instance with AWS Graviton processors
- Install and configure VNC
- Confirm KVM is available
- Install software dependencies
- Create a Windows virtual machine
- Run a Windows virtual machine
At the end, you will have a functional Windows desktop accessible using a VNC client from your local computer.
Create a bare-metal EC2 instance
You can use Graviton2, Graviton3, or Graviton4 processors. Graviton2 performance is sufficient for most Windows development. Use Graviton3 or Graviton4 if you want faster performance and features such as SVE2 (scalable vector extension), BFloat16 (16-bit floating point), and I8MM (8-bit integer multiplication).
In your AWS account, create a c6g.metal instance type and set up SSH connectivity running Ubuntu 24.04. Make sure to use at least 128 GB of storage to allow for virtual machine disk images. Each instance of Windows requires approximately 40 GB of storage. You can also use other bare-metal instances such as m6g.metal and r6g.metal.
To create the instance:
- Launch a new EC2 instance from the AWS Console
- Select Ubuntu Server 24.04 LTS as the AMI (make sure to select the Arm version)
- Choose
c6g.metalas the instance type - Configure a security group that allows SSH (port 22) from your IP address
- Create or select an existing SSH key pair for authentication
- In storage settings, allocate at least 128 GB for the root volume
- Launch the instance and wait for it to enter the "running" state
Make sure you can SSH to the instance using your SSH key.
Confirm that you can access your instance and run:
sudo apt update
You are now ready to get started installing software.
Install and configure VNC
Virtual Network Computing (VNC) is a tool used to connect to a remote Linux desktop. Running Windows requires a desktop environment to see the installation process and to interact with the Windows desktop.
You can install VNC on your EC2 instance. VNC is a client-server application. A VNC server runs on a remote machine (the EC2 instance). A VNC client runs on the local machine (your laptop) and connects to the remote server.
Install the VNC server and xfce4 desktop
To use VNC, you need to install a VNC server on your EC2 instance. There are multiple VNC servers, one good option is TigerVNC.
You also need Linux desktop software. There are many options for this, but using xfce4 provides a minimal install with good performance.
Install the desktop software:
sudo apt-get install xfce4 xfce4-goodies xorg dbus-x11 x11-xserver-utils xfce4-terminal -y
Install the VNC server:
sudo apt-get install tigervnc-standalone-server tigervnc-common -y
Configure a VNC password
Run the vncpasswd command to set a password for VNC. This is not the password for your user account, but for the VNC client to connect to the VNC server.
vncpasswd
Remember the password for later when you connect the client.
Configure the desktop startup for VNC
Use a text editor to create a file named $HOME/.vnc/xstartup with the following contents:
#!/bin/sh
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
exec startxfce4
Give the file executable permission:
chmod +x $HOME/.vnc/xstartup
Set up a systemd service to manage VNC
To create a systemd service to start the VNC server, create the file /etc/systemd/system/vncserver@.service.
Use sudo or root privileges because this file is in a system directory.
Your username should be ubuntu, but if it is not, change the User value to match your username after you create the new file.
[Unit]
Description=Remote desktop service (VNC)
After=syslog.target network.target
[Service]
Type=simple
User=ubuntu
PAMName=login
PIDFile=/home/%u/.vnc/%H%i.pid
ExecStartPre=/bin/sh -c '/usr/bin/vncserver -kill :%i > /dev/null 2>&1 || : '
ExecStart=/usr/bin/vncserver :%i -localhost no -geometry 1440x900 -alwaysshared -fg
ExecStop=/usr/bin/vncserver -kill :%i
[Install]
WantedBy=multi-user.target
To start the VNC service:
sudo systemctl start vncserver@1.service
To stop the VNC service:
sudo systemctl stop vncserver@1.service
To restart the VNC service:
sudo systemctl restart vncserver@1.service
Use port forwarding via SSH to connect to VNC
The default port for the first instance of VNC is 5901. SSH port forwarding is a good solution for accessing the Linux desktop on a cloud machine. This way, no additional ports need to be opened in the security group.
SSH to your remote Linux machine.
Substitute your private key file and the public IP address of the remote machine in the following command:
ssh -i <private_key> -L 5901:localhost:5901 ubuntu@<public_ip_address>
Once connected via SSH, use a VNC client to connect. Download and install a TigerVNC client for your computer.
Open the VNC client and enter the following for the VNC server:
localhost:5901
You will be prompted for the password you created earlier with vncpasswd.
A remote Linux desktop should appear on your local computer. When you are finished, close the VNC client first and then exit the SSH connection.
Install other software dependencies
Next, install the software you need to create a Windows on Arm virtual machine using QEMU and KVM. Before installing the software, confirm KVM is available on your EC2 instance. A bare-metal Graviton instance is used so that KVM is available.
Verify KVM support
Kernel-based Virtual Machine (KVM) support is required for hardware-accelerated virtualization and optimal virtual machine (VM) performance on Arm systems. Without KVM, your VMs run significantly slower because they rely on software emulation instead of using Arm's hardware virtualization features.
KVM is a virtualization infrastructure built into the Linux kernel that allows you to run virtual machines with near-native performance. It leverages Arm's hardware virtualization extensions to provide efficient CPU virtualization, while QEMU handles device emulation and management.
Verify your system supports KVM by running:
sudo apt install cpu-checker -y
kvm-ok
If KVM is available, you will see the messages:
INFO: /dev/kvm exists
KVM acceleration can be used
This confirms that:
- Your CPU supports hardware virtualization
- The KVM kernel module is loaded
- The
/dev/kvmdevice exists
Add your user account to the KVM group:
sudo usermod -a -G kvm $USER
newgrp kvm
Install required software
The Windows on Arm scripts require several software packages.
Install the packages using the Linux package manager, including QEMU and Remmina, the remote desktop software used to connect to Windows VMs.
sudo apt update
sudo apt install qemu-system-arm qemu-utils genisoimage wget curl jq uuid-runtime seabios remmina remmina-plugin-rdp -y
You now have the required software to create a Windows on Arm virtual machine on your EC2 instance.
Get started with the Windows on Arm VM automation scripts
A GitHub project provides two Bash scripts.
Start by cloning the project repository from GitHub to your Arm Linux system.
git clone https://github.com/jasonrandrews/win11arm.git
cd win11arm
The project includes two Bash scripts.
- VM create script:
create-win11-vm.shhandles all VM creation tasks - VM run script:
run-win11-vm.shmanages VM execution and connectivity
All configuration is available using command-line options.
Create a new Windows virtual machine
To create a new VM, run the command:
./create-win11-vm.sh all $HOME/win11-vm
This single command executes all required virtual machine creation steps as explained in the previous section.
The VM data is stored in the $HOME/win11-vm directory, and Windows will install automatically without any user intervention.
The product key used in the scripts is a generic key provided by Microsoft, which allows installation. This key is for testing purposes only and does not activate Windows. If you plan to continue using Windows beyond installation, you should replace it with a genuine product key.
Once the VM creation is complete, you'll see:
QEMU closed successfully.
Windows installation should be complete!
You can now use: ./run-win11-vm.sh $HOME/win11-vm
Your Windows on Arm VM is now ready to use.
Run your newly created Windows 11 virtual machine
After your Windows 11 Arm VM is created, launching it is simple with the unified run script:
./run-win11-vm.sh $HOME/win11-vm
This single command handles the entire VM startup and connection process automatically.
The script performs three key steps. It does the following:
- Checks if the VM is already running
- Starts the VM in headless mode if required
- Connects you through RDP using Remmina
When the virtual machine starts you will see it on your Linux desktop:
You now have a running Windows on Arm virtual machine running on an AWS Graviton-based EC2 instance.
VM shutdown
The preferred method is to shut down Windows normally from within the virtual machine.
- Click the Start button in Windows
- Select Power → Shut down
- Wait for Windows to complete shutdown
- The VM automatically stops when Windows finishes shutting down
- Remmina exits automatically when the connection closes
You should avoid killing QEMU directly, as it may corrupt the VM disk. Also, avoid exiting Remmina prematurely, as it may leave the VM running in the background.
More details about command-line arguments and how the scripts work are available in Automate Windows on Arm virtual machine deployment with QEMU and KVM on Arm Linux.


Top comments (0)