Files
obsidian-vault/family/how-to/openmediavault-rpi5.md
T
2026-05-29 09:01:25 +00:00

545 lines
26 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags:
- raspberry-pi
- nas
- openmediavault
- how-to
created: '2026-05-11'
updated: '2026-05-12'
status: hermes-pending
ip: 192.168.1.14
---
# OpenMediaVault на Raspberry Pi 5
**Статус:** Контейнеры запущены, Time Machine pending (май 2026)
**Цель:** NAS на RPi 5 — OS на SD-карте, хранилище на USB 3.0 HDD
## Прогресс
| Шаг | Статус | Заметки |
|-----|--------|---------|
| 1 — Флашим SD | ✅ | Bookworm 2025-05-13, RPi Imager |
| 2 — Первый запуск + обновление | ✅ | apt upgrade выполнен |
| 3 — Установка OMV | ✅ | OMV 7 установлен |
| 4 — Первый вход в веб-интерфейс | ✅ | Пароль сменён |
| 5 — Сеть | ✅ | DHCP static lease `192.168.1.14` (eth0 MAC `2c:cf:67:64:03:f3`); WiFi включён (wlan0, ASUS5, 192.168.1.15, 2026-05-19) |
| 6 — USB HDD | ✅ | 12TB Seagate ST12000NT001, EXT4, UUID `49e8f586-3839-4c5d-a1e1-58bfc3579ade`, смонтирован через OMV в `/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade` |
| 7 — omv-extras + Docker | ✅ | omv-extras 7.0.6, Docker 29.4.3, kraken в группе docker |
| 8 — Контейнеры | ✅ | Все через отдельные docker compose, конфиги на HDD (см. ниже) |
| 9 — Time Machine share | ✅ | `mbentley/timemachine:smb`, расписание 10:00-22:00 GMT+6, HDD/timemachine |
| 10 — Перенос данных | N/A | не входит в этот гайд |
| 11 — Hermes агент Кракен | ✅ | Запущен, Telegram @kraken_htpc_bot живой, mcpvault ✅, obsidian RW, SOUL.md, browser/Playwright ✅, Spotify ✅ |
| 11а — Docker на HDD | ✅ | data-root + containerd перенесены на HDD через symlink, SD освободилась до 22% |
| 12 — Obsidian sync (Taiga↔Kraken↔Eagle) | ✅ | Все 3 узла работают, кроны активны, двусторонний sync протестирован |
|| 13 — Vault wiki (llm-wiki) | ✅ | Eagle: scheduled wiki-curation job, Karpathy-style. Active 2026-05-13. |
| 14 — SMB media share | ✅ | Samba вручную, шара `media` read-only guest. smbd enabled. |
---
## Железо
- Raspberry Pi 5 (4 или 8 GB RAM)
- SD-карта ≥16 GB (Samsung/SanDisk A1 Class 10+)
- USB 3.0 HDD для хранения данных
- Проводной Ethernet (обязателен)
---
## Шаг 1 — Флашим SD-карту
**Образ:** Raspberry Pi OS Lite **64-bit (Bookworm / Debian 12)**
> ⚠️ Брать именно Bookworm, не Trixie (Debian 13) — OMV не поддерживается на Trixie!
- Последний Bookworm: **2025-05-13**
- Прямая ссылка: https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2025-05-13/2025-05-13-raspios-bookworm-arm64-lite.img.xz
**Инструмент:** Raspberry Pi Imager (https://www.raspberrypi.com/software/)
> ⚠️ Не Balena Etcher — только RPi Imager поддерживает OS Customisation!
> При выборе образа из каталога (не custom file) появляется кнопка "Edit Settings"
**Настройки (Edit Settings → шестерёнка):**
- Hostname: `kraken`
- Username: `kraken` (не `admin` — зарезервировано для OMV!)
- Password: сохранён в 1Password (запись "Kraken RPi")
- SSH: Enable → Allow public-key authentication → вставить `~/.ssh/id_rsa.pub`
После флаша вставить карту в Pi и включить.
---
## Шаг 2 — Первый запуск, обновление и настройка сети
SSH подключение (пока через WiFi/роутер):
```bash
ssh -i ~/.ssh/id_rsa kraken@kraken
```
Обновление системы:
```bash
sudo apt-get update && sudo apt-get upgrade -y
```
Static IP на eth0 (прямой кабель Mac ↔ Pi для установки):
```bash
sudo nmcli con add type ethernet ifname eth0 con-name eth0-static \
ipv4.method manual \
ipv4.addresses 192.168.100.2/24 \
ipv4.gateway 192.168.100.1 \
ipv4.never-default yes \
connection.autoconnect yes
sudo nmcli con up eth0-static
```
> ⚠️ `ipv4.never-default yes` — обязательно! Иначе интернет пойдёт через Mac, а не WiFi, и установка зависнет.
---
## Шаг 3 — Установка OMV
```bash
# Pre-install
wget -qO- https://github.com/OpenMediaVault-Plugin-Developers/installScript/raw/master/preinstall | sudo bash
sudo reboot
# После перезагрузки — основная установка (~30 мин)
wget -qO- https://github.com/OpenMediaVault-Plugin-Developers/installScript/raw/master/install | sudo bash
```
Pi перезагрузится автоматически по окончании.
---
## Шаг 4 — Первый вход в веб-интерфейс
- URL: `http://192.168.100.2` (или `http://kraken` через LAN)
- Логин: `admin`
- Пароль: `openmediavault`
- **Сразу сменить пароль!** (System → General Settings → Web Administrator Password)
---
## Шаг 5 — Сеть: DHCP + static lease, отключить WiFi
Переключить кабель Pi → роутер. Настроить DHCP через netplan (OMV управляет `/etc/netplan/20-openmediavault-eth0.yaml`):
```yaml
network:
ethernets:
eth0:
match:
macaddress: "2c:cf:67:64:03:f3"
dhcp4: yes
dhcp6: no
link-local: []
```
В роутере (ASUS): добавить static lease в `dhcp_staticlist` через nvram:
```bash
ssh -i ~/.ssh/id_rsa admin@192.168.1.1 \
"nvram set dhcp_staticlist='...<2C:CF:67:64:03:F3>192.168.1.14>kraken>' && nvram commit && service restart_dnsmasq"
```
Отключить WiFi навсегда (modprobe blacklist):
```bash
sudo sh -c 'echo blacklist brcmfmac > /etc/modprobe.d/disable-wifi.conf' < /dev/null
sudo sh -c 'echo blacklist brcmutil >> /etc/modprobe.d/disable-wifi.conf' < /dev/null
sudo update-initramfs -u -k all < /dev/null
```
> ⚠️ Все sudo команды через SSH требуют `< /dev/null` — иначе sudo ждёт stdin и зависает
---
## Шаг 6 — USB HDD
1. Подключить HDD к синему (USB 3.0) порту Pi
2. Проверить скорость: `sudo apt install hdparm && sudo hdparm -t --direct /dev/sda` → должно быть ~200-250 MB/s
3. Разметить: `sudo parted -s /dev/sda mklabel gpt mkpart primary 0% 100%`
4. Форматировать (агент не может — только вручную или через скрипт):
```bash
sudo bash /tmp/fmt.sh # скрипт с mkfs.ext4 -L storage /dev/sda1
```
5. OMV Web UI → Storage → File Systems → выбрать `/dev/sda1` → Mount
6. OMV монтирует в: `/srv/dev-disk-by-uuid-<UUID>/`
> ⚠️ Не монтировать вручную через fstab — OMV должен управлять монтированием сам
---
## Шаг 7 — omv-extras + Docker
```bash
# omv-extras
wget -qO- https://github.com/OpenMediaVault-Plugin-Developers/packages/raw/master/install | sudo bash < /dev/null
# Docker
curl -fsSL https://get.docker.com -o /tmp/get-docker.sh && sudo sh /tmp/get-docker.sh < /dev/null
sudo usermod -aG docker kraken < /dev/null
```
---
## Шаг 8 — Docker контейнеры
Каждый сервис — отдельный `docker-compose.yml` в `/home/kraken/<name>/`.
Все конфиги на HDD: `/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker/<name>/`
| Сервис | Compose | URL | Данные |
|--------|---------|-----|--------|
| Portainer | `/home/kraken/portainer/` | :9000 | HDD/docker/portainer |
| Watchtower | `/home/kraken/watchtower/` | — | stateless |
| Home Assistant | `/home/kraken/homeassistant/` | :8123 | HDD/docker/homeassistant |
| Jellyfin | `/home/kraken/media/` | :8096 | HDD/docker/jellyfin, media:ro |
| Transmission | `/home/kraken/media/` | :9091 | HDD/docker/transmission, HDD/media/downloads |
| Prowlarr | `/home/kraken/radarr/` | :9696 | HDD/docker/prowlarr |
| Radarr | `/home/kraken/radarr/` | :7878 | HDD/docker/radarr, media:rw |
| Sonarr | `/home/kraken/radarr/` | :8989 | HDD/docker/sonarr, media:rw |
| Rclone | `/home/kraken/rclone/` | — | cron воскр. 03:00 → mailru-crypt:kraken/docker |
Управление:
```bash
cd /home/kraken/<name> && sudo docker compose up -d # запустить
cd /home/kraken/<name> && sudo docker compose restart # рестарт
cd /home/kraken/<name> && sudo docker compose pull && sudo docker compose up -d # обновить
```
> ⚠️ Watchtower: нужна переменная `DOCKER_API_VERSION=1.54` — иначе крашится (API mismatch)
> ⚠️ sudo через SSH требует `< /dev/null` для каждой команды с tee/pipe
---
## Media pipeline — Transmission hook + PGS→SRT
**Статус: ✅ (2026-05-18)**
### Схема
```
Transmission → on-download-complete.sh → media-pipeline:kraken sync (NFO, библиотека)
→ pgs-to-srt.sh (PGS субтитры → SRT)
```
### Файлы
```
HDD/docker/media-pipeline/
on-download-complete.sh — монтирован в Transmission как /config/on-download-complete.sh (ro)
pgs-to-srt.sh — конвертация PGS→SRT через pgsrip
config.json — конфиг media-pipeline
download-complete.log — лог загрузок
pgs-to-srt.log — лог конвертации
```
### on-download-complete.sh
Запускается Transmission синхронно при завершении загрузки (`script-torrent-done-filename`).
Получает `TR_TORRENT_DIR` и `TR_TORRENT_NAME` — пути **внутри контейнера** (`/downloads/...`).
Вызывает `pgs-to-srt.sh` с путём `/downloads/complete/radarr/...` — скрипт сам переводит в хостовый путь.
> ⚠️ Скрипт выполняется **внутри контейнера Transmission** — хостовые пути недоступны напрямую.
> `/downloads` → `HDD/media/downloads`, `/media` → `HDD/media` смонтированы в контейнере.
> Не вызывать бинарники с хостовых путей (`$HDD/...`) — они недоступны внутри контейнера.
### pgs-to-srt.sh
- Живёт на **хосте** (`HDD/docker/media-pipeline/`), вызывается из `on-download-complete.sh`
- Использует `mkvmerge -i` для детекции PGS/VobSub (быстрее `mkvinfo`)
- PGS → `~/.local/bin/pgsrip -l ru -l en`
- VobSub → `SKIP` (не реализовано)
- Маркер `.sub-checked` рядом с mkv — пропускать уже проверенные файлы при bulk-скане
- Флаг `-l ru -l en` (два отдельных флага, не через запятую!)
### Библиотека субтитров (итог скана 2026-05-18)
| Папка | Тип | Статус |
|-------|-----|--------|
| documentaries-series/пингвины. шпион в толпе (3 файла) | HDMV PGS | ✅ .ru.srt + .en.srt |
| documentaries-series/жизнь птиц 1998 (10 файлов) | VobSub | ❌ не конвертированы (SKIP) |
| Всё остальное | SRT внутри mkv | ✅ транскода нет |
### Remote path mapping (Radarr/Sonarr)
Transmission пишет в `/downloads/complete/{radarr,sonarr}/...` (внутри контейнера).
Radarr/Sonarr видят файлы через `/media/downloads/...`.
Маппинг добавлен: `host=transmission, remotePath=/downloads/, localPath=/media/downloads/`.
---
## Шаг 9 — Time Machine share
Контейнер: `mbentley/timemachine:smb`
Compose: `/home/kraken/timemachine/docker-compose.yml`
Данные: `/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/timemachine/`
**Режим работы:** `restart: unless-stopped` — контейнер стартует автоматически при загрузке и работает постоянно.
**Credentials:**
- Пользователь: `timemachine`
- Пароль: `timemachine` ⚠️ сменить позже
**Подключение с Mac:**
1. Finder → `⌘K` → `smb://kraken`
2. Логин: `timemachine` / `timemachine`
3. System Preferences → Time Machine → Add Disk → выбрать `kraken`
> ⚠️ Не останавливать контейнер резко (`docker stop`) пока TM пишет бэкап — Samba оставляет stale lock на `mbp-black.sparsebundle/lock`. При следующем запуске TM не может смонтировать образ. Фикс: рестарт контейнера (`docker restart timemachine`).
## Changelog — Time Machine
- **2026-05-14** — добавлен cron-контроль: запуск 04:00 UTC / остановка 16:00 UTC
- **2026-05-22** — cron убран, `restart: unless-stopped`. Причина: резкая остановка через cron оставляла stale Samba lock → TM не мог смонтировать образ при следующем запуске
---
## Шаг 14 — SMB media share
**Статус: ✅ (2026-05-16)**
Samba установлена как часть OMV, но плагин `openmediavault-samba` не используется — конфиг управляется вручную.
`/etc/samba/smb.conf` (шара добавлена в конец файла):
```ini
[media]
path = /srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/media
comment = Kraken Media
browseable = yes
read only = yes
guest ok = yes
force user = kraken
```
`smbd` включён в автозапуск: `sudo systemctl enable smbd`
**Подключение с Mac:** Finder → `⌘K` → `smb://kraken` → шара `media` → гостевой доступ.
> ⚠️ OMV не управляет smb.conf (плагин не установлен) — правки не слетают при `omv-salt deploy run`.
> ⚠️ nmbd не нужен (`disable netbios = yes` в глобальных настройках OMV).
---
## Шаг 10 — Перенос данных
### Фильмы с Mac на NAS
```bash
rsync -avh --progress /path/to/movies/ kraken@kraken:/srv/.../media/movies/
```
### Time Machine
> ⚠️ Старый TM-бэкап перенести нельзя напрямую — TM привязывает бэкап к конкретному диску/хосту.
> Начать новый бэкап на NAS, старый HDD хранить как архив или отформатировать после нескольких успешных новых бэкапов.
---
## Hermes агент — ограничения и архитектура
> Важно знать при отладке поведения агента.
Агент работает **внутри** контейнера `hermes-kraken`. Это означает:
| Возможность | Статус | Способ |
|---|---|---|
| Читать/писать vault | ✅ | `/vault/` через mcpvault MCP |
| Писать файлы на хост | ✅ только через alpine+bind `/:/host` | Portainer API |
| docker / docker cp | ❌ нет в контейнере | только Portainer API |
| SSH на хост | ❌ нет ключей | — |
| apt install | ⚠️ слетает при rebuild | не использовать |
| Portainer endpoint ID | **3** (не 1!) | проверять: `GET /api/endpoints` |
### Передача файлов на хост — только base64
```bash
# Контент → base64 → Cmd alpine-контейнера → decode на хосте
B64=$(printf '%s' 'content' | base64 | tr -d '\n')
# Cmd: ["sh", "-c", "echo <B64> | base64 -d > /host/path && echo DONE"]
# HostConfig.Binds: ["/:/host"]
```
**Никогда не использовать:** echo с кавычками, printf escaping, heredoc — все они ломаются при передаче через Docker Cmd array.
### Токены в Portainer API — не через shell-переменные
Hermes маскирует секреты как `***` в terminal output. `$TOKEN` в JSON передаётся буквально.
Правильно: интерполировать значение ДО построения JSON (через Python), не в shell-строке curl.
### Правило трёх попыток
Если действие не удаётся 3 раза — агент должен остановиться и сообщить пользователю.
---
## Шаг 11 — Hermes агент Кракен
**Статус: ✅ запущен (2026-05-12), mcpvault ✅ (2026-05-13)**
### Файлы
```
/home/kraken/hermes/
Dockerfile — образ hermes-kraken:latest
docker-compose.yml — restart: unless-stopped, network_mode: host
.env — API ключи
```
Конфиги и данные на HDD:
```
/srv/dev-disk-by-uuid-49e8f586.../docker/hermes/config/
config.yaml — основной конфиг Hermes
.local/bin/mcpvault — symlink → ../lib/node_modules/@bitbonsai/mcpvault/dist/server.js
.local/lib/node_modules/@bitbonsai/mcpvault/ — persistent, не слетает при rebuild
```
### Toolsets (актуально 2026-05-13)
| Toolset | Статус |
|---------|--------|
| hermes-cli | ✅ |
| terminal | ✅ |
| file | ✅ |
| web (web_search + web_extract) | ✅ Tavily |
| browser (Playwright/Chromium) | ✅ (добавлен 2026-05-13) |
| memory | ✅ |
| spotify | ✅ токен скопирован с Eagle (PKCE, client_secret не нужен) |
| cronjob | ✅ |
> ⚠️ При сборке Кракена: `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` убран, `npx playwright install --with-deps chromium --only-shell` добавлен в Dockerfile
> ⚠️ Spotify — plugin toolset. Нужно: 1) `spotify` в `toolsets:` в config.yaml, 2) `known_plugin_toolsets.telegram: [spotify]` в config.yaml, 3) `auth.json` скопирован с Eagle (содержит refresh_token, PKCE). `client_secret` не нужен.
> ⚠️ SOUL.md не должен содержать `curl ... $TOKEN/KEY/SECRET` — Hermes блокирует файл по паттерну `exfil_curl`. Заменять на псевдокод (`# GET /api/...`).
### Модели (актуально 2026-05-13)
| Модель | Статус |
|--------|--------|
| `gemini-2.5-flash` | ✅ primary |
| openrouter fallbacks | ✅ |
> ⚠️ **Gemini prepaid кредиты исчерпаны (2026-05-14)** — HTTP 429 RESOURCE_EXHAUSTED. Пока не пополнено, Кракен работает на OpenRouter free fallback (не поддерживает vision — изображения из Telegram не обрабатываются). Пополнить: https://aistudio.google.com/projects
### Telegram бот
- Имя: Кракен / Bot: [@kraken_htpc_bot](https://t.me/kraken_htpc_bot)
### MCP серверы
| Server | Команда | Vault |
|--------|---------|-------|
| obsidian | `/opt/data/.local/bin/mcpvault /vault` | `/home/kraken/obsidian` → `/vault:rw` |
> ⚠️ mcpvault установлен через `npm install -g --prefix /opt/data/.local` — живёт на HDD, переживает rebuild образа
> ⚠️ Vault монтируется как `/vault:rw` (не `:ro`!) — иначе mcpvault не может писать заметки
> ⚠️ config.yaml: указывать **полный путь** `/opt/data/.local/bin/mcpvault` — PATH в контейнере его не включает
> ⚠️ args: `/vault` (путь внутри контейнера), не `/home/kraken/obsidian`
### Деплой / пересборка
```bash
# Сборка (осторожно — npm install съедает место на SD!)
cd /home/kraken/hermes && docker build -t hermes-kraken:latest .
# Если ENOSPC при сборке — сначала почистить build cache:
docker builder prune -af
# Запуск (docker compose не работает через SSH из-за long-lived process detection)
docker stop hermes-kraken && docker rm hermes-kraken
docker run -d --name hermes-kraken --restart unless-stopped --network host \
-v /srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker/hermes/config:/opt/data \
-v /home/kraken/obsidian:/vault:rw \
-e HERMES_UID=1000 -e HERMES_GID=1000 -e HERMES_ALLOW_ROOT_GATEWAY=1 \
--env-file /home/kraken/hermes/.env hermes-kraken:latest
# Проверка
docker logs hermes-kraken --tail 20
docker inspect hermes-kraken --format 'Status={{.State.Status}} Restarts={{.RestartCount}}'
```
### TODO
- [ ] Настроить HA → создать Long-Lived token → вписать в HASS_TOKEN в .env
- [x] ~~Перенести Spotify auth~~ ✅ скопирован с Eagle
- [ ] Проверить SD backup cron (ручной запуск ✅ 6.3G/12мин; автозапуск — первое воскресенье 03:00)
> Запуск контейнера: `docker run ... hermes-kraken:latest gateway run` — `gateway run` уже в команде, compose не используется.
---
## Шаг 11а — Docker и containerd на HDD
**Статус: ✅ (2026-05-13)**
SD карта 14GB быстро заполняется Docker образами. Всё перенесено на HDD.
### Docker data-root
```bash
# /etc/docker/daemon.json
{"data-root": "/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker-data"}
```
### containerd → symlink на HDD
```bash
sudo systemctl stop docker docker.socket containerd
sudo rsync -a /var/lib/containerd/ /srv/.../containerd/
sudo mv /var/lib/containerd /var/lib/containerd.bak
sudo ln -s /srv/.../containerd /var/lib/containerd
sudo systemctl start containerd docker
sudo rm -rf /var/lib/containerd.bak
```
### Build cache (buildkit) — живёт на SD!
Buildkit cache НЕ управляется data-root — растёт на SD. Cron очистки:
```
# /etc/cron.d/docker-buildcache-cleanup
0 4 * * * root docker builder prune -af --filter until=24h > /dev/null 2>&1
```
При ручной сборке перед build: `docker builder prune -af`
### Текущее состояние SD (после всех переносов)
- SD: ~22% используется (2.9GB из 14GB), 11GB свободно
- HDD: docker-data + containerd + build cache → не мусорят SD
---
## Шаг 12 — Obsidian sync (Eagle ↔ Taiga ↔ Kraken)
**Статус: ✅ работает на всех 3 узлах (протестировано 2026-05-13)**
Подробная документация: [[obsidian-sync]]
**Схема:**
```
Eagle (полный vault ~/obsidian) ──push/pull──┐
Taiga (sparse: personal/ family/) ────────────├── TrueNAS bare repo
Kraken (sparse: personal/ family/) ───────────┘ mallexxx.duckdns.org:/mnt/RED_2TB/storage/git/obsidian-vault.git
```
**Скрипты и кроны:**
| Узел | Скрипт | Крон | Vault |
|------|--------|------|-------|
| Eagle | `~/scripts/sync-vault.sh` | `0 * * * *` | `~/obsidian` (full) |
| Kraken | `~/scripts/sync-vault.sh` | `0 * * * *` | `~/obsidian` (sparse) |
| Taiga | `~/sync-vault.sh` (на хосте!) | Hermes cron `0 * * * *` | `/mnt/RED_2TB/docker/hermes/vault` (sparse) |
> ⚠️ Тайга: скрипт запускается на **хосте TrueNAS** (не внутри контейнера) — только truenas_admin имеет ZFS ACL доступ к bare repo.
## Шаг 13 — Vault wiki (llm-wiki / Karpathy approach)
Eagle-only: scheduled Hermes job раз в сутки запускает wiki-curation:
- Упорядочивает `wiki/` папку по SCHEMA.md
- Строит cross-references между страницами
- Резолвит git-конфликты вручную (semantic merge)
- Lint: orphans, broken links, stale content
**TODO:**
- [x] ~~Создать `wiki/SCHEMA.md`, `wiki/index.md`, `wiki/log.md`~~ ✅ 2026-05-13
- [x] ~~Добавить cron job на Eagle: ежедневно 02:00, skill `llm-wiki`~~ ✅ 2026-05-13
- [x] ~~Настроить `WIKI_PATH=~/obsidian/wiki` в Eagle `.env`~~ ✅ 2026-05-13
---
## Уроки (что пошло не так при установке)
- **Balena Etcher** не поддерживает OS Customisation — использовать только **RPi Imager**
- **`userconf.txt`** deprecated на Bookworm — правильный способ через RPi Imager (он пишет `custom.toml`)
- **`systemd.run=` в cmdline.txt** нужно указывать `/usr/lib/raspberrypi-sys-mods/firstboot`, а не свой скрипт
- **`ipv4.never-default yes`** при настройке static IP на eth0 — обязательно, иначе интернет идёт через Mac и установка зависает
- **OMV install script** при зависании — скорее всего нет интернета, проверить маршруты (`ip route`)
---
## Полезные ссылки
- Официальный вики OMV: https://wiki.omv-extras.org/
- Install script: https://github.com/OpenMediaVault-Plugin-Developers/installScript
- RPi Imager: https://www.raspberrypi.com/software/
- RPi Downloads: https://www.raspberrypi.com/software/operating-systems/