lefteros_by SomniusX
EL ↗Say hello

LEFTEROS / PERSONAL ARCHIVE

Windows VM for Omarchy

27 September 2026 / Lefteris Iliadis / SomniusX

Omarchy has a built-in Windows VM: omarchy-windows-vm install sets up Windows 11 running in a container, which you open over RDP. Windows VM for Omarchy is my plugin that gives it a place on the bar: a small Windows logo and a panel for everything else — connecting, restarting, shutting down and live figures. Current version 1.1.2, MIT licence.

The panel opening: a waiting message, then live figures and graphs

The panel opening: about a second of a waiting message, then the live figures with their graphs.

What it does

  • Status on the bar. The logo is bright while Windows runs, dim when it is stopped, and pulses while it starts or shuts down. Hover it for a summary, e.g. Windows running · up 7h 20m · RDP open.
  • Costs nothing while you are not looking. With the panel closed, the plugin only checks whether Windows is running: one small process every 5 seconds. No resource tracking, no Docker calls, no window polling in the background.
  • Live figures, on demand. Open the panel for CPU (against the VM’s vCPUs and against your machine), memory held on the host, disk I/O, how full the disk image is, network down/up, uptime and the container ID (one click copies it). CPU and network come with live graphs. The first numbers arrive in about a second, and closing the panel throws them away.
  • Connect without a password. When Windows is running, Connect opens an RDP window straight into your session with the credentials Omarchy saved. The password travels through a file descriptor, so it never shows in the process list. Closing that window never stops Windows.
  • RDP that reconnects by itself. If the connection through Docker drops, the window reconnects instead of disappearing — for windows opened by Connect as well as by Start and Restart.
  • Keep Windows running (optional). By default, closing the RDP window shuts Windows down, as Omarchy’s launcher does. With this on, only the window closes; apps, SSH sessions and background jobs carry on.
  • Restart and shut down with a safety catch. Both ask for a second click, close the RDP windows first and never ask for your password twice.
  • Other ways in: the Console (the VM’s screen in your browser, handy while it boots), the Shared folder (~/Windows), an optional Terminal button (e.g. ssh winvm) and Docker, which opens lazydocker when it is installed.
  • Failures show at once. A declined password prompt, missing credentials or a session that is already open appear in the panel straight away, not after a timeout.
  • A log for every start and connection, with FreeRDP’s own messages, so you can see why a session ended.

Screens

The panel with live CPU, memory, disk and network A waiting message over empty bars Settings with the RAM reminder The built-in help

In order: the live figures · while they load (about a second) · settings with the RAM reminder · the built-in help.

On the bar and in the panel

  • Left-click: opens the panel.
  • Right-click: goes to the Windows desktop — focuses the RDP window, connects, or starts Windows.
  • Hover: status, uptime and whether the RDP window is open.

In the panel, the main button follows the state — Show Windows desktop, Connect (RDP) or Start Windows — followed by Restart, Shut down, Console, Shared, Terminal (only when you have set one), Docker (only when lazydocker is installed), Logs, and the collapsible Settings and Help sections. Shutting down from the Windows Start menu works too.

The Console asks for a login: Omarchy installs the VM with Dockur’s PROTECT option, which puts a password on the web console even though only your own machine can reach it. Log in with the Windows username and password you chose at install.

“Keep Windows running”: the trade-off

Omarchy’s launcher treats a closed RDP window as “I am done” and shuts Windows down. With the option on, only the window closes, and Connect takes you back into the same session, with your apps as you left them.

The price: the VM keeps the RAM you gave it at install (RAM_SIZE) until you press Shut down or shut it down from Windows. While the option is on, the panel reminds you with the exact amount. The setting applies from the next start.

Install

omarchy plugin add https://github.com/Somnius/Windows-VM-for-Omarchy.git --enable

Already installed? omarchy plugin update lef.windows-vm.

Requirements: Omarchy with the Windows VM installed (omarchy-windows-vm install) and Docker using cgroup v2 (the Arch default). It only uses tools Omarchy already ships: omarchy-windows-vm, xfreerdp3, hyprctl, jq, uwsm-app, xdg-terminal-exec and wl-copy. Status and figures need no special permissions; starting and stopping go through Omarchy’s own command, which asks for your password if your user is not in the docker group.

From the command line

Every panel action is also a command, so it binds easily to a Hyprland shortcut:

omarchy-shell lef.windows-vm show       # the desktop, or connect/start
omarchy-shell lef.windows-vm restart
omarchy-shell lef.windows-vm stop
omarchy-shell lef.windows-vm status     # state as JSON; live figures only while the panel is open ("statsLive")
omarchy-shell lef.windows-vm toggle     # open/close the panel

How it works

Omarchy’s VM, underneath

omarchy-windows-vm runs Windows 11 in the dockurr/windows container: QEMU with KVM acceleration inside a privileged Docker container, with the RAM, cores and disk you choose at install. It exposes only on 127.0.0.1 port 8006 (the web console) and 3389 (RDP), and the launcher connects with FreeRDP (xfreerdp3) in a window titled Windows VM - Omarchy. The ~/Windows folder is shared with Windows as \\host.lan\Data.

Figures without privileges and without Docker

Every number comes from bin/Sample.sh, which prints one JSON line of raw counters and never touches the Docker socket. It finds the VM by looking, in each Docker container’s cgroup (systemd or cgroupfs), for the QEMU process named windows — the name dockurr/windows gives it. With the panel closed it runs as Sample.sh --state and reads only whether QEMU is running and since when. With the panel open it runs every 2 seconds from the moment it opens, and the plugin turns two samples into rates — so a dropped sample costs one refresh, not a wrong number.

Figure Source
Running a Docker container’s cgroup holding a qemu-system-* process named windows
CPU the cgroup’s cpu.stat, against the guest’s vCPUs (QEMU’s -smp) and against the host
Memory the cgroup’s memory.current, against your machine’s RAM
Disk I/O the cgroup’s io.stat, only the busiest device (so an encrypted disk is not counted twice)
Image the allocated blocks of the sparse ~/.windows/data.img
Network eth0 inside the VM’s network namespace
Uptime the QEMU process’s start time (field 22 of /proc/<pid>/stat)
Starting Docker reports the container running before QEMU exists

The only Docker call — docker inspect for the “starting” state — happens only with the panel open or an action in progress, and only when no VM process exists yet. The RDP window is looked up only when needed: panel open, hover, a click or an action in progress. The memory shown is what the VM takes from your machine, not what Windows’ Task Manager says: Windows claims its RAM at boot.

Connecting without a password

bin/Connect.sh opens an RDP window when Windows is already running, without going through omarchy-windows-vm launch, which would re-run the privileged bring-up — and might ask for a password — just to reconnect. It reads the credentials Omarchy’s installer saved, uses the launcher’s own flags, and hands the arguments to FreeRDP through a file descriptor, so the password is never on a command line. It refuses to connect while another session is connecting or reconnecting — Windows allows only one, and the two would keep kicking each other out — and it never tries the image’s well-known default password.

Auto-reconnect through a shim

The RDP connection runs through Docker’s port forwarding. When that link breaks, FreeRDP logs Network disconnect! and, without auto-reconnect, the window closes although Windows is fine. The plugin adds FreeRDP’s /auto-reconnect (up to 5 attempts): Connect starts FreeRDP with it directly, while for Start and Restart Omarchy’s launcher runs with the plugin’s bin/shim folder first on its PATH. Because the launcher runs xfreerdp3 by name, it finds a tiny wrapper that adds the flags and hands everything to /usr/bin/xfreerdp3 through a file descriptor — so even the launcher’s /p: does not stay readable in /proc/<pid>/cmdline. Only the unprivileged RDP client is wrapped; the launcher and everything it runs as root stay untouched, and no system file changes. A guard stops the shim from ever calling itself in a loop.

Actions that survive a shell restart

Start, restart and shutdown run through Launch.sh (which calls Omarchy’s launcher) or bin/Connect.sh, detached through uwsm-app, so an open RDP window does not close if the shell reloads. Shutdown and restart close the RDP windows first; if a launcher is still waiting on its window, that launcher stops the VM and the plugin waits for it, rather than stopping the VM underneath it. The scripts write failures to $XDG_RUNTIME_DIR/windows-vm/status.json, so the panel shows the reason at once; anything that happens inside FreeRDP (e.g. Windows rejecting the password) is in the log, and the panel stops waiting after 45 seconds.

An Omarchy bug, worked around safely

Omarchy’s launcher requires ~/Windows to be mode 700, but Dockur’s Samba sets the setgid bit, which chmod 0700 keeps — and the launcher refuses to start (omacom/omarchy#10256). Before every start, Launch.sh clears only that bit, only on your own folder: if it is not a symbolic link and you own it.

Logs, settings and boundaries

Every start, restart, shutdown and connection writes one file to ~/.local/state/windows-vm/logs/ (it follows $XDG_STATE_HOME), in a folder with mode 700; the 30 newest are kept, and FreeRDP runs with WLOG_LEVEL=INFO so the reason a session ended is recorded.

Settings live in ~/.config/omarchy/windows-vm/config.json: hideWhenStopped, keepAlive and terminalCommand (the only one without a switch in the panel — e.g. "ssh winvm" for a Terminal button). Unknown keys are kept, and a broken config.json is never overwritten. The plugin never edits the VM’s Docker configuration, never needs root, and removing it leaves the VM and its disk untouched.

Tests

The logic — reading the samples, rates from counter deltas, formatting, finding the window, when an action is done — lives in a pure JavaScript module with no Quickshell imports, so Qt 6’s qmltestrunner checks it offscreen with ten test groups. tests/run.sh also checks the shell scripts.

Thanks

1.1.0 builds on ideas and code from omarchy-winvm by Jon Spinks (MIT licence): the unprivileged resource sampling, the direct reconnect with saved credentials, the resource meters, and handing the shutdown to a waiting launcher. The adapted parts are listed in the repository’s NOTICE together with the original licence, and each such file says so in its header.

Sources and date

First release and Omarchy plugin marketplace listing: 2026-09-27. Version 1.1.0: the same day. Versions 1.1.1 and 1.1.2 (hover tooltip on Omarchy 4.0.4, ready for Qt 6.12): 2026-10-09.

Remove it with omarchy plugin remove lef.windows-vm (and optionally rm -rf ~/.config/omarchy/windows-vm ~/.local/state/windows-vm for settings and logs). License: MIT.