Omarchy 内置 Windows 虚拟机实战:用 Docker + KVM 在 Linux 桌面里跑 Windows 11
Omarchy 把"在 Linux 上运行 Windows"做成了一个开箱即用的功能模块:它基于 Docker 运行的特权级 Windows 虚拟机,通过一键安装脚本、应用启动器入口和全屏 RDP 连接,把传统上复杂的虚拟化配置收敛为几个交互式提问和一条命令。本文以 Windows VM 指南 为主线,结合其底层实现脚本与测试,完整讲解从安装前提、交互配置、日常使用、文件共享与安全边界,到资源调整、OEM 许可与彻底卸载的每一个环节。
读完本文,你将掌握:如何通过 Omarchy 菜单安装并启动一个 Windows 11 虚拟机、如何用 omarchy windows vm 系列命令管理它的生命周期、~/Windows 与 ~/.windows 的共享与隔离机制、以及这套方案在"零 GPU 透传"前提下的适用边界。
Windows VM 是什么:方案概览与前提条件
在 Omarchy 中,Windows 虚拟机并不是用 virt-manager 或 libvirt 搭建的,而是由脚本 bin/omarchy-windows-vm 驱动的一套 Docker + KVM 组合:一个基于 dockurr/windows 镜像的容器(容器名为 omarchy-windows),直接把宿主的 /dev/kvm 与 /dev/net/tun 设备透传给内部的 Windows 11 客户机,再通过 FreeRDP 提供桌面访问。
整个安装流程的入口是 Omarchy 菜单(Super + Space 唤起)中的 Install > Windows。该菜单项在仓库中的定义位于 default/omarchy/omarchy-menu.jsonc,其行为是拉起一个浮动的演示终端并执行 omarchy-windows-vm install;与之对应的 Remove > Windows 菜单项则定义在同文件的 L295 行附近,且仅在检测到 Windows 已安装(存在 windows-vm.desktop)时才显示。
动手安装前,请先确认两个硬性前提:
- KVM 虚拟化可用。绝大多数现代 CPU 都支持,但有时会在 BIOS 里被关闭。安装脚本会直接检测
/dev/kvm是否存在:不存在时报错并提示启用虚拟化或手动加载内核模块(Intel 用modprobe kvm-intel,AMD 用modprobe kvm-amd),对应逻辑见 bin/omarchy-windows-vm 中的check_prerequisites。多数情况下这只是进 BIOS 打开一个开关的事。 - 足够的磁盘空间。你分配给 Windows 的磁盘容量之外,还需要约 10GB 用于存放 Windows 11 安装镜像与系统开销,即总需求 ≈ 分配给 Windows 的容量 + 10GB。脚本用
df检查的是 实际存放虚拟磁盘~/.windows的那个文件系统的剩余空间,而不是笼统的整个家目录所在分区(见storage_space_path/available_storage_gb)。若~/.windows已是软链接,则按其指向的真实目录所在文件系统计算,这点对"把虚拟磁盘放到大容量盘上"的用户尤其关键。
安装交互:资源分配、凭据与下载进度
运行安装后,脚本先自动安装 freerdp、openbsd-netcat、gum 等依赖(用 omarchy-pkg-add),然后基于当前机器实测到的资源(总内存、CPU 核数)与可用磁盘,用 gum 弹出一系列交互式问题:
| 提问项 | 可选范围 / 校验规则 | 默认值 |
|---|---|---|
| RAM 大小 | {2,4,8,16,32,64}G 中不超过本机总内存的档位 |
4G |
| CPU 核数 | 正整数且不超过本机逻辑核数(1 起步) |
2 |
| 磁盘容量 | {32,64,128,256,512}G 中不超过"可用空间 − 10GB"的档位 |
64G(不可用时回退 32G) |
| Windows 用户名 | 字母/数字/_/-,最长 20 字符 |
docker |
| Windows 密码 | 可打印字符,最长 64 字符 | admin |
从源码可以确认这些交互项的输入一律先经过正则白名单校验(valid_ram/valid_cores/valid_disk/valid_username/valid_password/valid_tz),非法输入会回退到默认值而不是透传下去。磁盘下限是 32GB,因此若可用空间不足 42GB(32G 磁盘 + 10G 镜像)安装会直接中止。配置项确认后还会展示一份摘要并二次确认,随后把时区(timedatectl 读取,失败则 UTC)一并写入配置。
真正耗时的环节是镜像下载,10–15 分钟是正常水平。脚本拉起容器后会自动用浏览器打开 http://127.0.0.1:8006,你可以在这里直观地看到 Windows 的安装进度:
上图是安装完成后在 Web 控制台里看到的 Windows 11 桌面——注意 8006 端口的控制台同样要求你输入刚才配置的 Windows 用户名和密码,这是刻意的鉴权设计(见后文"安全边界"),因此不要把两个凭据搞混。容器启动时等待客户机就绪的逻辑在 __priv_up_wait:它会锚定本次容器的启动时间轮询 Docker 日志中的 windows started successfully,每 2 秒一次,最多约 2 分钟,超时才会报错。
日常使用:从应用启动器一键进桌面
安装完成后,Omarchy 会生成一个桌面入口 ~/.local/share/applications/windows-vm.desktop(Name=Windows,执行 uwsm app -- omarchy-windows-vm launch,图标为 windows)。此后只需在应用启动器中搜索并启动 Windows:
- 若虚拟机尚未运行,它会先自动启动(冷启动约需 15–30 秒,期间可能有授权弹窗);
- 就绪后自动以全屏 RDP 接入桌面,标题为 "Windows VM - Omarchy"。
RDP 会话默认携带 声音输出、麦克风输入与共享剪贴板,在 Linux 与 Windows 之间复制文本是即点即用的。分辨率会跟随窗口大小动态调整(FreeRDP 的 /dynamic-resolution),而 HiDPI 适配也做了处理:启动时会读取 Hyprland 当前聚焦显示器的缩放比例(hyprctl monitors),≥170% 时附加 /scale:180,≥130% 时附加 /scale:140,低于 130% 则不设置缩放、保持默认 100%,避免高分屏上出现模糊的界面。若你的账户名/密码不是默认值,凭据会从用户私有文件 ~/.config/windows/credentials(权限 0600,仅本人可读)读取,不会明文暴露给其他账户。
关闭 RDP 窗口的默认行为是自动关闭虚拟机——脚本在 RDP 退出后会自动执行 docker compose down。如果你有任务需要在里面继续跑、不希望窗口一关就被停机,改用保活模式启动:
omarchy windows vm launch --keep-alive
# 或短选项:omarchy-windows-vm launch -k
其余生命周期控制都集中在同一条命令上:
omarchy windows vm status # 它现在在运行吗?
omarchy windows vm stop # 关闭虚拟机
omarchy windows vm launch # 启动并接入 RDP
这些是文档层面的便捷形式,其底层实现是 bin/omarchy-windows-vm 中的 omarchy-windows-vm status | stop | launch 三个子命令;脚本自带的完整子命令还包括 install、remove,运行 omarchy-windows-vm help 可查看全部用法与示例。
值得注意的实现细节:compose 中 restart: "no",意味着虚拟机不会在宿主开机时自动拉起,必须由你显式启动。这既是省资源的默认策略,也被测试 windows-vm-test.sh 固化为契约(断言配置中存在 restart: "no" 且不允许 unless-stopped)。同一份测试还断言了 RDP 窗口在 Hyprland 中的视觉表现:匹配 class = "^xfreerdp$"、title = "^Windows VM - Omarchy$" 的窗口会通过 default/hypr/apps/windows-vm.lua 相关的应用规则保持完全不透明(opacity = "1 1")。
生命周期与权限模型(实现侧)
由于该容器运行在特权模式(需要 KVM、/dev/net/tun、NET_ADMIN 能力),对 Docker 的每次访问都默认经 polkit(pkexec)授权;若你开启了 sudoless Docker(即已加入 docker 组),则免弹窗直达。需要了解的是:omarchy-windows-vm 每次启动、停止都会触发一次独立的授权,且脚本会把特权进程的 PATH 固定为系统目录、LC_ALL 固定为 C.UTF-8,并只允许执行确认为 root 所有、非用户可写的目标二进制,从根上堵住 PATH 注入与用户态劫持(priv_target 对整条路径逐级校验属主与写权限)。这种对"谁有资格以 root 身份执行什么"的严格约束贯穿整个实现,是理解下文安全设计的前提。
文件共享与安全边界:~/Windows 之外的区域一概不可见
这套方案在文件互通与隔离上做了明确的切分:
~/Windows(共享目录):自动共享给 Windows 客户机,映射为容器内的/shared。想往 Windows 里带文件,就放进这个目录。~/.windows(磁盘目录):Windows 自己的虚拟磁盘,映射为容器内的/storage。安装时你分配的那个 64GB(或更大)空间就落在这里。
客户机对宿主文件系统的其余部分没有任何访问能力——虚拟磁盘之外,Docker 只把上述两个目录挂进容器,因此即使 Windows 侧出了什么乱子,也波及不到你的其他文件。
一些值得展开的细节:
- 两个目录可以是你拥有的软链接。当虚拟磁盘想放在更大容量的独立分区上时,把
~/.windows指过去即可;安装脚本校验可用空间时也以软链接实际指向目录所在文件系统为准。 - 两者必须是相互独立、互不重叠的目录。删除流程尤其较真:卸载时
remove会清空磁盘目录、但刻意保留共享目录,并在任何删除动作之前执行一次有界包含关系校验——从受保护的挂载锚点递归搜索两个目录树,确认谁都不在对方树内(因为 bind 别名可能让同一目录出现第二条父链,仅向上遍历是不够的)。为了避免恶意构造的目录树拖垮特权操作,遍历设有硬性超时(5 秒扫描、1 秒宽限),超时或无法证明两棵树分离时一律拒绝删除。对应测试见 windows-vm-mount-boundary-test.sh。
防文件交换的挂载管线
这是整个实现里最值得读源码的部分。为了防止"另一个以你身份运行的进程在路径通过检查后、容器消费前偷换目录"(经典的 TOCTOU 攻击面),启动前的挂载流程是这样的(见 bin/omarchy-windows-vm 的 resolve_caller/open_mount_source/bind_mount_leaf 等函数):
- 先把
~/.windows与~/Windows两个源目录打开并钉住(对目录本身持有文件描述符,并在/proc/$BASHPID/fd上记录其uid|设备号:inode); - 将这两个确切的目录 inode bind-mount 到 root 保护的每用户私有锚点(
/var/lib/omarchy/windows/mounts/users/<uid>/storage与.../shared,锚点树由 root 持有、逐级校验属主与写权限); - Docker 容器只看到这些 root 保护的锚点路径,永远接触不到你 HOME 下的真实路径。
这个设计既保住了"磁盘目录可以是自定义位置/软链接"的灵活性,又保证了特权容器消费的永远是那个刚被校验过的确切 inode——路径名在被检查后被改名或替换,都不会影响已经打开并钉住的那个目录。迁移期间,旧有的磁盘/共享目录会被收紧为 0700 权限,其他本地账户无法浏览其中的内容。
网络与端口的默认封锁
- 容器端口只绑定到 localhost:
127.0.0.1:8006(Web 控制台)、127.0.0.1:3389(RDP,TCP/UDP 均有)。因此局域网内任何其他机器都无法摸到这台 Windows。 - Web 控制台(8006)强制要求配置的 Windows 用户名与密码(compose 中对应
PROTECT: "Y"),防止同一宿主上的其他本地账户通过该端口驱动你的虚拟机。
生成的 compose 文件内容相当直白,核心字段如下(实际文件由安装时生成于 /var/lib/omarchy/windows/docker-compose.yml,image: dockurr/windows,容器名 omarchy-windows):
services:
windows:
image: dockurr/windows
container_name: omarchy-windows
environment:
VERSION: "11"
RAM_SIZE: "8G"
CPU_CORES: "4"
DISK_SIZE: "64G"
USERNAME: "<你配置的用户名>"
PASSWORD: "<写入时经过两层转义的密码>"
PROTECT: "Y"
TZ: "<你的时区>"
devices: [/dev/kvm, /dev/net/tun]
cap_add: [NET_ADMIN]
ports:
- 127.0.0.1:8006:8006
- 127.0.0.1:3389:3389/tcp
- 127.0.0.1:3389:3389/udp
volumes:
- /var/lib/omarchy/windows/mounts/users/<uid>/storage:/storage
- /var/lib/omarchy/windows/mounts/users/<uid>/shared:/shared
restart: "no"
stop_grace_period: 2m
密码在写入前会做两层转义(先对 YAML 解析层转义反斜杠与双引号,再对 compose 变量插值层把 $ 转成 $$),因此密码里即使含 "、\ 或 $ 也能原样到达客户机。compose 文件归 root 所有、模式 0640,并且只能由提升权限的、经过输入校验的写入动作修改——这一点在文件头部的注释里被强调为"曾经的 ~/.config/windows 版本存在被恶意进程改写、进而借特权 bring-up 把整个磁盘挂进容器的风险,不要移回 $HOME 下"。也就是说,任何以你身份运行的程序都无法改写该文件来请求特权容器执行超出预期的挂载。
调整资源与自定义:重跑安装或手改 compose
想改内存/CPU/磁盘等资源分配,重新运行一遍安装即可:omarchy-windows-vm install 会按你的新答案重写虚拟机配置。数据不会被清除——compose 的卷路径在重装/迁移过程中被保留。
如果你需要更深层的自定义(例如把 USB 设备挂进容器),文档允许你用 sudo 手改 /var/lib/omarchy/windows/docker-compose.yml,所有可用选项参见 Dockur Windows 开源项目(本方案即基于该项目的容器方案)。改完用 omarchy windows vm launch 重启生效即可。历史版本若把 compose 放在 ~/.config/windows/ 下,脚本的 migrate_legacy_compose 会在升级后自动把配置迁入新的 root 所有位置,并通过相同的卷路径保留既有虚拟机数据,无需重新下载安装。
限制与许可:没有 GPU 透传的清醒认知
务必理解这套方案的能力边界,文档对此直言不讳:
- 没有 GPU 透传,因此不适合游戏或视频剪辑这类强图形负载;
- 它擅长的场景是"把 Microsoft Office 之类非跑不可的 Windows 软件跑起来",属于典型的生产力补充而非游戏方案。
软件版本方面:安装的是 Windows 11 Pro(未激活),使用受门控的功能需要自备正版授权密钥。有一个很容易被忽略的细节:如果你的电脑出厂自带 Windows,OEM 密钥仍留在固件(UEFI/ACPI)里,即使已经整体改装为 Omarchy 也不会消失。用以下命令即可把它打印出来:
omarchy windows key
该密钥与这台机器硬件绑定:它能为"在这台硬件上重装的 Windows"完成激活,但通常不能激活虚拟机——因为虚拟机呈现的硬件与原始固件并不一致。
彻底卸载:Remove > Windows
不需要虚拟机时,通过 Omarchy 菜单的 Remove > Windows(即 omarchy-windows-vm remove)整体移除。这一步会先要求确认,然后:停止并删除容器、移除镜像、删除虚拟磁盘目录 ~/.windows 及其中的数据,同时保留共享目录 ~/Windows。因此文档特别提醒:删除前务必先把任何还需要的文件从 ~/Windows 挪出来。
从实现上看,remove 是唯一会在删除前执行"递归式 bind 别名证明"的路径:它会先重建并核对两个受保护挂载、确认没有额外的或叠加的挂载层、确认容器确已停止且 Docker 状态可验证,再做包含关系校验与 find -xdev 的定向清空——-xdev 保证遍历绝不跨入其它已挂载文件系统。任何一步校验失败都会中止删除并保留数据,宁可失败也不误删。对应的防回归测试见 windows-vm-compose-test.sh 与 windows-key-test.sh(后者守护 OEM 密钥打印功能)。
命令速查
| 目的 | 命令 |
|---|---|
| 首次安装 / 重新配置资源 | omarchy-windows-vm install |
| 启动并全屏 RDP 接入(关窗即停) | omarchy windows vm launch |
| 启动并保持运行 | omarchy windows vm launch --keep-alive(或 -k) |
| 查询运行状态 | omarchy windows vm status |
| 手动停止 | omarchy windows vm stop |
| 打印固件中的 OEM Windows 密钥 | omarchy windows key |
| 彻底卸载(清空虚拟磁盘) | omarchy-windows-vm remove(或菜单 Remove > Windows) |
| 安装进度监控 | 浏览器访问 http://127.0.0.1:8006 |
文中所有实现层面的描述均可在仓库源码中复核:核心逻辑集中在 bin/omarchy-windows-vm,菜单入口见 default/omarchy/omarchy-menu.jsonc,行为契约则由 test/shell.d/windows-vm-test.sh、windows-vm-mount-boundary-test.sh、windows-vm-compose-test.sh 与 windows-key-test.sh 共同守护。如果你正在评估"在 Linux 桌面上有没有一台够用的 Windows",Omarchy 这条 Docker + KVM + RDP 的路线以极少的配置成本换来了清晰隔离与完整生命周期管理,值得直接上手一试。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
