An on-demand Windows machine, for testing the POS desktop app
Source:
g2labs-in/linode-infra@f503b3f·docs/runbooks/azure-windows-test-vm.md· updated 2026-09-19
Status: plan, not built. Nothing exists in Azure today. This is what to run when we need it.
Last updated 2026-09-19.
Why this exists
Nobody on the team runs Windows. Abhilash and Anshul are on MacBooks, Aakash is on Linux, and the
Billuu POS desktop app is Windows-only (build.win is the only target in package.json). That leaves
one platform we ship to and cannot touch.
There are two different gaps hiding under “we need Windows”, and they need different answers.
| Gap | Answer | Cost |
|---|---|---|
| Does it build, boot and behave? | GitHub Actions windows-latest, already running | free |
| Does the installer work, and how does the app feel? | this VM | per hour, only while running |
| Does it print on a real till? | a physical Windows box, later | one-time hardware |
This document covers the middle row. It does not replace the other two.
What this machine can and cannot do
Can:
- Run the NSIS installer and see the real SmartScreen prompt
- Prove the update path end to end: install build N, publish N+1, watch it update
- Measure how the app actually performs, on hardware that is not a developer’s laptop
- Exercise the Windows spooler path, including enumerating printers
- Catch real Windows path, permission and filesystem problems
- Reproduce a customer’s bug report on something that resembles their machine
Cannot:
- Print anything real. Azure VMs have no USB passthrough to a thermal printer, and RDP
redirection is unreliable for raw printing.
printerManager.cjs:276pickswindows-spooler-rawon Windows, and the output end of that path needs physical hardware. Printing verification waits for the mini PC. - Replace the CI job. Anything CI already asserts should not be checked by hand here.
The cost model, and how to keep it near zero
Three states, and only one of them is free.
| State | Compute | Disk | What to run |
|---|---|---|---|
| Running | billed hourly | billed | ./windows-test-vm.sh start |
| Stopped properly | free | billed | ./windows-test-vm.sh stop |
| Deleted | free | free | ./windows-test-vm.sh destroy |
Two traps worth stating plainly:
az vm stopdoes not stop the billing. It keeps the compute allocated and keeps charging.az vm deallocate, which thestopsubcommand uses, is the one that releases it. This is the single most common way to waste money on an idle Azure VM.- A reserved public IP also bills while idle. This VM has none. It is reached over Tailscale, which costs nothing and matches the rest of the estate.
So the idle cost is the OS disk alone. For a 64 GB StandardSSD that is a few dollars a month, which is the price of not waiting twenty minutes for a rebuild when someone needs to test something.
If the disk cost is not worth it, destroy removes the resource group entirely and cost drops to
zero. Rebuilding is one command and a few minutes. Choose per how often it is used.
Prices must be read in the Azure portal. az consumption returns no amounts on this subscription,
which CONTEXT.md already records.
Budget constraint to clear first
CONTEXT.md records that the $200 Azure credit ends around 2026-10-13, and that the account stops
working on the 12-month grants unless it moves to pay-as-you-go.
Creating a Windows VM after that date needs the account transition finished first. This is a dependency, not a detail. If the account is not moved, this plan cannot be executed at all.
How to use it
The script is azure/windows-test-vm.sh. It is a plan you can run, not a build step.
cd ~/code/linode
./azure/windows-test-vm.sh create # once. Validates, creates, joins the tailnet../azure/windows-test-vm.sh start # power on when needed./azure/windows-test-vm.sh ip # the address to RDP to./azure/windows-test-vm.sh stop # deallocate, which stops the billing./azure/windows-test-vm.sh destroy # remove everything, cost drops to zeroAccess is RDP over Tailscale, so the port is never exposed to the internet:
mstsc /v:<tailscale-ip> # from Windows# from macOS, use Microsoft Remote Desktop; from Linux, remminaThen, on the VM, install the POS from the release feed and use it the way an outlet would.
Size and image
| Setting | Default | Why |
|---|---|---|
| Size | Standard_B2s (2 vCPU, 4 GB) | Burstable is right for occasional interactive use. Windows 11 wants 4 GB minimum. |
| Image | Windows 11 Pro | The point is to see what a customer sees. Server has a different shell, no consumer SmartScreen, and no consumer UX. |
| Disk | 64 GB StandardSSD | Enough for Windows plus the app. |
If Standard_B2s is refused, try Standard_B2ms. CONTEXT.md is explicit that SKU restriction flags
on this offer are unreliable, which is why the script runs az vm create --validate before creating
anything. That check costs nothing and takes seconds.
Password
Windows needs an admin password for RDP. The script prompts for one, or reads WINTEST_PASSWORD from
~/.config/billuu/infra.env. Put it in the password manager either way.
When to build it
Not yet. Build it when one of these becomes true:
- A customer reports a Windows-only bug that CI cannot reproduce
- Someone needs to verify the update path against a real install, not the packaged
--dirbuild - The POS desktop is about to be installed at an outlet where we cannot watch it
- Performance is in question and a developer’s laptop is not a fair test
Until then it costs nothing to leave unbuilt, and building it early only starts the disk billing.
What this is not
- Not a staging environment. Those are
.scratch/staging-environment-split. - Not a substitute for CI. CI already packages and boots the real Windows app on every PR.
- Not a print test. Buy the mini PC for that. It is the only thing that closes that gap.
- Not a macOS answer. macOS is not a target, and
printerManager.cjstakes a different transport there, so a Mac test would exercise a code path no customer runs.
State of things, for the record
| Item | State |
|---|---|
| VM exists | no |
| Account transition to pay-as-you-go | not done, needed before 2026-10-13 |
Tailscale auth key in ~/.config/billuu/infra.env | yes |
CI windows-latest job | running, desktop-smoke.yml |
| Physical Windows machine for printing | not bought, recommended later |