Description
System details
Omarchy 4.0.3-1, kernel 7.2.3-arch1-3, Docker 29.7.2 + KVM
What's wrong?
Summary
While using Omarchy's built-in Windows VM, I hit two separate but related defects:
- Data loss (dockurr/windows lifecycle, as used by Omarchy): if the VM container is never stopped cleanly after installation, dockurr/windows never writes its install-completion marker. On the next start it treats the existing, non-empty
data.imgas an unfinished installation, deletes it, and starts a fresh unattended Windows install. There is no prompt and no backup. - Launcher permission check (omarchy-windows-vm): after the container's first start, the shared folder carries the setgid bit. The launcher's
chmod 0700keeps that bit, its exactmode == 700check fails, and the launcher exits silently.
In my case these two combined: issue 2 blocked every start after a host reboot, and the first start after I worked around it triggered issue 1.
The root cause of issue 1 is in dockurr/windows code; I am filing here because the user-facing integration and lifecycle are Omarchy's, and Omarchy could add safeguards. It may need to be forwarded upstream.
Everything below comes from a real incident, logs and file timestamps, and read-only inspection of the exact code that was running (scripts copied from the running container). I did not intentionally reproduce the data loss.
Environment
- Omarchy
4.0.3-1(package),omarchy-windows-vmfrom that package - Docker
29.7.2, docker-compose5.5.1, KVM - Image as written by
omarchy-windows-vm install:dockurr/windows(no tag/digest pin → resolved tolatest)org.opencontainers.image.version:6.05org.opencontainers.image.revision:efe47da76d49c9d77c0a26799c70315fa4d91055- image ID / repo digest:
sha256:0cff9eb0e7aee9953e55bc682852ca4fdca233145a58ae1ec94f0b0c01a2ed30
- Installer defaults:
VERSION=11, 4G RAM, 2 cores, 64G disk; storage~/.windows, shared~/Windows - Host filesystem: Btrfs
Sequence of events
omarchy-windows-vm installcreated and started the container. Windows 11 was downloaded and installed unattended. The firmware log shows Windows Boot Manager started from the virtual hard disk twice (about 7 and 10 minutes after first boot).- The container kept running and was never stopped. Its on-disk state afterwards still showed running, with no finish time.
- Roughly 20 minutes later the host froze for an unknown reason and had to be powered off. I am not attributing the freeze to Omarchy or dockurr/windows.
- After reboot, the storage directory contained
data.img(read-only check: Windows GPT layout with EFI/MSR/NTFS/recovery partitions, NTFS system volume not dirty) and the oldsetup.img, but nowindows.boot. - Launching "Windows" from the menu failed silently; Docker was never even started (issue 2).
- I cleared the setgid bit on
~/Windowsand ran the privileged start actionomarchy-windows-vm __priv up, which runs the same mount/permission preflight. The preflight passed. - On that start the container logged:
The new
Creating overlay image... Adding win11x64.xml for automatic installation... Creating a 64 GB growable disk image in raw format... BdsDxe: skipped Boot0003 "UEFI QEMU QEMU HARDDISK" ... BdsDxe: starting Boot0002 "UEFI QEMU DVD-ROM ..."data.imghas a file birth time 1.9 s after the container'sStartedAt, matching that log line to the millisecond. The previous image was gone, and allwindows.*,.romand.varsstate files had been recreated (only the ISO was kept). A fresh unattended installation started. - After this start
~/Windowswas mode2777again.
The lost VM was a fresh test installation with no important data, but the same mechanism would destroy a VM that has real data.
Issue 1: an existing disk is deleted when the completion marker was never written
Code is from dockurr/windows 6.05 (/run/*.sh in the running container).
Install decision (install.sh, needsInstall):
if [ -n "$previousBase" ] && [[ "${STORAGE,,}/${previousBase,,}" != "${iso,,}" ]]; then
...
return 0
fi
if [ -n "$previousBase" ] && hasSystemImage; then
return 1
fi
hasData && hasBootMarker && return 1
return 0hasBootMarkerchecks$STORAGE/windows.boot.hasDatais true when the first 100 KiB of the disk are non-zero.- So a non-empty disk without the marker means "install".
Deletion (install.sh): startInstall unconditionally calls cleanupStorage, which runs:
find "$STORAGE" -maxdepth 1 -type f -iname 'data.*' -not -iname '*.iso' -deleteIn this incident windows.base matched the current install source (win11x64.iso), so the backup/discard branches of cleanupInstall did not apply, and the deletion came from cleanupStorage. For context:
discardPreviousalso deletesdata.*when the install source changed, or whenwindows.baseis missing, andhasCompletedInstallis false.backupPreviousis used only whenhasCompletedInstallis true.
Marker creation (power.sh):
markWindowsBooted()is the only place that createswindows.boot.- It is called only from
finish(), which runs when the container is shut down (the SIGTERM/SIGHUP/SIGABRT/SIGQUIT trap →gracefulShutdown→finish) or when QEMU exits. - Even then it writes the marker only if
ready()confirms that the last firmware boot entry was Windows Boot Manager from the hard disk, and the install ISO still exists. - No path writes the marker while the VM is running.
Consequence: a VM that was installed but never cleanly stopped has no marker. Any interruption that bypasses finish() (host crash, power loss, SIGKILL, OOM kill) makes the next start delete the installed disk and reinstall, with no confirmation and no backup.
Steps to reproduce
Derived from the code; not executed. Use a disposable VM only.
omarchy-windows-vm installand wait until Windows is fully installed.- Do not stop the VM. Interrupt it uncleanly, e.g.
docker kill --signal KILL omarchy-windows(or power off the host). - Confirm the storage dir has a non-empty
data.imgand nowindows.boot. - Start again (
omarchy-windows-vm __priv up, orlaunchonce issue 2 is worked around).
Expected per code: "Creating a 64 GB growable disk image…", the previous data.img is deleted, and a fresh install starts.
Possible workaround
From the code; not verified by me: after the first installation, stop the VM once with omarchy-windows-vm stop, then confirm that windows.boot exists in the storage directory before relying on the VM.
Suggested safeguards
- dockurr/windows:
- never auto-delete a non-empty
data.*just because the marker is missing — abort, or rename it to a backup name first; - write the marker at runtime as soon as
ready()confirms a boot from the installed disk.
- never auto-delete a non-empty
- Omarchy:
- before
up, refuse to start (or back up the disk) whendata.*is non-empty butwindows.bootis missing; - after install, guide the user through one clean stop;
- document the risk;
- consider pinning the image digest;
- forward the lifecycle part upstream.
- before
Issue 2: launcher permission check fails on a setgid shared folder
omarchy-windows-vm, prepare_caller_mounts (runs as root on every up / up_wait):
chmod 0700 -- "/proc/$BASHPID/fd/$storage_fd" "/proc/$BASHPID/fd/$shared_fd" || { ...; return 1; }
storage_mode=$(stat -Lc '%a' "/proc/$BASHPID/fd/$storage_fd" 2>/dev/null) || storage_mode=""
shared_mode=$(stat -Lc '%a' "/proc/$BASHPID/fd/$shared_fd" 2>/dev/null) || shared_mode=""
if [[ $storage_mode != 700 || $shared_mode != 700 ]]; then
...
return 1
fimounted_leaf_matches also requires $mode == 700.
- dockurr/windows side:
samba.sh(addShare) runschmod 2777 "$dir"on an empty, writable shared directory, so after a container start~/Windowsis2777. - Why
chmod 0700is not enough: GNUchmodwith an octal mode of four or fewer digits preserves the setuid/setgid bits on directories.2777becomes2700,stat %areturns2700, and the function returns 1 without any message. Verified on the affected system:chmod 0700leaves2700,chmod 00700yields700. - Result: after the container's first start, every later
launch/up_waitfails while the shared folder is empty. From the menu (Terminal=false) the user gets no diagnostic beyond the generic "Failed to start Windows VM" notification.
Suggested fixes
- Use
chmod 00700(orchmod u=rwx,go=,g-s) where the directory mode is normalized, and/or validate only the access bits ((mode & 0777) == 0700) instead of the exact%astring. - Print the specific failing path, the actual mode and the expected mode to stderr, and surface it in the notification.
- Note that dockurr/windows'
2777also leaves the shared folder world-writable while the VM runs.
Scope and uncertainty
- The cause of the host freeze is unknown and not attributed to Omarchy or dockurr/windows.
- The data loss was not intentionally reproduced. The mechanism comes from the exact code that was running plus the incident timeline; the deletion itself is not logged by dockurr/windows, but the new disk's creation time matches its log line.
- The lost VM was a recent test install with no important data.
I can provide the sanitized timeline and full code/log excerpts if helpful.
Activity
windows-vm: actually clear setgid on the shared/storage source before…
windows-vm: actually clear setgid on the shared/storage source before…
hanzxtf commentedon Sep 30, 2026
+1; just had this same issue.
installed win11 and had it fully configured with office 2024 and then next day i check, there is no windows.
omarchybot commentedon Oct 4, 2026
Following the merge of #12323, The shared-folder permission refusal is fixed. The reported dockurr reinstall/data-loss incident is separate and remains unresolved; keep the issue open for it.
Scope checked by GPT-6 in Codex and Codex Medium against the discussion and landed change; review independence is not guaranteed. No tests were rerun for this cleanup.
Omabot on behalf of DHH
omarchybot commentedon Oct 11, 2026
I read the data-loss half of this against current
dockur/windowsmaster (after v6.06) and Omarchy'squattro. The mechanism you describe holds in source, with one refinement. I did not reproduce it: that needs a full Windows install under nested KVM, so this is proven by reading the code, not by running it.needsInstall()insrc/install.shkeeps an existing disk only when bothhasDataandhasBootMarkerhold, or when awindows.systemimage is recorded. A non-emptydata.imgwith nowindows.bootand an unchanged install source goes tostartInstall(), which callscleanupStorage(). That deletes everydata.*in storage with no size check, prompt or backup.windows.bootis written only bymarkWindowsBooted()insrc/power.sh, and its only caller isfinish(). The refinement:finish()runs on a graceful container stop and when QEMU exits by itself (src/entry.sh), so it is not limited to a clean stop. What skips it is anything that kills the container's entrypoint first: host power loss, a hard freeze,docker kill -s KILL, the OOM killer. Nothing writes the marker while the VM is running.omarchy-windows-vm installstarts the container detached and leaves it running (bin/omarchy-windows-vm,install_windows), and__priv_up/__priv_up_waitcheck mounts but not disk or marker state. A freshly installed VM therefore has no marker until something stops it. In practice that is usually the firstlaunchwithout--keep-alive, which runsdocker compose downwhen RDP closes. A host crash before that point loses the install.The setgid half is fixed on
quattroby #12323, as noted above. The privileged preflight now runschmod u=rwx,go=,a-sbefore it requires mode 700.No fix is drafted, because every Omarchy-side safeguard is a choice about behaviour rather than a correction. Seen from outside, a non-empty
data.imgwith nowindows.bootlooks the same whether the install was interrupted half way (and should restart) or finished but was never stopped (and should be kept). Refusing to start, backing up the disk first, or stopping the VM once after install each trades one of those cases against the other. Pinning the image digest would not change this mechanism. The underlying fix belongs upstream indockur/windows: write the marker at runtime once the firmware shows a boot from the disk, or never delete a non-empty disk without a backup. No upstream issue for it turned up in a search. Until then, the workaround from the report holds: after the first install, runomarchy-windows-vm stoponce and check thatwindows.bootexists in~/.windows.Checked by Opus 5.5 in Claude Code, with Codex Medium as a second opinion. Codex independently traced the same deletion path and contributed the
finish()-on-QEMU-exit refinement above. It also agreed that no guard can tell the two states apart. This now waits on the maintainer: whether to add a safeguard inomarchy-windows-vm, and whether to report it upstream.jandrusk commentedon Oct 13, 2026
The setgid /
== 700launcher failure is the same cluster as #14445, #14196, #11879 — fixes in flight at #12264, #12046, #9783.The data.img wipe on unclean stop looks distinct and serious (silent reinstall). Can you confirm:
omarchy-windows-vm stop→ next start deletes data.img)?~/.local/share/omarchy/windows-vm(or the compose data path) still holds an install-complete marker after that path?If we can reproduce the wipe, that should be P1 separate from the setgid fix.