# vds-kzntsv-bootstrap ## Goal Поднять облачный VDS `vds.kzntsv.site` (Rusonyx) и вынести туда ключевые инфраструктурные сервисы пользователя, чтобы не зависеть от собственного железа (мёртвая NAS показала single-point-of-failure). Цель — устранить риск повторения 2026-05-18 для **инфраструктурного** слоя (gitea/verdaccio/seafile/registry/hermes); production CMS (MoreThenCms) остаётся на [[windows-recovery-host]] и обсуждается отдельно в [[future-resilient-architecture-goals]]. После миграции [[kreknin-synology]] остаётся как backup target, не как live host инфры. ## Сервисы под миграцию (snapshot 2026-05-19) Источник размеров — `du -sh /volume1/docker/*/` на kreknin (см. Decisions log 2026-05-19). | Сервис | Сейчас на kreknin | После cleanup / на VDS | План | |---|---|---|---| | gitea | `/volume1/docker/gitea/` 2.6G | ~2.6G | lift-and-shift (compose + data tar) | | verdaccio | `/volume1/docker/personal/verdaccio/` 8.5G | 2–3G | prune old tarballs до миграции | | owncloud | `/volume1/docker/owncloud/` 187M + дубль 195M | — | **отказ**, заменяется seafile | | seafile | (новый) | 30–40G | green install на VDS | | registry | `/volume1/docker/infrastucture/registry/` 99G | 5–15G | **`registry garbage-collect` ДО миграции** (write op, требует read-only/stopped registry) | | hermes | `/volume1/docker/hermes/` 0 (пусто на kreknin) | ~10G (user estimate) | TBD — что это, где state | Раскладка после cleanup: ~55–75G data + ~5G OS/docker → ~80–100G с headroom. ## Тариф VDS (Rusonyx, https://www.rusonyx.ru/hosting/vps/#ssd) **Заказан (финал, user 2026-05-19): `160 NVMe`** (Rusonyx переименовал прежний `160 SSD` тариф в NVMe — апгрейд storage по той же цене, IOPS выше). Конфигурация заказа: - 6 vCPU 2.6GHz - 8192 MiB RAM (8 GiB) - 163840 MiB disk (160 GiB) NVMe - Ubuntu Server 24.04 - 1 IPv4 (free) - Лицензия / CMS / ispmanager — нет (docker-only, control-panel мусор не нужен) - VPS Backup от Rusonyx — 0 шт (свой backup через rsync/borg → kreknin или off-site cloud) - SSH root access — Вкл на старте (после bootstrap — отключить, sudo-user) Reasoning: - 160GB headroom ~45% на старте после миграции (~60-70G data + 5G OS) — годен на 2-3 года без add-on operations. - 8GB RAM overkill для baseline (~2.1G) но даёт реальный запас под burst и любые будущие сервисы. - User выбрал «купи-и-забудь» вариант over 80 SSD+add-on; +1000 ₽/мес vs 80 SSD = +12k₽/год за operational simplicity. **Не выбраны:** - 40 SSD — 4GB RAM впритык, OOM risk под seafile+registry burst. - 80 SSD — RAM ок, но 80GB диск требует add-on (цены непрозрачны без звонка в managers). - 220+ SSD — overkill без local LLM-inference. ## Open questions - [ ] **Hermes** — Nous Research agent runtime (https://hermes-agent.nousresearch.com/). Открытые подвопросы: - self-host их open-source / docker image, или client-wrapper к managed API? - если self-host — какой репо/образ, какие порты, какие external deps (vector DB, LLM endpoint)? - state где живёт (postgres / sqlite / files / vector store)? - 10G — это weights/embeddings/vector store/document cache? - влияет на RAM-выбор тарифа (если local LLM inference — 8GB мало, нужно 16+) - [x] ~~Owncloud дубль~~ — **live = `/volume1/docker/owncloud/`** (vitya:users, mysql активен 2026-05-20), stale = `/volume1/docker/personal/owncloud/` (alexey, mysql last May 5). Owncloud в scope этих 3 фаз не входит — user явно убрал в Phase 3. - [x] ~~DNS-cut стратегия~~ — DNS A-records проставлены user'ом 2026-05-20 сразу на VDS IP до bootstrap'а (`vds`, `*.vds`, `git`, `registry`, `verdaccio`). Сервисы на kreknin продолжают отвечать пока traefik там жив; cut фактический произойдёт когда remote DNS resolver кэш протухнет (≤ TTL). - [ ] Backup VDS → kreknin: какой механизм? rsnapshot / restic / borg / Hyper Backup pull через SFTP? (deferred — после Phase 3) - [x] ~~OS на VDS~~ → **Ubuntu 24.04 LTS** (decision 2026-05-19): LTS до апреля 2029, docker official APT repo flow, mainstream community для docker-стека, гарантированно есть в Rusonyx templates. ## Key files (пока нет; появятся по ходу — compose-файлы, ansible-плейбук если будет, sync scripts) ## 🚨 Pre-Phase-1 — Rusonyx warning 2026-05-20 **`apt upgrade` Ubuntu 24.04 на Rusonyx виртуализации перезапускает SSH service. Делать ТОЛЬКО через VNC console Rusonyx-панели**, иначе SSH рвётся посередине, апгрейд бьётся. Sequence (paste-ready в VNC console, root login): ```bash apt update apt upgrade -y apt --fix-broken install -y apt upgrade -y ``` После reboot SSH повторно достижим (89.253.255.94, root, пароль из `vds-kzntsv.env`). Отсюда мой Phase 1 начинается. ## Connectivity (2026-05-20) - IP: `89.253.255.94` - Vendor hostname: `vps-21075162-534388.host4g.ru` - DNS (REGRU, user 2026-05-20): - A `vds.kzntsv.site` → 89.253.255.94 - A `*.vds.kzntsv.site` → 89.253.255.94 - A `git.kzntsv.site` → 89.253.255.94 - A `registry.kzntsv.site` → 89.253.255.94 - A `verdaccio.kzntsv.site` → 89.253.255.94 - Креды (root initial + sudo user vitya + portainer admin + LE email): `C:\Users\vitya\projects\.common\secrets\vds-kzntsv.env`. Root pass одноразовый, rotate'нется в Phase 1 (PasswordAuthentication off, SSH-key-only). ## Kreknin pre-migration findings (probe 2026-05-20) SSH `vitya@195.19.90.188` через `id_ed25519_kreknin` ✅. Sudo требует пароль (пока не у меня). | Сервис | Путь | Размер | DB | Compose live? | |---|---|---|---|---| | gitea | `/volume1/docker/gitea/` | data 2.6G + own postgres 9.6 dir | внутренний Postgres 9.6 (`gitea:gitea`) | ✅ `gitea` + `gitea-db` на сети `proxy` | | verdaccio | `/volume1/docker/personal/verdaccio/` | storage 8.5G + config 8K | нет | ✅ | | registry | `/volume1/docker/infrastucture/registry/` | **99G** | нет (auth + docker filesystem) | ✅ (compose-файл есть) | | owncloud-live | `/volume1/docker/owncloud/` | mysql активен 2026-05-20 | mysql + redis в одном compose | ✅ live (vitya:users owner) | | owncloud-stale | `/volume1/docker/personal/owncloud/` | mysql последний May 5 | mysql + redis | устарел | | hermes | `/volume1/docker/hermes/` | только `/data/` (uid 10000), нет compose | ? | ❓ конфиг где-то ещё (DSM Container Manager?) — open question | ## Phase 1 — Bootstrap (план, после VNC-upgrade) 🖥️ paste-ready, root@89.253.255.94 через SSH (после VNC-upgrade reboot) 1. `adduser --gecos "" --disabled-password vitya` + `echo 'vitya:Pryakhin10~' | chpasswd` + `usermod -aG sudo vitya`. 2. `mkdir -p /home/vitya/.ssh && echo '' > /home/vitya/.ssh/authorized_keys && chmod 700 /home/vitya/.ssh && chmod 600 /home/vitya/.ssh/authorized_keys && chown -R vitya:vitya /home/vitya/.ssh`. Pubkey = `~/.ssh/id_ed25519.pub` с Windows-PC. 3. Тест login `ssh vitya@89.253.255.94` отдельным окном **до** harden sshd (lockout-safety). 4. Harden sshd: `sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/; s/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config && systemctl reload sshd`. 5. `apt install -y ufw fail2ban` + `ufw default deny incoming && ufw default allow outgoing && ufw allow 22/tcp && ufw allow 80/tcp && ufw allow 443/tcp && ufw enable`. fail2ban sshd jail дефолт-on. 6. Docker official APT: ```bash install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc chmod a+r /etc/apt/keyrings/docker.asc echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu noble stable" > /etc/apt/sources.list.d/docker.list apt update apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin usermod -aG docker vitya ``` 7. `mkdir -p /opt/stacks/{traefik,portainer,databases}`. 8. Traefik compose (`traefik:v3.0`), HTTP-01 challenge (DNS уже на VDS), per-subdomain certs. Dashboard на `traefik.vds.kzntsv.site` с basicAuth `vitya:Pryakhin9` (htpasswd-hashed). 9. Portainer compose (`portainer/portainer-ce:latest`), labels → `portainer.vds.kzntsv.site`. First-visit setup wizard: admin `vitya` / `vitya.kuznetsov@gmail.com` / `Pryakhin9`. Generate API key → дописать в `vds-kzntsv.env` `PORTAINER_API_KEY=...`. С этого момента я могу деплоить stacks через Portainer REST API. **Traefik v2.11 vs v3.0 decision:** v3 mainline, breaking changes в middleware label syntax. v2.11 LTS до Q1 2027, совместим с существующими compose-стеками на windows-recovery-host. Recommend **v3.0** — VDS green install, нет legacy compose-файлов к pin'ить; за год v3 уже зрелый. ## Phase 2 — Shared DBs (план) DB-park (Postgres 16, MariaDB 11, Redis 7, MongoDB? — см. Open questions). Каждый — отдельный compose в `/opt/stacks/databases//`, на общей docker network `shared-dbs` (external). Admin GUI через Portainer (или отдельные образы adminer/pgadmin/redis-commander если нужно). Exposure model — см. Open questions ниже. ## Phase 3 — Migration с kreknin ### 🚨 Источник данных — restored backup, НЕ live containers Данные `/volume1/docker/{gitea,personal/verdaccio,infrastucture/registry}/` на [[kreknin-synology]] это **restored Hyper Backup** мёртвой [[dead-synology-diskstation]] (restore сессии 2026-05-18 вытащил `/docker` шару из `.hbk` репо). На kreknin сами эти сервисы **не запущены** — files сидят на disk. Поэтому миграция = чистый pull данных + standup на VDS, **без docker stop / pg_dump на kreknin**. Container'ы умерли вместе с DiskStation 2026-05-18. Read first: `.wiki/concepts/hyper-backup-structure-and-recovery.md`, `.wiki/sources/nas-recovery-session-2026-05-18.md`, `.wiki/entities/kreknin-synology.md`, `.wiki/entities/dead-synology-diskstation.md`. ### 3.1 Gitea 1. На kreknin (через sudo, перм issue на postgres dir): `tar c -C /volume1/docker/gitea . | ssh vds 'tar x -C /opt/migrate/gitea/'` — pulls `data/` + `postgres/` + `docker-compose.yml`. Hyper Backup restored файлы могут иметь Synology ACL + funky POSIX perms (см. [[hyper-backup-structure-and-recovery]] раздел ACL); rsync через root tar выровняет. 2. На VDS: `chown -R 999:999 /opt/migrate/gitea/postgres && chmod 700 /opt/migrate/gitea/postgres` (postgres uid 999, требует strict 700 на datadir). 3. На VDS: запустить **temporary** Postgres 9.6 на restored datadir → `docker run --rm -d --name pg96-temp -v /opt/migrate/gitea/postgres:/var/lib/postgresql/data postgres:9.6` (env `POSTGRES_USER=gitea POSTGRES_PASSWORD=gitea POSTGRES_DB=gitea`). Подождать `pg_isready`. 4. На VDS: `docker exec pg96-temp pg_dump -U gitea gitea > /opt/migrate/gitea.sql` → ~80-200 MB SQL. 5. На VDS shared postgres-16: `psql -U postgres -c 'CREATE DATABASE gitea OWNER gitea;'` (после создания юзера gitea в shared cluster) → `psql -U gitea -d gitea < /opt/migrate/gitea.sql`. Гитеа автоматически мигрирует schema под 16 (Gitea умеет). 6. Stop temp pg96, удалить `/opt/migrate/gitea/postgres/` (больше не нужно — данные в shared postgres). 7. На VDS Portainer stack: ```yaml services: gitea: image: gitea/gitea:1.25.5 environment: - USER_UID=1000 - USER_GID=1000 - DB_TYPE=postgres - DB_HOST=postgres:5432 # shared - DB_NAME=gitea - DB_USER=gitea - DB_PASSWD=gitea volumes: - /opt/stacks/gitea/data:/data networks: [proxy, shared-dbs] labels: - traefik.enable=true - traefik.http.routers.gitea.rule=Host(`git.kzntsv.site`) - traefik.http.routers.gitea.entrypoints=websecure - traefik.http.routers.gitea.tls.certresolver=letsEncrypt - traefik.http.services.gitea.loadbalancer.server.port=3000 ``` `data/` смонтировать с `/opt/migrate/gitea/data/` (или mv туда). 8. Smoke: `git.kzntsv.site` → existing user login (data restored) → repo browse → clone test. 9. Decommission на kreknin: ничего не делаем, файлы restored backup и так сидят паркингом на kreknin volume. Не трогать. ### 3.2 Verdaccio 1. На kreknin → VDS: rsync (через `scp -O` или `tar | ssh`) `/volume1/docker/personal/verdaccio/{storage,config}/` → vds:/opt/stacks/verdaccio/. 2. На VDS: chown под uid контейнера verdaccio (10001, см. verdaccio Dockerfile). 3. **Pre-clean ПОСЛЕ rsync, на VDS** (не на kreknin — кreknin это backup-target read-only-bydefault): удалить старые tarball'ы > N версий из `storage///` либо просто оставить как есть (8.5G нормально для 160 GB VDS). 4. На VDS Portainer stack: image `verdaccio/verdaccio:6`, volumes `/opt/stacks/verdaccio/storage:/verdaccio/storage` и `config:/verdaccio/conf`, labels на `verdaccio.kzntsv.site`. 5. Smoke: `npm publish` тест с windows-recovery-host против `verdaccio.kzntsv.site`. ### 3.3 Registry 1. На kreknin: pre-clean GC доступен **без** running registry container — запустить temp registry:2 локально на kreknin против restored datadir: `sudo docker run --rm -v /volume1/docker/infrastucture/registry/docker:/var/lib/registry -v /volume1/docker/infrastucture/registry/config.yml:/etc/docker/registry/config.yml registry:2 garbage-collect /etc/docker/registry/config.yml`. **Write op on kreknin** — требует согласия user'а (правка backup'а на kreknin диске). Альтернатива: skip GC на kreknin → rsync 99G сетью → GC на VDS пост-фактум (медленнее по сети, безопаснее для kreknin). 2. На kreknin → VDS: rsync `/volume1/docker/infrastucture/registry/{auth,docker,docker-compose.yml,config.yml}` → vds:/opt/stacks/registry/. 3. На VDS Portainer stack: image `registry:2`, volumes для `auth` + `docker` (data) + `config.yml`, labels на `registry.kzntsv.site`. basicAuth middleware если в config задан htpasswd. 4. Smoke: `docker login registry.kzntsv.site` + `docker pull` известный image. ### Open question по 3.3 GC - GC на kreknin (write-op на backup-target, ~30 мин обработка, экономит ~85G сетевого трафика и ~85G диска на VDS) - GC на VDS пост-rsync (читает 99G по сети — медленно при типичном Rusonyx ~50-100 Mbps, ~3-5 ч; не трогает kreknin) Recommend **GC на kreknin** — backup-target, но `garbage-collect` registry:2 пересоберёт data IN-PLACE, не deletes; risk минимальный. Связанные wiki-страницы (для контекста): - `.wiki/entities/kreknin-synology.md` — текущий host kreknin - `.wiki/concepts/future-resilient-architecture-goals.md` — глобальные цели resilience - `.wiki/concepts/recovery-architecture-snapshot.md` — текущая prod-инфра ## Decisions log - **2026-05-19:** OS на VDS = **Ubuntu 24.04 LTS** (Noble). Reasoning: LTS support до апреля 2029, docker official APT flow, mainstream community для docker-workload. Debian 12 отвергнут — не даёт material выгоды при 8GB RAM (idle разница ~100M), а docker-docs primary-target — Ubuntu LTS. - **2026-05-19 (заказан):** Тариф `160 NVMe` (Rusonyx переименовал 160 SSD → 160 NVMe, та же цена, апгрейд по IOPS). VPS Backup от Rusonyx — 0 шт (свой backup pipeline). SSH root — Вкл на bootstrap, потом отключить. - **2026-05-19 (final tier):** User выбрал **160 SSD (2500 ₽/мес)**. Reasoning user'а — operational simplicity, не звонить в support за add-on ценой, есть запас на 2-3 года. +1000 ₽/мес vs 80 SSD приемлемо. - **2026-05-19 (промежуточный заход, откатан):** Рекомендовалось 80 SSD + disk add-on исходя из того что 8GB RAM избыточно после уточнения hermes-профиля. User решил что 500 ₽/мес экономии не стоят звонков в support и риска что add-on окажется дорогой. - **2026-05-19 (первый заход):** Изначально 160 SSD по budget-расчёту headroom; затем переоценено вниз после уточнения hermes; затем user вернул обратно к 160 SSD по user-preference. - **2026-05-19:** Owncloud → seafile. Reasoning: пользователь решил заменить (rationale пока не записан). - **2026-05-19:** Registry **garbage-collect ДО миграции** (на kreknin), чтобы не тащить 99G чтобы потом всё равно чистить. - **2026-05-19:** Verdaccio тоже prune ДО миграции, по аналогичной логике. ## Next actions (по порядку) 1. **Уточнить hermes** — что это, где его state. 2. **Решить owncloud дубль** — нужны ли данные для миграции в seafile или green install. 3. **Registry GC на kreknin** — stop писателей, `docker run --rm -v ...:/var/lib/registry registry:2 garbage-collect /etc/docker/registry/config.yml`, measure delta. ⚠️ prod-changing, согласие от user. 4. **Verdaccio prune** — `npm cache clean` + удалить old tarballs из storage. (low risk, restorable из upstream npm) 5. ~~**Закупить тариф у Rusonyx**~~ → **заказан 160 NVMe, Ubuntu 24.04, 8GB/6vCPU/160GB, 1 IPv4** (2026-05-19, ожидание выделения IP). 6. **DNS** — A `vds.kzntsv.site → ` в REGRU. 7. **Bootstrap VDS** (zero-day чек-лист): apt update/upgrade → создать sudo-user `vitya` + копировать SSH key → `PermitRootLogin no` + `PasswordAuthentication no` → ufw (22/80/443 only) → fail2ban → docker official APT repo (docker-ce + buildx + compose-plugin). Полная paste-ready команда в чате сессии. 8. **Per-service migration** (gitea → tarball+restore, verdaccio → tar storage, seafile → green install, registry → rsync, hermes → repo+state). 9. **Backup pipeline** VDS → kreknin (или cloud — Backblaze B2 для off-site, см. [[future-resilient-architecture-goals]]). 10. **DNS cut** к production, удаление сервисов с kreknin (с paranoid keep-on-kreknin-7days).