# Развёртывание > Пошаговая инструкция «с нуля до работающего сервера на домене» — [install-guide.md](install-guide.md). > Здесь — справочник: требования, каталоги, сервисы, переменные. ## Требования к серверу - Linux x86_64 (нужен mount propagation для storage-agent), Docker 24+, Docker Compose v2. - 4+ ядра, 8+ ГБ RAM (аудиоанализ при первом запуске ест CPU; TensorFlow-модели ~1–2 ГБ RAM). - Диск: ~5 ГБ под систему и БД + кэш транскодов (по умолчанию 50 ГБ) + обложки (~1 ГБ на 10k альбомов). - Сетевой доступ к NFS-серверам. ## Каталоги на хосте ``` /srv/pipu/ ├── library/ # точки монтирования источников (storage-agent, rshared) ├── library-host/ # опционально: заранее смонтированные папки для type=local ├── cache/ │ ├── transcode/ │ └── covers/ ├── postgres/ ├── meili/ ├── redis/ ├── models/ # модели Essentia (скачиваются при первом старте analyzer) ├── updates/ # кэш APK релизов ├── uploads/ # staging очереди на добавление (размер ≈ лимит очереди, по умолчанию 10 ГБ) ├── backups/ └── caddy/ # сертификаты Let's Encrypt (если используется compose.tls.yaml) ``` Перед первым запуском: `mkdir -p /srv/pipu/library && mount --bind /srv/pipu/library /srv/pipu/library && mount --make-rshared /srv/pipu/library` (делает `deploy/install.sh`, плюс systemd-unit для повторения после перезагрузки). ## `deploy/compose.yaml` (сокращённо) ```yaml x-library-ro: &library-ro type: bind source: /srv/pipu/library target: /library read_only: true bind: { propagation: rslave } services: nginx: image: ${REGISTRY}/pipu-music-web:${PIPU_VERSION} ports: ["443:443", "80:80"] volumes: - *library-ro - /srv/pipu/cache:/cache:ro - /srv/pipu/uploads:/uploads:ro # прослушивание очереди админом (internal location) - ./certs:/etc/nginx/certs:ro depends_on: [api] api: image: ${REGISTRY}/pipu-music-api:${PIPU_VERSION} env_file: .env volumes: - *library-ro - /srv/pipu/cache:/cache - /srv/pipu/updates:/updates - /srv/pipu/uploads:/uploads depends_on: [postgres, redis, meilisearch, storage-agent] storage-agent: image: ${REGISTRY}/pipu-music-storage-agent:${PIPU_VERSION} cap_add: [SYS_ADMIN] security_opt: ["apparmor:unconfined"] volumes: - type: bind source: /srv/pipu/library target: /library bind: { propagation: rshared } - /srv/pipu/uploads:/uploads:ro # читает staging при импорте # /library-rw/ монтируется агентом внутри своего контейнера, наружу не пробрасывается environment: [INTERNAL_TOKEN, DATABASE_URL, PIPU_FILE_UID, PIPU_FILE_GID] recommender: image: ${REGISTRY}/pipu-music-recommender:${PIPU_VERSION} command: ["serve"] env_file: .env depends_on: [postgres, redis] analyzer: image: ${REGISTRY}/pipu-music-recommender:${PIPU_VERSION} command: ["analyze", "--workers", "4"] env_file: .env volumes: [*library-ro, /srv/pipu/models:/models] deploy: { resources: { limits: { cpus: "4" } } } postgres: image: pgvector/pgvector:pg17 volumes: [/srv/pipu/postgres:/var/lib/postgresql/data] redis: image: redis:7-alpine command: ["redis-server", "--appendonly", "yes"] volumes: [/srv/pipu/redis:/data] meilisearch: image: getmeili/meilisearch:v1.11 environment: [MEILI_MASTER_KEY, MEILI_ENV=production] volumes: [/srv/pipu/meili:/meili_data] ``` Наружу открыт только `nginx`. Все остальные — во внутренней сети compose. ## Конфигурация (`.env`) | Переменная | Пример / по умолчанию | Описание | |---|---|---| | `PIPU_VERSION` | `1.0.0` | Версия образов | | `PUBLIC_URL` | `https://music.home.lan` | Внешний адрес | | `DATABASE_URL` | `postgres://pipu:…@postgres:5432/pipu` | | | `REDIS_URL` | `redis://redis:6379/0` | | | `MEILI_URL`, `MEILI_MASTER_KEY` | | | | `JWT_PRIVATE_KEY_FILE` | `/run/secrets/jwt_ed25519.pem` | Генерируется `install.sh` | | `STREAM_SIGNING_KEY` | 32 байта base64 | HMAC подписанных ссылок | | `INTERNAL_TOKEN` | случайный | Межсервисная аутентификация | | `BOOTSTRAP_ADMIN_USERNAME` | `admin` | | | `BOOTSTRAP_ADMIN_PASSWORD` | пусто → сгенерировать и вывести в лог | | | `TRANSCODE_CACHE_LIMIT_GB` | `50` | | | `TRANSCODE_CONCURRENCY` | `2` | | | `GITEA_URL`, `GITEA_REPO`, `GITEA_TOKEN` | `https://git.home.lan`, `owner/pipu-music`, read-only токен | Для самообновления Android | | `PIPU_FILE_UID`, `PIPU_FILE_GID` | `1000` | Владелец файлов, которые агент записывает в принимающие хранилища | | `UPLOADS_DIR` | `/uploads` | Staging очереди на добавление (лимит очереди задаётся в админке) | | `LOG_LEVEL` | `info` | | ## TLS - **Свой домен (рекомендуется)** — `deploy/compose.tls.yaml` добавляет Caddy, который получает и продлевает сертификат Let's Encrypt для `DOMAIN` и проксирует на nginx; nginx тогда наружу не открыт. Включается строкой `COMPOSE_FILE=compose.yaml:compose.tls.yaml` в `.env`. - **Свой reverse proxy** (Traefik, NPM, nginx) — без `compose.tls.yaml`, прокси направляется на `HTTP_PORT` (например `127.0.0.1:8081`); нужны `X-Forwarded-*`, тело до 16 МБ, без буферизации ответов. - nginx внутри образа `web` берёт схему и IP клиента из `X-Forwarded-Proto` / `X-Forwarded-For`, если запрос пришёл из частной сети (docker, LAN). - Локальная сеть без домена — свой CA; сертификат CA нужно установить на устройства или включить пиннинг в мобильном (см. mobile.md). Пошаговая установка на домен — [install-guide.md](install-guide.md). ## Бэкапы - `pg_dump` ежедневно (cron-контейнер `backup`), хранить 14 дней + еженедельные 8 недель → `/srv/pipu/backups`. - Meilisearch, кэши и модели **не бэкапятся** — восстанавливаются (`reindex`, повторный транскод / анализ). - Признаки анализа хранятся в Postgres — после восстановления из бэкапа повторный многочасовой анализ не нужен. - Сама музыка — на NFS, вне зоны ответственности приложения. ## Обновление ```bash cd /opt/pipu && ./update.sh 1.3.0 # pull образов, up -d, миграции при старте api, проверка /readyz ``` Откат: `./update.sh 1.2.4` (миграции пишутся обратно-совместимыми в пределах минорной версии: сначала добавить, потом — в следующем релизе — удалить).