# 09. Инфраструктура ## 1. Сервер (проверено 18–20.10.2026) | Что | Значение | |---|---| | ОС | Ubuntu 26.04.1 LTS | | Ресурсы | 4 vCPU, 7.7 ГБ RAM, 8 ГБ swap, 145 ГБ диска (занято 24 ГБ) | | IP | 169.58.76.183 — сайты (PTR `fr-ub2gs3.veilix.cc`, прямая запись совпадает); 173.212.216.183 — основной, через него по умолчанию идёт исходящий трафик (PTR `vmi3593751.contaboserver.net`) | | Установлено | Node 24.22, PostgreSQL 18.6, nginx 1.28 (модули geoip2 и brotli подключены), Redis 8, Postfix 3.10, OpenDKIM, certbot 4.0, Docker 29 (не используем), git | | Нет | pnpm, база GeoLite2 (`.mmdb`), сертификаты TLS, geoipupdate | | DNS | все 15 хостов проекта (вместе с `umi.veilix.cc`) указывают на 169.58.76.183; DNS-хостинг — Gcore | ## 2. Папки ``` /home/vx_dev/veilix_dev/ — dev (пользователь vx_dev): рабочая копия git, .env, media/ /home/vx_prod/veilix/ — prod (пользователь vx_prod), закрыто для остальных releases/<дата-тег>/ — собранные релизы (храним 5 последних) current -> releases/… — симлинк на работающий релиз shared/.env — секреты prod shared/media/ — загруженные файлы prod /home/umami/umami/ — Umami (пользователь umami) ``` vx — личный пользователь администратора, к проекту отношения не имеет. Под vx_prod нет редактора и ИИ-агентов, VSCode Remote под vx_prod не подключаем ([D-004](decisions/D-004-environments.md)). Домашние папки всех пользователей — `750`, владелец — сам пользователь. Пустые папки `/home/vx/veilix.app`, `/home/vx/my.veilix.app`, `/home/vx/tg.veilix.app` проекту не нужны. ## 3. Процессы 1. Каждый сервис — под своим пользователем ОС, как systemd user-юнит. Root один раз включает `loginctl enable-linger` для vx_dev, vx_prod и umami, чтобы юниты работали без входа в систему. 2. `veilix-dev.service` (vx_dev): `pnpm dev` на 127.0.0.1:3100 из `/home/vx_dev/veilix_dev`, перезапуск при сбое. Файл юнита — `ops/systemd/veilix-dev.service` в репозитории, подключён через `systemctl --user link`. 3. `veilix.service` (vx_prod): `pnpm start` на порту 3000 из `/home/vx_prod/veilix/current`, `NODE_ENV=production`, перезапуск всегда. 4. `umami.service` (umami): Umami на порту 3300. 5. Фоновые задачи Payload выполняются внутри процессов приложения ([02-architecture.md](02-architecture.md), раздел 7). 6. Логи — journald, у каждого пользователя свои (`journalctl --user -u veilix` под vx_prod). Сессии Claude логи prod не видят: при разборе проблем prod их присылает пользователь. 7. Проверка живости — `/svc/health` (с проверкой базы). ## 4. nginx | Хосты | Куда | Доступ | |---|---|---| | veilix.app, veilixvpn.com, my.veilix.app, my.veilixvpn.com, tg.veilix.app, tg.veilixvpn.com | 127.0.0.1:3000 | открыт | | plc.veilix.cc | 127.0.0.1:3000 | только разрешённые IP | | dev.veilix.app, dev.my.veilix.app, dev.veilixvpn.com, dev.my.veilixvpn.com | 127.0.0.1:3100 | только разрешённые IP или пароль; `X-Robots-Tag: noindex` | | dev.tg.veilix.app, dev.tg.veilixvpn.com | 127.0.0.1:3100 | открыт для Telegram; приложение пускает только разрешённые Telegram-аккаунты | | dev.plc.veilix.cc | 127.0.0.1:3100 | только разрешённые IP | | umi.veilix.cc | 127.0.0.1:3300 | только разрешённые IP | 1. Слушает 169.58.76.183, порты 80 (редирект на HTTPS и проверка certbot) и 443. TLS 1.2 и 1.3, HTTP/2, HSTS. 2. Сертификаты — certbot, проверка через HTTP. 3. Сжатие — brotli и gzip. 4. GeoIP: блок `geoip2` в `/etc/nginx/conf/ip.conf`, база `/var/lib/GeoIP/GeoLite2-Country.mmdb` с `auto_reload`. Переменная `$geoip_country_code` уходит приложению заголовком `X-Geo-Country`; такой же заголовок от клиента nginx затирает. Старый модуль GeoIP v1 (`modules-enabled/50-mod-http-geoip.conf`) отключён: он занимает те же имена переменных, и `nginx -t` падает с ошибкой «the duplicate "geoip_country_code" variable». 5. Заголовки к приложению — общий файл `/etc/nginx/snippets/veilix_proxy.conf` (плюс `maps/upgrade.conf` для WebSocket), его подключает каждый `location` с `proxy_pass`: `Host`, `X-Real-IP` и `X-Forwarded-For` (оба — `$remote_addr`: nginx стоит первым, цепочку от клиента не принимаем), `X-Forwarded-Proto`, `X-Geo-Country`, заголовки WebSocket (нужны горячей перезагрузке dev). `proxy_set_header` не наследуется в `location` со своими `proxy_set_header`, поэтому файл подключается в каждый такой `location`. 6. В `nginx.conf` подключаются только `sites-enabled/*.conf`: файлы сайтов должны заканчиваться на `.conf`. Сайты: `sites-available/02_vx_veilix-prod.conf`, `02_vx_veilix-dev.conf`, `02_vx_plc-prod.conf`, `02_vx_plc-dev.conf`. Сертификаты Let's Encrypt (certbot, проверка через nginx): `veilix.app` (6 prod-хостов брендов), `dev.veilix.app` (6 dev-хостов), `plc.veilix.cc`, `dev.plc.veilix.cc`. a. Доступ по IP — файл `conf/ip_list_admin.conf` со строками `allow …;`, в `location` после него `deny all;`. Подключается на хостах админки и dev, кроме `dev.tg.*`. b. `dev.tg.*` — отдельный `server` без ограничения по IP, с `X-Robots-Tag: noindex`. c. На порту 80 — `location / { return 301 https://$host$request_uri; }`. Именно внутри `location`, а не на уровне `server`: так certbot при продлении может вставить свой `location` для проверки домена. 7. Лимиты запросов (`limit_req`) на эндпоинты входа и формы. 8. Для tg.* (этап 3) разрешить показ во фрейме Telegram Web (`frame-ancestors` для доменов telegram.org). 9. Пути трекера Umami на доменах брендов проксируются на 127.0.0.1:3300 ([08-integrations.md](08-integrations.md), раздел 8). 10. Конфигурацию готовит Claude текстом на согласование, применяет root. ## 5. PostgreSQL 1. Базы `veilix_dev`, `veilix_prod` и `umami`. Владелец каждой — роль с именем пользователя ОС: `vx_dev`, `vx_prod`, `umami`. 2. Вход по Unix-сокету с проверкой пользователя ОС: стандартное правило Ubuntu `local all all peer` в `pg_hba.conf` пускает пользователя ОС только под ролью с тем же именем. Паролей нет, `pg_hba.conf` и `pg_ident.conf` править не нужно. 3. У каждой базы снято право подключения для всех (`REVOKE CONNECT … FROM PUBLIC`): войти может только владелец. vx_dev в `veilix_prod` не попадёт. 4. Роль владеет своей базой: Payload нужны права на изменение схемы (push в dev, миграции в prod). 5. Строка подключения: `postgresql://vx_dev@/veilix_dev?host=/var/run/postgresql` (для prod — `vx_prod@/veilix_prod`). ## 6. Почта (Postfix) 1. `smtp_bind_address = 169.58.76.183`, `myhostname = fr-ub2gs3.veilix.cc`: письма уходят с IP, у которого обратная запись совпадает с прямой. 2. OpenDKIM: ключи для veilix.app и veilixvpn.com (селектор, напр. `mail`). 3. DNS для каждого из двух доменов: a. SPF: `v=spf1 ip4:169.58.76.183 -all` (плюс SMTP-провайдер, если используем); b. DKIM: TXT `mail._domainkey.<домен>` с открытым ключом; c. DMARC: `_dmarc.<домен>` — сначала `v=DMARC1; p=none; rua=mailto:…`, после проверки — `p=quarantine`. 4. Проверить, что хостер не закрывает исходящий порт 25 и что 169.58.76.183 нет в чёрных списках. 5. Приём почты поддержки (`help@…`) — где ящик, вопрос Q-16. ## 7. Бэкапы 1. Prod-база: каждую ночь `pg_dump -Fc veilix_prod`, хранить 14 ежедневных и 8 еженедельных, копия на другой сервер (куда — Q-14). Раз в месяц — проверочное восстановление в отдельную базу. 2. `shared/media` — синхронизация на тот же внешний сервер. 3. Dev не бэкапим: код — в git, данные dev — тестовые. ## 8. Задачи для root по этапам | № | Этап | Задача | Статус на 21.10.2026 | |---|---|---|---| | R-00 | 0 | Переезд разработки: перенести `/home/vx/veilix_dev` в `/home/vx_dev/veilix_dev` (владелец vx_dev); SSH-ключ vx_dev для VSCode; Claude Code под vx_dev | сделано | | R-01 | 0 | Репозиторий в Gitea; SSH-ключ vx_dev с правом push; для vx_prod — ключ только на чтение (deploy key) | в работе: репозиторий `ssh://git@gte.apr.sh:26789/andreypr207/veilix.app.git` подключён как `origin`; ключ vx_dev — `~/.ssh/veilix_gitea_ed25519`, ждёт добавления в Gitea как deploy key с правом записи; ключ vx_prod — на этапе 1h | | R-02 | 1a | PostgreSQL: роли `vx_dev`, `vx_prod`, `umami` и их базы (раздел 5) | сделано и проверено: vx_dev входит в `veilix_dev`, в `veilix_prod` — отказ | | R-03 | 1a | pnpm: `corepack enable pnpm` | сделано | | R-04 | 1a | Пользователи vx_dev, vx_prod, umami; `loginctl enable-linger` для них | сделано | | R-05 | 1a | nginx и сертификаты для всех хостов (раздел 4) | сделано: опечатка исправлена, `X-Robots-Tag` на всех dev-хостах | | R-06 | 1a | MaxMind: `geoipupdate` (ключ в `/etc/GeoIP.conf`, база в `/var/lib/GeoIP`, таймер `geoipupdate.timer`); отключить старый модуль GeoIP v1; блок `geoip2` (раздел 4, пункт 4) | сделано | | R-07 | 1c | Postfix, OpenDKIM, DNS-записи SPF, DKIM, DMARC — или данные SMTP-провайдера | | | R-08 | 1h | PostgreSQL для prod — если не сделано вместе с R-02 | сделано в R-02 | | R-09 | 1h | nginx и сертификаты для prod-хостов | сделано вместе с R-05 | | R-10 | 1h | Umami: nginx и сертификат для `umi.veilix.cc` (DNS уже есть; база — в R-02) | | | R-11 | 1h | Бэкапы prod-базы и media на внешний сервер (Q-14) | | | R-12 | 2 | VPS с Chatwoot и Twenty, ключи API; 2 бота поддержки в BotFather | | | R-13 | 3 | Боты в BotFather: 2 основных и тестовые; домен для Telegram Login; адреса MiniApp | | | R-14 | 4 | Аккаунты платёжных систем (сначала тестовые), адреса вебхуков | | | R-15 | 1h | По желанию (Q-18): sudo-правило, разрешающее vx_dev запускать от имени vx_prod только команду выкладки | | | R-16 | 1h | По желанию: правило nftables — к 127.0.0.1:3000 подключаются только `www-data` (nginx) и vx_prod ([10-security.md](10-security.md), раздел 7) | | | R-17 | 1a | nginx dev: отдельный `location /_next/webpack-hmr` с `proxy_read_timeout 1h` в server-блоках dev-хостов и `dev.plc.veilix.cc`. Иначе WebSocket горячей перезагрузки закрывается каждые 30 секунд (`proxy_read_timeout 30` в `nginx.conf`) | в работе: есть в `dev.plc` и `dev.tg.*`; нужен ещё в блоке `dev.veilix.app`, `dev.veilixvpn.com`, `dev.my.*` |