--- title: Docker data-root ↔ mount race — диагностика created: '2026-09-20' type: tech namespace: personal tags: - docker - systemd - mount - kraken - diagnostics confidence: high related: - '[[family/how-to/kraken-access]]' - '[[personal/tech/xray-reverse-tunnel-kraken-truenas]]' --- # Docker data-root ↔ mount race > Обнаружено 2026-09-20 на Kraken (RPi5/OMV, USB-HDD). Зеркало скилла > `docker-data-root-mount-race` (категория `docker-service-administration`). ## Триггер Хост, у которого Docker `data-root` лежит на **диске, монтирующемся поздно** (USB-HDD, OMV `/srv/dev-disk-by-uuid-*`, RPi). Признаки: - `docker ps -a` **пусто**, но контейнеры раньше были. - `docker info` → `Containers=0`, при этом диск с `docker-data` смонтирован. - После reboot сервисы в контейнерах (cloudflared, reverse-proxy bridge, jellyfin…) молча «down» — но контейнеры **не удалялись**. - journal: `Duplicate entry in '/etc/fstab'?` от `systemd-fstab-generator`. ## Механизм `dockerd` стартует **раньше**, чем монтируется диск с его `data-root`. Открывает путь, видит пусто → инициализирует **пустое** состояние (0 контейнеров). Через секунды диск монтируется поверх точки, реальный `containers/` появляется — но dockerd уже разобрал состояние и **не перечитывает**. **Ничего не потеряно.** Metadata цела на диске, daemon её просто не видит. Причина: у `docker.service` **нет `RequiresMountsFor=`** на маунт-юнит диска. ## Диагностика ```bash # 1) daemon думает, что пусто docker ps -a --format "{{.Names}}\t{{.Status}}" # пусто docker info --format "{{.DockerRootDir}} | containers={{.Containers}}" # 2) но metadata НА ДИСКЕ (нужен root; каталог 711 root:root) sudo ls -la /srv/dev-disk-by-uuid-/docker-data/ sudo ls /srv/dev-disk-by-uuid-/docker-data/containers/ | wc -l # >0 = целы # 3) подтвердить гонку: dockerd стартовал РАНЬШЕ диска ps -o pid,lstart,cmd -p "$(pgrep -x dockerd)" # истинное время старта uptime -s # время boot sudo dmesg | grep -iE "EXT4-fs \(sd[a-z]" | tail -5 # когда ФС смонтировалась findmnt /srv/dev-disk-by-uuid- # 4) подтвердить отсутствие зависимости (корень) systemctl show docker -p RequiresMountsFor # пусто = нет порядка grep -c "" /etc/fstab # 2 = дубль ``` **ПИТФОЛЛ — не то поле времени.** `systemctl show docker -p ExecMainStartTimestamp` может вернуть **stale** значение (прошлая активация). Реальное время — `ps -o lstart -p $(pgrep -x dockerd)`. **ПИТФОЛЛ — не тот источник RestartPolicy.** `RestartPolicy.Name` из `docker-data/containers//hostconfig.json` показывает то, что передал *исходный* `docker run` — часто `null`, даже когда политика есть в живом daemon state. НЕ делать вывод «контейнеры не поднимутся сами». Проверять живой daemon: ```bash docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' ``` Если `unless-stopped` — контейнеры **сами** вернутся при рестарте daemon, ручной `docker start` не нужен. ## Фикс ### Немедленный (вернуть сервисы — диск уже смонтирован) ```bash sudo systemctl restart docker docker info --format "containers={{.Containers}} running={{.ContainersRunning}}" ``` Контейнеры с `restart: unless-stopped` поднимаются сами. ### Постоянный (сделать reboot безопасным) 1. Бэкап: `/etc/fstab`, `/etc/docker/daemon.json`, compose-каталоги сервисов → датированная папка. 2. Drop-in, чтобы systemd не стартовал dockerd раньше диска: `/etc/systemd/system/docker.service.d/mounts.conf` ```ini [Unit] RequiresMountsFor=/srv/dev-disk-by-uuid-/docker-data After=srv-dev\x2ddisk\x2dby\x2duuid\x2d.mount ``` Имя `.mount` юнита = путь, где `/` → `-`, а каждый литеральный `-` escaped как `\x2d` (systemd escaping). 3. `sudo systemctl daemon-reload` 4. Убрать **дубль** строки монтирования того же диска из `/etc/fstab` (systemd отказывается от дублей; OMV часто добавляет свою рядом с ручной `UUID=...`). 5. Проверка: ```bash findmnt --verify --fstab # rc=0 systemctl show docker -p Requires -p After # маунт-юнит в ОБОИХ sudo systemctl restart docker # sanity: контейнеры перезагрузятся ``` ## Нельзя - Не предполагать потерю данных, если `docker ps` пусто, но диск смонтирован — **сначала посчитать `docker-data/containers/`**. `Containers=0` в `docker info` не доказательство удаления. - Не `rm -rf` «устаревший» docker-data — это и есть данные. - Не править `/etc/fstab` через sed; писать полный файл локально, валидировать `findmnt --verify --fstab`, затем копировать на место. ## Чеклист после фикса - [ ] `docker info` count == число каталогов в `docker-data/containers/`. - [ ] `systemctl show docker -p RequiresMountsFor` **не пусто** и указывает на `docker-data`. - [ ] `grep -c "" /etc/fstab` = `1`. - [ ] После тестового `systemctl restart docker` критичные контейнеры `Up`. - [ ] При следующем плановом reboot: `docker ps -q | wc -l` возвращает ожидаемое число, не `0`. ## Применение на Kraken (2026-09-20) Полный кейс: [[family/how-to/kraken-access]] и [[personal/tech/xray-reverse-tunnel-kraken-truenas]]. Кратко: boot 17:29:17, dockerd 17:31:01, диск 17:31:27 (на 26 с позже) → 0 контейнеров. Фикс: drop-in `RequiresMountsFor=/srv/.../6194539b.../docker-data` + чистка fstab. Бэкап: `/home/kraken/backups/docker-recovery-20260920-105228/`.