# Установка сервера и подключение приложений Пошаговая инструкция: от чистого Linux-сервера до работающего Pipu Music на своём домене с HTTPS, плюс подключение веб- и Android-клиента. Справочник по устройству развёртывания — [deployment.md](deployment.md). В примерах: - домен — `music.example.com`, замените на свой; - репозиторий — `gitea.hsrv.site/mrleo1nid/pipu-music`, клонируется в `/opt/pipu-music`; - диск с музыкой смонтирован в `/mnt/music`; - данные сервиса (БД, кэши, сертификаты) — в `/srv/pipu`. Готовые Docker-образы не публикуются: сервер клонирует репозиторий и собирает образы сам (`docker compose build`). Релизы в Gitea содержат только APK для Android. > **Что умеет текущая версия.** В `v0.1.0` (этап M0) работают развёртывание, HTTPS и проверка связи > клиентов с сервером. Вход, библиотека и плеер появятся в следующих версиях > (см. [roadmap.md](roadmap.md)). Шаги, которые зависят от версии, помечены. --- ## 1. Что нужно заранее | Что | Требование | |---|---| | Сервер | Linux x86_64 (Debian 12 / Ubuntu 22.04+), 4+ ядра, 8+ ГБ RAM, 30+ ГБ на системном диске (система, образы, кэш сборки, данные) | | Доступ | root или пользователь с `sudo` | | Домен | Поддомен, которым вы управляете, например `music.example.com` | | Сеть | Входящие **80/tcp** и **443/tcp** (+ **443/udp** для HTTP/3) из интернета на сервер | | Музыка | Отдельный диск с библиотекой, подключённый к серверу (шаг 4). NFS-хранилища можно добавить позже из админки | | Интернет на сервере | Нужен при сборке: скачиваются базовые образы и зависимости Go / npm / Python | | Gitea | Доступ к репозиторию на чтение (если он приватный — токен или SSH-ключ) | --- ## 2. DNS Создайте у регистратора или в DNS-панели запись: | Тип | Имя | Значение | |---|---|---| | `A` | `music` | публичный IPv4 сервера | | `AAAA` | `music` | публичный IPv6 (если есть; если нет — запись не создавайте) | Если сервер стоит дома за роутером, пробросьте на роутере порты 80 и 443 на внутренний IP сервера. Проверка — команда должна вернуть IP вашего сервера: ```bash dig +short music.example.com ``` > Сертификат Let's Encrypt выдаётся, только когда домен уже указывает на сервер и порт 80 доступен из интернета. > Не запускайте шаг 7, пока эта проверка не проходит. --- ## 3. Docker ```bash curl -fsSL https://get.docker.com | sudo sh ``` Добавьте своего пользователя в группу `docker`, чтобы запускать сборку без `sudo` (после этого перелогиньтесь): ```bash sudo usermod -aG docker "$USER" ``` И `git`, если его нет: ```bash sudo apt-get install -y git ``` Проверьте, что есть Docker Compose v2.24 или новее (нужен для `compose.tls.yaml`): ```bash docker compose version ``` Откройте порты в файрволе, если он включён (пример для `ufw`): ```bash sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw allow 443/udp ``` --- ## 4. Диск с музыкой Музыка лежит на отдельном диске, смонтированном в `/mnt/music`. Если диск уже смонтирован — проверьте его командой из конца шага и переходите к шагу 5. Найдите диск и его UUID (раздел с музыкой — например, `/dev/sdb1`): ```bash lsblk -f ``` Создайте точку монтирования: ```bash sudo mkdir -p /mnt/music ``` Добавьте в `/etc/fstab` строку (подставьте свой UUID и файловую систему из вывода `lsblk -f`): ```ini UUID=1234abcd-0000-0000-0000-000000000000 /mnt/music ext4 defaults,nofail,noatime 0 2 ``` `nofail` нужен, чтобы сервер загружался, даже если диск не подключён. Смонтируйте и проверьте: ```bash sudo mount -a && findmnt /mnt/music && ls /mnt/music | head ``` Сервисы Pipu работают в контейнерах не от root, поэтому музыка должна быть доступна на чтение всем: файлы — `644`, каталоги — `755`. Если раньше права были закрытыми: ```bash sudo chmod -R a+rX /mnt/music ``` > Для дисков с NTFS / exFAT права задаются опциями монтирования, а не `chmod`: добавьте в fstab > `uid=0,gid=0,umask=022` вместо `defaults`. Pipu только читает этот диск. Записывать в него будет лишь импорт одобренных треков из очереди на добавление, и только если в админке разрешить это для хранилища ([uploads.md](uploads.md)). --- ## 5. Клонирование репозитория ```bash sudo mkdir -p /opt/pipu-music && sudo chown "$USER" /opt/pipu-music ``` ```bash git clone https://gitea.hsrv.site/mrleo1nid/pipu-music.git /opt/pipu-music && cd /opt/pipu-music/deploy ``` > Если репозиторий приватный: клонируйте по SSH (`git@gitea.hsrv.site:mrleo1nid/pipu-music.git`) с ключом, > добавленным в Gitea, или по HTTPS с токеном (право `read:repository`) — Git спросит логин и токен вместо пароля. > Сохраните доступ (`git config credential.helper store` или SSH-ключ): `update.sh` делает `git fetch` при каждом обновлении. Все дальнейшие команды выполняются из `/opt/pipu-music/deploy`. --- ## 6. Подготовка сервера ```bash sudo LIBRARY_HOST=/mnt/music ./install.sh ``` Скрипт: - создаёт каталоги данных в `/srv/pipu` (БД, кэши, загрузки, сертификаты); - делает `/srv/pipu/library` точкой монтирования с shared propagation, чтобы NFS-хранилища, которые потом подключит админ, были видны всем контейнерам, и ставит systemd-юнит, повторяющий это после перезагрузки; - проверяет, что `/mnt/music` существует, и записывает его в `.env` как `LIBRARY_HOST` — эту папку получат контейнеры `api`, `nginx` и `analyzer` (внутри они видят её как `/library-host`); - создаёт `.env` со случайными паролями БД, ключом поиска и межсервисным токеном. Заполните в `/opt/pipu-music/deploy/.env`: ```ini COMPOSE_FILE=compose.yaml:compose.tls.yaml DOMAIN=music.example.com ACME_EMAIL=you@example.com ``` Проверьте, что там же стоит путь к музыке (его вписал `install.sh`): ```ini LIBRARY_HOST=/mnt/music ``` Если диск смонтирован в другое место — поправьте путь здесь и перезапустите сервисы (`./update.sh`). `ACME_EMAIL` — адрес, на который Let's Encrypt пишет, если с сертификатом что-то не так. Остальные значения менять не нужно; `PIPU_VERSION` проставляет `update.sh`. `.env` не хранится в git (он в `.gitignore`), поэтому обновления репозитория его не трогают. --- ## 7. Сборка и запуск ```bash ./update.sh ``` Скрипт переключает репозиторий на последний релизный тег (например, `v0.1.1`), собирает образы, запускает все сервисы и ждёт готовности API (до 2 минут). Первая сборка занимает 5–15 минут, следующие — быстрее за счёт кэша Docker. Конкретная версия: `./update.sh v0.1.1`. > Локальная сборка поддерживается начиная с `v0.1.1`: в теге `v0.1.0` `compose.yaml` ещё ссылается на реестр образов. > Пока `v0.1.1` не вышел, запускайте `./update.sh main`. При первом запуске Caddy сам получит сертификат для `DOMAIN`, обычно за 10–30 секунд. Проверка с любого компьютера: ```bash curl -s https://music.example.com/api/v1/server-info ``` Ожидаемый ответ: `{"name":"pipu-music","version":"0.1.0",...}`. Проверка всех зависимостей: ```bash curl -s https://music.example.com/readyz ``` Должно вернуться `"status":"ok"` и `ok` для `postgres` и `redis`. --- ## 8. Веб-клиент Откройте `https://music.example.com` в браузере. - **v0.1.0:** карточка «Сервер 0.1.0 на связи» и переключатель языка RU / EN. - **С версии 0.2 (M1):** экран входа. Первый вход — логин `admin` и пароль, который сервер один раз выводит в лог при первом запуске: ```bash docker compose logs api | grep -i bootstrap ``` Сразу после входа приложение попросит сменить логин и пароль администратора ([auth-and-roles.md](auth-and-roles.md#первый-запуск-bootstrap)). Чтобы задать пароль заранее, впишите `BOOTSTRAP_ADMIN_PASSWORD` в `.env` **до** первого запуска. - **С версии 0.3 (M2):** Админка → Хранилища → «Добавить хранилище» → тип «Локальная папка» → выберите подпапку на диске `/mnt/music` (в списке она видна как `/library-host/...`) → «Проверить» → шаблоны путей → «Сохранить и пересканировать» ([library-and-storage.md](library-and-storage.md)). Там же можно добавить NFS-хранилище: адрес сервера и путь экспорта. --- ## 9. Android-приложение 1. На телефоне откройте страницу релизов `https://gitea.hsrv.site/mrleo1nid/pipu-music/releases` и скачайте `pipu-music-<версия>.apk`. 2. Откройте файл. Android попросит разрешить установку из этого источника (браузера или файлового менеджера) — разрешите и вернитесь к установке. 3. Запустите **Pipu Music**, на экране подключения введите адрес сервера: `music.example.com` (`https://` подставится сам) → «Проверить». 4. Ожидаемый результат — «Сервер 0.1.0 на связи». Если версия приложения слишком старая для сервера, приложение попросит обновиться. Дальше приложение обновляется само: с версии M5 оно проверяет новые релизы через ваш сервер и предлагает обновиться ([mobile.md](mobile.md#самообновление)). Адрес Gitea и репозиторий (`GITEA_URL`, `GITEA_REPO`) `install.sh` и `update.sh` берут из `git remote` клона; `GITEA_TOKEN` (только на чтение) нужен, если репозиторий закрытый. Если сервер не видит Gitea по внешнему адресу — впишите внутренний в `GITEA_URL`. Раздать приложение — ссылка `https://<ваш-домен>/android`. > Приложению нужен настоящий сертификат. С самоподписанным сертификатом и голым IP-адресом подключиться не получится. > Поэтому нужен домен с Let's Encrypt, как в этой инструкции. --- ## 10. Вариант: свой reverse proxy вместо Caddy Если на сервере уже есть Traefik, Nginx Proxy Manager или свой nginx с сертификатами: 1. В `.env` закомментируйте `COMPOSE_FILE` и задайте порт, на котором Pipu будет слушать только локально: ```ini #COMPOSE_FILE=compose.yaml:compose.tls.yaml HTTP_PORT=127.0.0.1:8081 ``` 2. Направьте прокси для `music.example.com` на `http://127.0.0.1:8081` с такими настройками: - передавать заголовки `Host`, `X-Forwarded-For`, `X-Forwarded-Proto`; - лимит тела запроса не меньше **16 МБ** (загрузка треков идёт чанками по 8 МБ); - **без буферизации ответов** (стриминг аудио), длинные таймауты чтения (≥ 1 ч). Пример для nginx: ```nginx server { listen 443 ssl http2; server_name music.example.com; # ssl_certificate / ssl_certificate_key — как у остальных ваших сайтов client_max_body_size 16m; location / { proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_buffering off; proxy_read_timeout 1h; } } ``` 3. Запуск и проверки — как в шагах 7–9. Если прокси работает на другой машине, укажите в `HTTP_PORT` внутренний адрес сервера (`192.168.1.10:8081`) и закройте этот порт от интернета файрволом. --- ## 11. Обновление, бэкапы, удаление Обновление до последнего релиза: ```bash cd /opt/pipu-music/deploy && ./update.sh ``` Конкретная версия или откат: `./update.sh v0.2.0`. Скрипт делает `git fetch`, переключается на тег, пересобирает образы и перезапускает сервисы; старые образы удаляются. Миграции БД применяются автоматически при старте API. Бэкапы базы делаются сами: каждую ночь в `/srv/pipu/backups`, раз в неделю копия проверяется восстановлением, перед каждым `update.sh` — внеочередная копия. Всё остальное восстанавливается само — см. [deployment.md](deployment.md#бэкапы). Копия сейчас и восстановление: ```bash cd /opt/pipu-music/deploy && docker compose run --rm backup once ``` ```bash cd /opt/pipu-music/deploy && ./restore.sh ``` Без аргумента `restore.sh` показывает список копий; с именем файла (`./restore.sh daily/pipu-20261020-033000.dump`) восстанавливает базу, сохранив прежнюю рядом. Остановить сервис, сохранив данные: ```bash cd /opt/pipu-music/deploy && docker compose down ``` --- ## 12. Если что-то не работает | Симптом | Что проверить | |---|---| | Сертификат не выдаётся, браузер ругается на HTTPS | `dig +short music.example.com` указывает на сервер; порт 80 открыт снаружи; `docker compose logs caddy` | | `502 Bad Gateway` | API ещё запускается или упал: `docker compose ps`, `docker compose logs api` | | `/readyz` отвечает `degraded` | Какая зависимость не `ok` — её логи: `docker compose logs postgres` / `redis` | | `update.sh`: ошибка `git fetch` | Нет доступа к репозиторию: проверьте SSH-ключ или сохранённый токен (`git fetch` вручную) | | Сборка падает на скачивании зависимостей | Нет интернета или DNS на сервере; повторите `./update.sh` — уже собранные слои берутся из кэша | | Сборка падает: «no space left on device» | Очистите кэш сборки: `docker builder prune` | | `permission denied … docker.sock` | Пользователь не в группе `docker` (шаг 3) или вы не перелогинились | | Сервисы не стартуют: `… is not a shared or slave mount` | `sudo mount --make-rshared /` (и добавьте в автозагрузку); на systemd-системах корень и так shared | | `update.sh`: «LIBRARY_HOST … пуст» | Диск с музыкой не смонтирован: `findmnt /mnt/music`, `sudo mount -a`; если папка и должна быть пустой — `PIPU_ALLOW_EMPTY_LIBRARY=1 ./update.sh` | | Сервисы не стартуют: `bind source path does not exist: /mnt/music` | Диск не смонтирован: `findmnt /mnt/music`, `sudo mount -a`; путь в `LIBRARY_HOST` в `.env` | | Хранилище «Локальная папка» пустое или ошибка доступа | Диск смонтирован (`ls /mnt/music`); права на чтение для всех — `sudo chmod -R a+rX /mnt/music` (шаг 4) | | `storage-agent` не стартует, ошибка про propagation / shared mount | Ещё раз `sudo ./install.sh`; на хосте `findmnt -o TARGET,PROPAGATION /srv/pipu/library` должно показать `shared` | | Android: «Сервер не отвечает» | Тот же адрес открывается в браузере телефона; в адресе нет опечатки; телефон в сети с доступом к серверу | | Android: «Эта версия приложения слишком старая» | Установите APK из последнего релиза | Логи всех сервисов сразу: ```bash cd /opt/pipu-music/deploy && docker compose logs -f --tail=100 ```