Files
admin/vds-kzntsv-bootstrap.md
vitya f9bdb97be3 docs(.wiki,.tasks): vds-kzntsv-bootstrap done — 3 phases shipped
VDS Rusonyx 160 NVMe (89.253.255.94 / vds.kzntsv.site) активирован
2026-05-20. 3 фазы за ~6 часов:

- Phase 1: harden + docker 29.5.1 + traefik v2.11 + portainer 2.21.5
- Phase 2: shared DB park (postgres 16 / mariadb 11.4 / mongo 7 /
  redis 7) via traefik raw TCP forward + self-signed TLS
- Phase 3: миграция gitea (132 repos), verdaccio (2063 pkgs), registry
  fresh install (user accepted loss old images) + Joxit GUI

Ingest 1 source + 1 entity (vds-kzntsv) + 6 concepts (traefik-tcp-
passthrough-vs-starttls / portainer-2.21-admin-password-regression /
db-tls-self-signed-via-traefik-raw-tcp / rusonyx-vps-onboarding-quirks
/ registry-gc-mount-and-modify-flag / compose-bcrypt-escape-trap).

3 follow-up  tasks: vds-gc-cron, vds-backup-rsync-kreknin, vds-ntfy-push.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 22:39:31 +03:00

20 KiB
Raw Blame History

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 23G prune old tarballs до миграции
owncloud /volume1/docker/owncloud/ 187M + дубль 195M отказ, заменяется seafile
seafile (новый) 3040G green install на VDS
registry /volume1/docker/infrastucture/registry/ 99G 515G registry garbage-collect ДО миграции (write op, требует read-only/stopped registry)
hermes /volume1/docker/hermes/ 0 (пусто на kreknin) ~10G (user estimate) TBD — что это, где state

Раскладка после cleanup: ~5575G data + ~5G OS/docker → ~80100G с 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+)
  • 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.
  • 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)
  • OS на VDSUbuntu 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):

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 '<pubkey>' > /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:
    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/<engine>/, на общей 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:
    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/<scope>/<package>/ либо просто оставить как есть (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 prunenpm 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 → <IP> в 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).