一劳永逸的无人值守安装

在 KVM 上全新自动安装 Omarchy 4.0.2(Arch + Hyprland),并一次性配好 RDP 远程桌面。网络固定 IP、网关、DNS 直接写进无人值守脚本,装完即是固定 IP。用 <占位符> 替换成你自己的值即可(密码 = 用户名)。

关键三步

1. 强制 UEFI(必须)

Omarchy 按 UEFI + Limine 装系统,传统 BIOS(SeaBIOS)会黑屏装不上。libvirt XML 指定 OVMF:

<os firmware="efi">
  <loader type="pflash" readonly="yes">/usr/share/OVMF/OVMF_CODE_4M.fd</loader>
  <nvram>/var/lib/libvirt/qemu/nvram/omarchy_VARS.fd</nvram>
</os>

2. 磁盘大小两处配套

  • 虚拟盘qemu-img create -f qcow2 ...qcow2 <大小>G
  • 根分区user_configuration.jsondisk_config 里第二个分区 size.valueMiB(如 100GiB = 101887
  • ⚠️ 虚拟盘必须 ≥ 分区总量 + 余量(≥128G 起步),否则报 Partition overlaps backup GPT header

3. cidata 无人值守(就两个文件)

cidata/
├── user_configuration.json     # 分区 + 引导器 + 主机名 + 固定IP + 密码哈希
└── user_credentials.json       # 用户名 + 密码(密码 = 用户名)

固定 IP 关键:custom_commandsprintf 写入 NetworkManager keyfile lan.nmconnection

"custom_commands": [
  "mkdir -p /etc/NetworkManager/system-connections",
  "printf '[connection]\\nid=lan\\ntype=ethernet\\ninterface-name=enp1s0\\nautoconnect=true\\n\\n[ipv4]\\nmethod=manual\\naddress1=<VM_IP>/24,<网关DNS_IP>\\ndns=<网关DNS_IP>;\\n\\n[ipv6]\\nmethod=ignore\\n' > /etc/NetworkManager/system-connections/lan.nmconnection",
  "chmod 600 /etc/NetworkManager/system-connections/lan.nmconnection",
  "systemctl enable NetworkManager"
]

密码哈希用 openssl passwd -6 '<用户名>' 生成,登录时用户名和密码填同一个

打包启动

genisoimage -output cidata.iso -volid cidata -joliet -rock cidata/
# 或 xorriso -as mkisofs -o cidata.iso -V cidata -J -r cidata/
  • cdrom1 = Omarchy 系统 ISO
  • cdrom2 = cidata.iso(卷标 cidata
  • 接任意能出网的网络即可(固定 IP 在脚本里,网段无 DHCP 也不受影响)

装完自动进 Hyprland 桌面。

装后:sudo 免密 + RDP

sudo 免密(VM 关机时,host 端离线做):

sudo virt-customize -a omarchy.qcow2 --run-command \
  'echo "<用户名> ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/<用户名>-nopass && chmod 440 /etc/sudoers.d/<用户名>-nopass'

RDP 用 hypr-rdp(Hyprland 是 Wayland,xrdp 不可用且只服务 X11):

yay -S --noconfirm --needed hypr-rdp
# ~/.config/hypr-rdp/config.toml
cat > ~/.config/hypr-rdp/config.toml <<EOF
bind = "0.0.0.0:3389"
username = "<用户名>"
password = "<用户名>"
capture_mode = "wlr"
fps = 30
EOF
echo 'o.launch_on_start("hypr-rdp")' >> ~/.config/hypr/autostart.lua
printf '[Autologin]\nSession=hyprland\nUser=<用户名>\n' | sudo tee /etc/sddm.conf.d/autologin.conf
sudo ufw allow 3389/tcp
sudo systemctl reboot

微软远程桌面连 <VM_IP>,用户名密码都填 <用户名>,自签证书点接受即进桌面。

踩过的坑

  1. Secure Boot 固件(.ms.fd) → 卡死 UEFI 阶段virt-install --boot uefi 会自动选 OVMF_CODE_4M.ms.fd(Secure Boot 固件)并加 secure='yes',而 Omarchy 手册强制要求关闭 Secure Boot/TPM,否则 VM 卡死在 UEFI 阶段。修:用非 Secure Boot 固件 OVMF_CODE_4M.fd<loader secure='no'>、别用 .ms.fd。(整合包 create-vm.sh 已内置防呆自动拦截)
  2. BIOS/UEFI 不匹配 → 黑屏:必须 UEFI(OVMF) + Limine;KVM 下 Hyprland 花屏可在 Limine cmdline 加 nomodeset
  3. 网卡名:virtio 默认 enp1s0,用别的型号改 interface-name
  4. 磁盘太小 → GPT 头重叠:虚拟盘 ≥ 分区总量 + 余量(≥128G)
  5. /bootesp → Installation halted:分区 flags 必须含 "esp"
  6. ACPI 重启无效:KVM guest 常不响应 virsh reboot,用 guest 内 sudo systemctl reboot;改盘前确认真关机
  7. 无 root 密码 → sudo 免密必备:装完只有「用户名=密码」能登录,提权靠注入的 sudo 规则
  8. 嵌套 SSH / 引号坏:命令写脚本文件 scp 过去、ssh host "bash -s" < script.sh 喂执行,别用深层嵌套引号
  9. RDP 用 hypr-rdp,别用 xrdp:xrdp 只服务 X11,连不了 Hyprland

配套脚本

🎁 完整一键安装整合包可直接下载(含全部脚本 + 配置模板 + 指南): 下载 omarchy-kvm-pack.zip (11 KB)

整合包结构:install.sh(一键编排)+ config.env.example(配置模板)+ scripts/(build-cidata.sh / create-vm.sh / virt-inject.sh / rdp-setup.sh / fix-cidata.py)+ GUIDE.md用法cp config.env.example config.env → 填好 → ./install.sh,自动完成 打包 cidata 盘 + 建 UEFI VM + 启动安装。

下方是核心脚本要点,完整可运行版以上面整合包为准。

无人值守安装配套的 3 个脚本,直接复制保存使用(完整版见上方整合包)。

make-cidata.sh —— 打包无人值守盘

#!/bin/bash
# make-cidata.sh — 把 cidata 目录打包成无人值守安装盘(密码登录,无公钥)
# 用法: bash make-cidata.sh <cidata目录> [输出iso]
set -e
SRC="${1:?用法: make-cidata.sh <cidata目录> [输出iso]}"
OUT="${2:-/tmp/cidata.iso}"
for f in user_configuration.json user_credentials.json; do
  [ -f "$SRC/$f" ] || { echo "缺少 $SRC/$f" >&2; exit 1; }
done
if command -v genisoimage >/dev/null; then
  genisoimage -output "$OUT" -volid cidata -joliet -rock "$SRC"
elif command -v xorriso >/dev/null; then
  xorriso -as mkisofs -o "$OUT" -V cidata -J -r "$SRC"
else
  echo "需要 genisoimage 或 xorriso" >&2; exit 1
fi
echo "OK: $OUT  (用户名/密码见 user_credentials.json,密码=用户名)"

virt-inject.sh —— 装后提权(VM 关机时,host 端)

#!/bin/bash
# virt-inject.sh — 无人值守装完后的"提权配置"(不使用公钥)
# 用法(在 KVM host 上, VM 已关机): bash virt-inject.sh <qcow2路径> <用户名>
set -e
QCOW="${1:?用法: virt-inject.sh <qcow2路径> <用户名>}"
USER="${2:?缺少用户名}"
command -v virt-customize >/dev/null || { echo "需要 libguestfs-tools" >&2; exit 1; }
sudo virt-customize -a "$QCOW" --run-command \
  "systemctl enable sshd 2>/dev/null || true; \
   mkdir -p /etc/ssh/sshd_config.d && echo 'PasswordAuthentication yes' > /etc/ssh/sshd_config.d/10-passwd.conf"
sudo virt-customize -a "$QCOW" \
  --run-command "echo \"$USER ALL=(ALL) NOPASSWD: ALL\" > /etc/sudoers.d/${USER}-nopass && chmod 440 /etc/sudoers.d/${USER}-nopass"
echo "=== 完成:开机后用「用户名 = 密码」SSH 登录(不依赖公钥)==="

rdp-setup.sh —— RDP 一键配置

#!/bin/bash
# rdp-setup.sh — Hyprland 原生 RDP(hypr-rdp) + 自动登录 + 防火墙 一键配置
# 在 guest(omarchy) 内以普通用户执行;sudo 需可用(建议先 virt-inject)
set -e
RDP_USER="${RDP_USER:-<用户名>}"
RDP_PASS="${RDP_PASS:-$RDP_USER}"        # 密码 = 用户名
echo "=== 1. 安装 hypr-rdp (AUR, 约5分钟编译) ==="
yay -S --noconfirm --needed hypr-rdp || sudo -n pacman -S --noconfirm hypr-rdp
echo "=== 2. 写配置 ~/.config/hypr-rdp/config.toml ==="
mkdir -p ~/.config/hypr-rdp
cat > ~/.config/hypr-rdp/config.toml <<EOF
bind = "0.0.0.0:3389"
username = "$RDP_USER"
password = "$RDP_PASS"
capture_mode = "wlr"
fps = 30
EOF
echo "=== 3. 随 Hyprland 自启 (Omarchy lua 配置) ==="
grep -q 'hypr-rdp' ~/.config/hypr/autostart.lua 2>/dev/null || \
  echo 'o.launch_on_start("hypr-rdp")' >> ~/.config/hypr/autostart.lua
echo "=== 4. sddm 自动登录 Hyprland ==="
printf '[Autologin]\nSession=hyprland\nUser=%s\n' "$RDP_USER" | sudo -n tee /etc/sddm.conf.d/autologin.conf >/dev/null
echo "=== 5. 防火墙放行 3389 ==="
sudo -n ufw allow 3389/tcp 2>/dev/null || sudo -n ufw allow 3389
echo "=== 6. 重启系统生效 ==="
echo "建议: sudo systemctl reboot;之后用微软远程桌面连 <此机IP>,用户名密码都填 $RDP_USER"

另有 fix-cidata.py(自动补 /bootesp 标记并重打包)和完整的 cidata/ 两个 json,随原文档一起提供。

适合人群

需要在 KVM/虚拟机里跑 Omarchy(Arch + Hyprland)桌面、且要远程桌面访问的折腾型用户;无人值守 + 固定 IP 适合测试环境或远程开箱。