import: .wiki/sources/ from MoreThenCms via subtree-split

git-subtree-dir: .wiki/sources
git-subtree-mainline: d25c0577c8
git-subtree-split: a5e96432bc
This commit is contained in:
2026-05-21 13:46:39 +03:00
4 changed files with 474 additions and 0 deletions

View File

@@ -0,0 +1,275 @@
---
title: IIS Host Migration Session 2026-05-19
type: source
tags: [migration, iis, windows, recovery, traefik]
ingested: 2026-05-19
raw_path: ../../.tasks/iis-on-host-migration.md
updated: 2026-05-21
---
# IIS Host Migration Session 2026-05-19
Перенос основного CMS-сайта (`MoreThenCms.Web`, обслуживает 11 клиентских доменов через Host header) с IIS внутри [[snolla-recovery-vm]] на нативный IIS [[windows-recovery-host]]. Цель — убрать VBox как слой нестабильности.
## Хронология
**Старт сессии** (после [[nas-recovery-session-2026-05-18]]) — VM один раз отвалилась сетью, оживили через `VBoxManage guestcontrol` + `ipconfig /release /renew` (recipe в [[vbox-windows-stability-tuning]] подтверждён ещё раз).
**Phase 1 — discovery (read-only):**
- В VM через SSH (NAT-forward 127.0.0.1:8022, не bridged 192.168.1.15 — это устарело): `appcmd list site/apppool/app/vdir /xml`.
- На хосте: `Get-WindowsOptionalFeature -Online IIS-*` через elevated PS — все нужные фичи (`IIS-WebServer`, `IIS-ASPNET45`, `IIS-NetFxExtensibility45`, `IIS-ISAPIFilter`, `IIS-ManagementConsole`, `IIS-IIS6ManagementCompatibility`, `IIS-Metabase`) уже установлены. `WebAdministration` PS-module грузится из admin-PS.
- В источниках на хосте (`grep`): hardcoded `C:\inetpub\wwwroot\` в коде CMS нет — всё через `ConfigurationManager.AppSettings["sitePath"]`. **НО** в prod Web.config (из backup `vm-sites\`) ключ `sitePath` явно проставлен абсолютным путём — патч в Phase 2 необходим.
**Phase 1 находки (важные расхождения с wiki):**
- `Snolla.IdentityManager` в VM **на :8089, не :80** (вики говорила :80). Sub-apps stostayer.old `/calc`, `/price`, `/price/tireService` существуют с отдельными app pools.
- `C:\stayer\Snolla.IdentityManager\Web.config` указывает на **`SRV-1135520-1\SQLEXPRESS` + Integrated Security** — мёртвый внешний сервер. Сайт точно не работает, deploy-артефакт без живого консьюмера.
- `C:\stayer\MoreThenCms.Web\Web.config` (stostayer) использует **внешний production MSSQL `89.253.219.2,1433`** с user `stostayer` — это **другая инфраструктура**, не наш контейнер.
**Phase 2 — миграция (admin PS):**
- `Default Web Site` остановлен (`autoStart=false`).
- `robocopy` из `C:\nas-recovery\vm-sites\``C:\sites\MoreThenCms.Web` (8.7 GB / 44.7k файлов / 1m13s) и → `C:\stayer\` (2.2 GB / 13.9k файлов / 21s).
- Web.config patch только для `C:\sites\MoreThenCms.Web\Web.config`: `sitePath` `C:\inetpub\wwwroot\MoreThenCms.Web\``C:\sites\MoreThenCms.Web\` + `Data Source=10.0.2.2``Data Source=localhost`. UTF-8 with BOM через `[System.IO.File]::WriteAllText` (паттерн из [[cms-config-rewrite-pattern]]).
- 3 AppPool (`.NET v4.0` Integrated, `ApplicationPoolIdentity`): `MoreThenCms.Web`, `stostayer`, `stostayer.old`.
- 3 IIS-сайта: `MoreThenCms.Web *:80` (catch-all), `stostayer *:8090`, `stostayer.old *:8091`.
- ACL `icacls /grant 'IIS AppPool\<site>:(OI)(CI)M' /T` на каждый physical root.
- Skip: `Snolla.IdentityManager` (по решению — публично не нужен), 3 sub-apps stostayer.old (по решению).
- Smoke test через `http://localhost/` с Host header: все 11 хостов отвечают (большинство 302 redirect — CMS работает). `rimiz.ru` — 404 (CMS-side, не инфра). stostayer/oldstostayer на :8090/:8091 — timeout, **в текущей сессии не разбирались**.
**Phase 3 — traefik switch (file-provider auto-reload):**
- Backup `*.yml.bak-phase3-2026-05-19` для 11 файлов в `C:\Users\vitya\projects\docker\diskstation\traefik\data\custom\`.
- Patch: `host.docker.internal:18080``host.docker.internal:80` (11 yml: snolla, rimiz, labtools, labtoolspro, pilorama98, tandemmebel, emspb, kupimknigi, maljarka, sestech, isc-artmaterials). UTF-8 без BOM.
- **Не трогали** `stostayer.yml` (`:18180`) и `oldstostayer.yml` (`:18181`) — stayer-сайты остаются на VM.
- Public smoke через `https://localhost:4443/` с Host header: **10/11 → HTTP 200**. Только `rimiz.ru` → 404 (как и в локальном тесте).
## Результат
Production-трафик 10 главных доменов теперь идёт **полностью без VM**: client → OpenWRT → traefik → host-IIS native → MSSQL container на хосте. VM продолжает обслуживать только stostayer/oldstostayer через старые NAT port forwards (`:18180/:18181`).
## Открытые вопросы / нюансы
- **`rimiz.ru` отдаёт 404** на host-IIS (и на VM до миграции тоже отдавал?). CMS-routing не знает этот host. Проверить mapping в БД / CMS-сайт-таблице.
- **stostayer + stostayer.old timeout** на host-IIS (`http://localhost:8090/`, `:8091`). Возможно cold-start ASP.NET, возможно реальный bug. Сейчас не разбирали — traefik для них всё ещё на VM.
- **Snolla.IdentityManager не мигрирован** — если что-то внутри CMS дёргает его через `localhost:8089`, может тихо ломаться (пока симптомов не видно).
- **VM можно глушить** только после: либо доделать stostayer на host, либо явное решение не мигрировать stayer на этот хост.
- **Phase 5 не сделан**: URL Rewrite + ARR (фикс `:4443` в редиректах CMS), Application Initialization (warm-start), log rotation `C:\inetpub\logs\`.
## Технические артефакты
- `.tasks/iis-on-host-migration.md` — детальный план фаз, обновлён со статусом Phase 1-3 = done.
- Backup конфигов traefik: `*.yml.bak-phase3-2026-05-19`.
- Скрипты Phase 2 и Phase 3 — в истории чата сессии (не сохранены отдельным файлом, идемпотентны на повторный запуск).
## Phase 4 — реорг `C:\sites\` + перенос stayer'ов (вечер 2026-05-19)
Пользователь возмутился разбросом (`C:\sites\` только для snolla, `C:\stayer\` для stayer). Реорганизация ради единой иерархии:
- `C:\sites\MoreThenCms.Web``C:\sites\snolla` (rename)
- `C:\stayer\MoreThenCms.Web``C:\sites\stostayer` (move)
- `C:\stayer\stostayer.old``C:\sites\stostayer.old` (move)
- `C:\stayer\Snolla.IdentityManager``C:\sites\snolla-identity-manager` (move + kebab-rename)
- Удалены: `C:\stayer\Mis.StoStayer.{Price,TireService,Calculator}.{Api,Web}` (sub-apps не нужны).
- `C:\stayer\` полностью удалён.
IIS sync:
- Site `MoreThenCms.Web` → переименован в `snolla` (`Set-ItemProperty IIS:\Sites\... -Name name`)
- AppPool `MoreThenCms.Web`**rename невозможен in-place**, пересоздан как `snolla` с тем же набором properties (.NET v4.0 Integrated, `ApplicationPoolIdentity`), site rebound, старый pool удалён.
- `physicalPath` всех 3 sites обновлён под `C:\sites\<name>`.
- Web.config sitePath patches:
- `C:\sites\snolla\Web.config`: `C:\sites\MoreThenCms.Web\``C:\sites\snolla\`
- `C:\sites\stostayer\Web.config`: `C:\stayer\MoreThenCms.Web\``C:\sites\stostayer\`
- ACL re-grant + cleanup stale ACE для `IIS AppPool\MoreThenCms.Web`.
## Phase 5 — stostayer.old DB-conn патч (запутанная история conn-string'ов в stayer\)
Локальный smoke `:8091` падал в timeout. Причина: `C:\sites\stostayer.old\web.config` имел `Data Source=10.0.2.2` (адрес VBox NAT gateway, на хосте не резолвится) с user=`snolla` — фактически идентичная нашему MSSQL контейнеру конфигурация, только адрес неправильный. Patch: `10.0.2.2``localhost`. После — `:8091` → 200.
Это **наша ошибка** Phase 2 — пропустили patch этого файла, так как фокус был только на `MoreThenCms.Web\Web.config`.
## Phase 6 — stostayer DB-conn миграция на новый сервер (`www.stostayer.ru`)
Локальный smoke `:8090` тоже timeout — но по другой причине. `C:\sites\stostayer\Web.config` указывал на `89.253.219.2,1433` (внешний production MSSQL), оказался **полностью недоступен**: TCP timeout, ping fail, traceroute затухает на 8-м hop у `139.45.230.171`. На VM (через тот же путь) тоже не работал — пользователь подтвердил.
Пользователь предоставил новые creds: `www.stostayer.ru,1433` / `stayer_site` / `^I9D)LB)DK)8J#xBDG$t}W&_ioaT!M!LF`. Тест из не-elevated PS: TCP reachable, SQL login OK (SQL Server 2022). Patched Web.config.
**Pitfall:** после patch IIS отдавал HTTP 500. Причина — символ `&` в пароле, который **в XML является зарезервированным**. ASP.NET Web.config XML парсер падал на парсинг conn-string. Fix: `&``&amp;` (XML entity escape). См. [[webconfig-password-xml-escape]].
## Phase 7 — отключение traefik routes для stayer'ов
Пользователь решил: stayer'ы наружу светить не нужно вообще (даже после миграции на хост). `stostayer.yml` и `oldstostayer.yml` переименованы в `*.yml.disabled` — traefik file-provider не подхватывает, route gone. Site'ы на хосте `:8090/:8091` остаются для возможного внутреннего использования.
Backup yml для этих изменений: `*.yml.bak-stayer-switch-2026-05-19` (содержит вариант с переключением на `:8090/:8091` — на случай если решим включить обратно).
## Phase 8 — VM savestate
После того как 100% prod-трафика идёт через host-IIS, VM `snolla-recovery` заморожена через `VBoxManage controlvm "snolla-recovery" savestate`. VMState = `saved`. Конфигурация и диски сохранены — resume за 5-10 сек если что-то понадобится. Не удалена.
## Финальное состояние (2026-05-19 конец сессии)
```
C:\sites\
├── snolla\ (was MoreThenCms.Web in C:\inetpub\wwwroot, was in vm-sites\wwwroot\MoreThenCms.Web)
├── stostayer\ (was C:\stayer\MoreThenCms.Web)
├── stostayer.old\ (was C:\stayer\stostayer.old)
└── snolla-identity-manager\ (was C:\stayer\Snolla.IdentityManager — deploy artifact, без живого consumer'а)
IIS sites/pools (имя=имя=пуло):
snolla *:80 → C:\sites\snolla (pool snolla, .NET v4.0 Integrated, AppPoolIdentity)
stostayer *:8090 → C:\sites\stostayer (pool stostayer)
stostayer.old *:8091 → C:\sites\stostayer.old (pool stostayer.old)
Connection strings:
snolla: Data Source=localhost → MSSQL container на хосте (DB MoreThenCms)
stostayer: Data Source=www.stostayer.ru,1433 (user stayer_site) → внешний SQL Server 2022
stostayer.old: Data Source=localhost → MSSQL container на хосте (DB stostayer, user snolla)
Traefik active routes: 10 главных доменов (snolla.com и др.) + rimiz.ru → host:80.
Stayer routes (stostayer.snolla.com, old.stostayer.ru) DISABLED — приватны.
ртвые: disk.yml, dsm.yml (192.168.1.10).
VM snolla-recovery: VMState=saved (frozen, ~92 GB на диске + ~3 GB savestate RAM dump).
```
## Открытые вопросы (что унесли в следующую сессию)
- **`rimiz.ru` отдаёт 404** на host-IIS. CMS-routing не знает этот host — нужно копнуть БД (таблица CMS-сайтов, mapping host header → site).
- **Snolla.IdentityManager** на хосте: папка перенесена, но IIS-сайт не создавался (по решению). Если CMS-код где-то дёргает `localhost:8089` или подобное — будет тихий fail. Симптомов пока нет.
- **Phase 5 (из старого плана)**: URL Rewrite + ARR для фикса `:4443` в редиректах, Application Initialization (warm-start), log rotation `C:\inetpub\logs\`. Не блокеры.
- **VM-cleanup**: если через ~неделю стабильной работы host'а проблем не будет — `VBoxManage unregistervm "snolla-recovery" --delete` освободит ~92 GB. NAT port forwards в OpenWRT (host:18080/18180/18181/18189) можно удалить.
## Связано
[[recovery-architecture-snapshot]] обновлён под полностью-host chain. [[snolla-recovery-vm]] — VMState=saved, все sites unused. [[windows-recovery-host]] — финальный layout `C:\sites\`. [[webconfig-password-xml-escape]] — новый concept про XML entity escape в conn-string'ах. [[cms-config-rewrite-pattern]] — UTF-8 BOM подтверждён ещё много раз.
---
## Phase 9 — RЕVERT (вечер 2026-05-19, после провала)
**Что случилось:** через ~10 минут после моего commit'a "done" пользователь открыл `https://www.pilorama98.ru/``502 Bad Gateway`. Дальше остальные домены показали `TOO_MANY_REDIRECTS`.
**Root cause** (расписан полностью в [[iis-migration-2026-05-19-postmortem]]):
- Phase 3 я patched traefik backend `host.docker.internal:18080``host.docker.internal:80`.
- Внутри traefik-контейнера `host.docker.internal:80` через Docker Desktop NAT резолвится **обратно в сам traefik** на его HTTP entrypoint :80 (potential Docker publish-port loopback gotcha).
- traefik `https.yml` имеет http-catchall middleware `redirect-to-https` → traefik отвечает 301 на свой же запрос → loop.
**Реактивная цепочка ошибок** (~1 час):
- Restart-WebAppPool, nuke workers, docker restart traefik → only sделали хуже (503).
- Patched `host.docker.internal:80 → 192.168.1.143:80` → тот же loop (LAN-IP через NAT тоже возвращается в traefik).
- Patched на `:8088` + добавил IIS binding → нужен `Stop+Start Website` чтобы binding applied, изначально забыл → IIS не listen → 503.
- Думал что 301 от CMS (Pitfall 5 X-Forwarded-Proto), копал CMS код — реальный источник был самим traefik (headers без `Server: Microsoft-IIS` подсказывали).
**Revert (выполнен):**
- `VBoxManage startvm "snolla-recovery"` — VM поднята из savestate (~10s + 90s warmup + recipe `ipconfig /release /renew` для застрявшего network adapter из [[vbox-windows-stability-tuning]]).
- 11 главных yml восстановлены из `*.bak-phase3-2026-05-19` (`host.docker.internal:18080` → VM).
- Stayer yml: `stostayer.yml.disabled / oldstostayer.yml.disabled` удалены, восстановлены из `*.bak-stayer-switch-2026-05-19` (`:18180/18181` → VM).
- `docker restart traefik` (file-provider не подхватил reload автоматом).
- Public smoke через VM-chain → 7/11 хостов отвечают (2 c 200, 5 с CMS-side редиректами на canonical — нормально для CMS-логики); проверено пользователем в браузере → работает.
## Финальное состояние (после revert)
Production снова на VM-chain (как было в начале сессии 2026-05-19). На хосте осталось:
- `C:\sites\{snolla, stostayer, stostayer.old, snolla-identity-manager}` — ~11 GB, **inert** (не на prod-пути)
- IIS sites/pools `snolla, stostayer, stostayer.old` — не получают трафика (traefik backend назад на VM)
- traefik backups: `*.bak-phase3-2026-05-19`, `*.bak-stayer-switch-2026-05-19`, `*.bak-hostip-2026-05-19`
- VM `snolla-recovery` — running, IIS активен, обслуживает 11 главных доменов + 2 stayer публично через port forwards.
**Артефакты для следующей попытки** (НЕ удалять):
- Web.config'и в `C:\sites\` пропатчены правильно (sitePath, conn-strings, stostayer на новый DB) — можно переиспользовать.
- IIS sites/pools уже настроены — переключение требует только traefik backend patch + tested correctly.
- Recipe для правильной миграции — в [[iis-migration-2026-05-19-postmortem]].
---
## Phase 10 — Attempt 2 (вечер 2026-05-19, после прочтения post-mortem)
Повторная попытка миграции, выполненная **по recipe из [[iis-migration-2026-05-19-postmortem]]**. Все 5 главных правил соблюдены, миграция прошла без incidents.
### Что сделано
1. **Read-only sanity (без prod-touches):** VM running confirmed, port-scan на хосте показал `:18090` и `:8089` свободны, traefik dir contains 4 серии bak-stamp'ов сохранённых от attempt 1, IIS sites/pools (`snolla, stostayer, stostayer.old`) живы.
2. **Backend port: `:8089`** (recipe-A — НЕ `:80`, НЕ совпадает с traefik publish `:8000/:4443/:8080`, НЕ совпадает с VM NAT forwards `:18080/:18180/:18181`). Выбор user'а (был кандидат `:18090`, user предпочёл `:8089`).
3. **Atomic revert plan ДО старта** (recipe-E): новая bak-серия `.bak-pre-attempt2-2026-05-19` для всех 13 yml (11 cms + 2 stayer), paste-ready команда восстановления записана в `.tasks/STATUS.md`.
4. **IIS binding** `snolla *:8089` добавлен через elevated PS (`New-WebBinding` + `Stop-Website; Start-Website` — recipe-A note: без restart binding не активируется).
5. **Loop-detect через `docker exec`** (recipe-A + recipe-F): `docker exec traefik wget --spider -S --header="Host: emspb.ru" http://host.docker.internal:8089/` → first response `301 → http://www.emspb.ru/` от `Server: Microsoft-IIS/10.0`. `host.docker.internal` resolves to `192.168.65.254:8089` — это Docker Desktop host gateway, **не traefik publish-port**. NO NAT loop. См. [[docker-host-loopback-detect]] про общую технику.
6. **Smoke test с `-MaximumRedirection 0`** (recipe-B): `curl.exe -k -I --max-redirs 0 -H "Host: emspb.ru" https://localhost:4443/` → first response `301`, `Server: Microsoft-IIS/10.0`, `Location: http://www.emspb.ru/`. Это **expected CMS canonical redirect** (та же логика что была на VM).
7. **Canary atomic** — patched ОДИН yml (`emspb.yml`), file-provider auto-reload, public smoke clean → 📱 **phone-test с мобильного интернета** (recipe-C) → `https://emspb.ru/` открылось → ✅.
8. **Second canary**`labtools.yml`, smoke + phone-test → ✅.
9. **Batch patch** оставшихся 9 cms yml (snolla, rimiz, labtoolspro, pilorama98, tandemmebel, kupimknigi, maljarka, sestech, isc-artmaterials) одним PS-блоком (recipe-G: batch не item-by-item) → smoke 20 hostnames → все `Server: Microsoft-IIS/10.0` ✅.
10. **Stayer routes — disabled** (re-confirmed Phase 7 decision): user подтвердил «stayer'ы локальные, через traefik наружу не светят». Rename `stostayer.yml → stostayer.yml.disabled`, `oldstostayer.yml → oldstostayer.yml.disabled`. `docker restart traefik` (file-provider не подхватил deletion auto-reload). Verify: `stostayer.snolla.com`/`old.stostayer.ru``404 text/plain` (traefik no-route). Host IIS sites `:8090`/`:8091` остаются live для прямого/локального доступа.
11. **VM running parallel** (recipe-D): VM **НЕ savestate'ить** минимум 24h+. Освободит RAM/disk только после подтверждённой стабильности host'а.
### Pitfall found and resolved
- **WinHTTP proxy strips Server header.** Локальный `curl.exe http://localhost:8090/` returned response без `Server: Microsoft-IIS/10.0` и без `X-Powered-By: ASP.NET` (плюс с `Proxy-Connection: keep-alive`) — выглядело как «не IIS отвечает». Это привело к moment'у panic. Но `docker exec traefik wget ...` (real production path) **показал headers корректно**. Вывод: для loop-detect / IIS-confirmation тестов **использовать `docker exec` из traefik container**, не Windows `curl.exe` — последний ходит через Windows-уровневый proxy который headers вырезает.
### My mistake during this session
- Я неверно интерпретировал «мигрировать stayer'ов» как «patch traefik backend → host:8090/8091» (т.е. пускать через traefik наружу). User имел в виду «host IIS уже на :8090/:8091, traefik routes должны быть **DISABLED**». Сделал prod-changing patch → user интервент-stop → revert + rename `.yml.disabled`. См. memory `feedback-migrate-semantics`. Lesson — переспрашивать semantics для internal/low-traffic сервисов перед prod-changing.
### Финальный chain (после attempt 2)
```
Клиент (browser)
→ DNS → 94.19.247.14 (public IP)
→ OpenWRT NAT 443 → 192.168.1.143:4443
→ traefik:4443 (TLS termination)
→ match Host → одно из 11 cms-yml → backend
→ http://host.docker.internal:8089/
↳ Docker Desktop host gateway 192.168.65.254:8089
→ Windows host IIS site `snolla` (*:80 + *:8089 bindings)
→ C:\sites\snolla\, .NET Framework 4.8.1, ApplicationPoolIdentity
→ conn → MSSQL container на host:1433
← HTTP response
```
Stayer chain: traefik routes disabled. `stostayer.snolla.com` / `old.stostayer.ru` → traefik 404. Host IIS sites `:8090`/`:8091` живут для локального доступа.
VM `snolla-recovery`: **running parallel** (24h+ soak), backend в traefik больше не используется — VM NAT port forwards (`:18080/:18180/:18181`) холостые.
### Артефакты этой сессии
- `.tasks/STATUS.md` — статус task'а 🔴 active.
- traefik backups в `data/custom/`: `*.yml.bak-pre-attempt2-2026-05-19` для 13 yml, плюс `stostayer.yml.disabled`, `oldstostayer.yml.disabled`.
- `feedback-migrate-semantics` memory — урок для будущих сессий.
- `concepts/docker-host-loopback-detect.md` — новый concept для loop-detect technique.
## Что осталось open
- **24h+ soak** до savestate VM (минимум до утра 2026-05-20, лучше 48h до 2026-05-21).
- **VM cleanup**: после soak — `savestate` (освободит RAM), позже `unregistervm --delete` (освободит ~92 GB).
- **Port forwards OpenWRT** (`host:18080/18180/18181/18189` → VM) — больше не нужны, удалить позже.
- **rimiz.ru / ics-artmaterials.com 404** — CMS-side routing issues, не инфра. Открытым.
- **X-Forwarded headers** (`:4443` в redirect URL) — известный bug [[traefik-on-windows-docker-desktop]] Pitfall 5, отдельная задача.
## Phase 11 — close-out 2026-05-21 (~36h soak)
Soak window 2026-05-19 22:00 MSK → 2026-05-21 ~08:00 MSK ≈ 36 hours. Verify:
- **Traefik:** `Up 38 hours` (`docker ps`) — zero restarts, zero unscheduled bouncing.
- **w3wp.exe** (IIS worker для `snolla` site): PID 10432, CreationDate 2026-05-20 23:19:21 MSK, uptime 8.8h. Это **scheduled IIS app pool recycle** (default 1740 min ≈ 29h), не crash — нормальное поведение pool'а после ~29h работы. Память 522 MB.
- **HTTPS smoke** 11 заявленных hosts через `docker exec traefik wget --spider --server-response https://<host>/`:
| Host | Code | Server | Verdict |
|---|---|---|---|
| `emspb.ru` | 301→www | Microsoft-IIS/10.0 | ✅ |
| `snolla.com` | 301→on.snolla | Microsoft-IIS/10.0 | ✅ |
| `on.snolla.com` | 200 | Microsoft-IIS/10.0 | ✅ |
| `pilorama98.ru` | 301→www | Microsoft-IIS/10.0 | ✅ |
| `labtools.ru` | 301→www | Microsoft-IIS/10.0 | ✅ |
| `labtools.pro` | 301→www | Microsoft-IIS/10.0 | ✅ |
| `tandemmebel.ru` | 301→www | Microsoft-IIS/10.0 | ✅ |
| `kupimknigi.spb.ru` | 200 | Microsoft-IIS/10.0 | ✅ |
| `maljarka.ru` | — | DNS bad address | ⚠️ off-infra (nslookup 8.8.8.8 → no A record) |
| `sestech.ru` | — | TLS cert `CN=*.domainparking.ru` (expired 2026-05-13) | ⚠️ off-infra (domain parking provider) |
| `ics-artmaterials.com` | 200 | `nginx-reuseport/1.21.1` | ⚠️ off-infra (A→87.236.16.28, мигрирован на сторонний WP-хостинг) |
**Итог:** 8/8 наших sites зеленые. 3 hostname'а из исходного списка task'а оказались **уже не на нашей инфраструктуре** — DNS либо снят, либо переключен на сторонние хостинги. Это не regression миграции; эти domains надо просто убрать из traefik/IIS routing'а как dead.
Decisions:
- iis-host migration close-out: **DONE 2026-05-21**.
- VM `snolla-recovery`: готова к savestate (soak passed, zero rollbacks). User-side savestate решает отдельно.
- 3 off-infra domains: новая задача — `iis-traefik-dead-routes-cleanup` (low prio).
## Артефакты Phase 11
- `.tasks/STATUS.md` — task → 🟢 done.
- (existing) traefik bak-серия `.bak-pre-attempt2-2026-05-19` — оставить ещё неделю (atomic revert на случай unforeseen regression).