Windows 11 на Linux-сервере через KVM/QEMU

Изображение статьи

Зачем мне Windows на Linux-сервере

Для одного из проектов понадобился Word worker – отдельный сервис, который умеет открывать и обрабатывать .docx через настоящий Microsoft Word (нужен COM-движок, а не LibreOffice или python-docx – иначе вёрстка плывёт). А Word нормально живёт только на Windows.

У меня уже стоит и круглосуточно работает машина с Linux-сервером – на нём Docker, веб-сервисы, всякая мелочь. Арендовать ещё один VPS под Windows ради одной задачи – выглядело избыточно. Решил поднять Windows параллельно прямо на этой машине, как виртуалку.

Установка пакетов

Ubuntu/Debian. Ставим сразу всё, что понадобится позже – гипервизор, тулзу для создания VM, OVMF (UEFI-прошивка для виртуалок) и swtpm (эмулятор TPM):

bash

После usermod – релогин (или newgrp libvirt), иначе virsh будет ругаться на права.

Готовим ISO

Нужны два ISO: сам Windows 11 и virtio-win.iso с драйверами для виртуального диска и сетевой карты. Без virtio-драйверов установщик Windows тупо не увидит наш диск на этапе выбора раздела.

virtio-win.iso берём с официального репозитория Fedora: fedorapeople.org/groups/virt/virtio-win. Windows-ISO – с любого источника, который тебе нравится.

Кладём оба файла в /var/lib/libvirt/images/ и переименовываем в короткие имена, чтобы потом команда не была километровой:

bash

Bridge-сеть и потерянный SSH

Чтобы виртуалка была видна в локалке (а не сидела за NAT'ом libvirt), нужен мост br0 поверх физического интерфейса. Конфиг netplan /etc/netplan/01-bridge.yaml:

yaml·/etc/netplan/01-netcfg.yaml

Я указал статический IP вместо DHCP – для сервера так удобнее, не плавает между перезагрузками. eno1 – это имя моей сетевой карты, у тебя оно может быть другим (ip a покажет, типа enp3s0 или ens18). <HOST_IP> и <GATEWAY_IP> – IP сервера и роутера в твоей LAN (например, 192.168.1.50/24 и 192.168.1.1). Применяем:

bash

Грабли №2: TPM 2.0 и Secure Boot

Первая попытка запустить установку – самая наивная (i440fx + BIOS):

bash

Установщик загружается, но сразу выкидывает экран: «Компьютер должен поддерживать доверенный платформенный модуль 2.0. Компьютер должен поддерживать безопасную загрузку».

Windows 11 проверяет vTPM 2.0 и Secure Boot прямо на этапе установки. Без них дальше не пройти – это не обходится через какой-нибудь tweak в реестре установщика. Нужен честный UEFI-запуск с эмулятором TPM.

Грабли №3: No bootable option or device was found

Логика: переключусь на q35 (это уже UEFI-совместимый чипсет), добавлю --boot cdrom,hd,menu=on – и поедет:

bash

И тут на VNC меня встречает грустный экран No bootable option or device was found и Boot Manager:

UEFI Boot Manager Menu – список устройств загрузки

Причина в том, что --machine q35 сам по себе не включает UEFI – нужно явно указать OVMF-loader. Без него q35 пытается грузиться в legacy-BIOS-режиме, на котором эта связка работает криво, и установщик не находит загрузочное устройство. Плюс синтаксис --boot cdrom,hd,menu=on – устаревший, для UEFI нужен --boot loader=/usr/share/OVMF/OVMF_CODE_4M.secboot.fd,loader.readonly=yes.

Рабочая команда: UEFI + Secure Boot + vTPM 2.0

Финальный вариант, который успешно стартует и доходит до установщика Windows:

bash

Что тут важного:

  • OVMF_CODE_4M.secboot.fd + loader.secure=yes – UEFI-прошивка с включённым Secure Boot.
  • smm.state=on – System Management Mode. Без него Secure Boot молча не работает: Windows-проверка вроде бы проходит, но реально ключи не сохраняются.
  • nvram.template=OVMF_VARS_4M.fd – отдельный NVRAM на каждую VM (там Secure Boot хранит свои ключи).
  • --tpm backend.type=emulator,backend.version=2.0,model=tpm-crb – даёт виртуальный TPM 2.0 через swtpm, именно его проверяет установщик.
  • --cpu host-passthrough – пробрасываем все флаги CPU как есть. Win 11 проверяет конкретные инструкции – без passthrough может ругаться на «неподдерживаемый процессор».

Если у тебя нет файла OVMF_CODE_4M.secboot.fd – посмотри что есть командой ls /usr/share/OVMF/. На старых Ubuntu имена без _4M: OVMF_CODE.secboot.fd и OVMF_VARS.fd.

Boot Manager и virtio-драйверы

Стартуем VM и подключаемся к VNC:

bash

vncdisplay покажет что-то вроде :0 – это значит порт 5900 (:15901, и т.д.).

На VNC видим Boot Manager (тот самый экран No bootable option). Это нормально – нужно вручную выбрать стрелками UEFI QEMU DVD-ROM QM00001 (это и есть наш windows-11.iso) и нажать Enter.

Когда выскочит надпись Press any key to boot from CD or DVD... – жми любую клавишу за 5 секунд, иначе свалишься обратно в Boot Manager.

Дальше идёт обычный установщик Windows. На шаге выбора диска список будет пустой – нужно нажать Load driver и подгрузить с virtio-win CD драйверы:

  • viostor – драйвер для virtio-диска (выбирай папку под нужную версию Win, обычно w11/amd64). Без него диск не виден.
  • NetKVM – драйвер сетевой карты virtio. Без него после установки не будет сети.

Грабли №4: VM выключилась после первой перезагрузки и не вернулась

Драйверы подгружены, диск виден, тыкнул «Установить» – установщик проглотил файлы и радостно сообщил, что «компьютер сейчас будет перезагружен автоматически». Идёт обратный отсчёт, экран гаснет, VNC показывает чёрный квадрат. Жду минуту, две – никаких признаков жизни.

Иду в virsh list --all – статус shut off. То есть VM честно выключилась, но обратно сама не поднялась: libvirt по умолчанию не рестартует домен после ACPI shutdown изнутри гостя. Лечится в одну строчку:

bash

Подключаюсь к VNC заново – установка продолжается с того места, где остановилась. Чтобы такое не повторялось дальше (Win 11 ещё несколько раз перезагружается во время установки), можно сразу включить автозапуск VM при выключении гостя:

bash

Проверка XML конфигурации

Убедимся, что все нужные фичи реально включены:

bash

Должно быть видно:

xml

VNC через SSH-туннель

--graphics vnc,listen=0.0.0.0 я оставил для удобства первой установки, но в проде так оставлять нельзя – это незашифрованный VNC, открытый в LAN.

Правильно – туннелировать через SSH:

bash

И тыкать VNC-клиентом в localhost:5900. После установки можно через virsh edit windows11 поменять listen='0.0.0.0' на listen='127.0.0.1' – тогда снаружи VNC-порт будет недоступен совсем.

Апдейт через неделю: тот же KVM, но через Docker

Через неделю после всей этой ручной настройки я наткнулся на статью на Хабре: «Запуск Windows-контейнеров под Linux и MacOS». Там показан почти тот же подход, только через образ dockurr/windows.

Важный нюанс: это не «Windows внутри обычного Docker-контейнера» в классическом смысле. Под капотом всё равно QEMU/KVM, просто Docker-контейнер берёт на себя скачивание ISO, установку, хранение диска, web-доступ и проброс RDP. То есть это тот же класс задач, но без ручного virt-install, OVMF, TPM и подгрузки virtio-драйверов в установщике.

Для моего сценария итоговый docker-compose.yml получился таким:

yaml·docker-compose.yml

Что тут важно:

  • /dev/kvm – без него это будет медленная эмуляция или контейнер вообще не стартанёт. На VPS почти всегда нужен dedicated server или включённая nested virtualization.
  • /dev/net/tun + NET_ADMIN – нужны для сетевой части VM.
  • ./windows:/storage – постоянный диск Windows. Если снести volume/папку, потеряешь установленную систему.
  • 3389/tcp и 3389/udp – нормальный RDP-доступ, сильно приятнее web/VNC.
  • 9031:5000 – мой прикладной порт под Word worker внутри Windows.
  • RAM_SIZE и CPU_CORES – сразу задают те же 8 ГБ и 4 ядра, которые я выделял руками через virt-install.

Если бы я нашёл это решение сразу, скорее всего начал бы с него. Ручной KVM/QEMU всё ещё полезен, когда нужен полный контроль над VM, сетью, UEFI, дисками и libvirt XML. Но если цель – просто поднять Windows рядом с Linux-сервисами и подключаться по RDP, Dockur закрывает задачу заметно быстрее.

Итоги

VM на 8 ГБ RAM и 4 ядрах живёт параллельно с Linux-сервером, не мешает Docker-контейнерам и остальным сервисам. Word worker крутится внутри Win, общается с основным приложением через HTTP по bridge-сети – для Linux-приложений виртуалка выглядит как обычная машина в LAN. А если не хочется руками собирать весь libvirt-пазл, можно начать с dockurr/windows: под капотом будет тот же KVM, но с гораздо более коротким входом.

Опубликовано 07.05.2026

Все статьи