# macOS Docker Desktop — что НЕ проезжает в контейнер (virtiofs) > Создан 2026-09-26. Статус: **проверено живыми тестами** (alpine:3.20, linux/arm64, > Docker Desktop на macOS 26.6.1, Apple Silicon). > Тема: границы проброса путей из мира macOS в контейнер. > Все выводы получены прогонами, не по памяти. Команды воспроизведения — в §5. --- ## 0. Главный вывод одной строкой На macOS Docker Desktop отдаёт контейнеру файлы **не через Linux `mount`**, а через **virtiofs** — демон, который строит маппинг путей **в момент старта контейнера** и знает только пути **мира macOS**, известные ему на старте. Всё, что появляется **позже** и **внутри VM** (ручной `mount`, шара, новый каталог), изнутри VM видно корректно — **до контейнера не доходит**. Это не кэш: рестарт не помогает, `sleep` не помогает. 📌 На Linux-хосте (нативный Docker) этих ограничений **нет** — там `nsenter`-трюки с `mount --bind` работают. Все выводы ниже — про **macOS**. --- ## 1. Симлинки в контейнер НЕ проезжают Симлинк, чья цель лежит **вне** смонтированного каталога, в контейнере **битый**. ``` host: /tmp/symtest/outside/file.txt = "REAL CONTENT ON HOST" bind: -v /tmp/symtest/bind:/mnt (внутри /mnt/target -> /tmp/symtest/outside) контейнер: cat /mnt/target/file.txt → can't open '/mnt/target/file.txt': No such file or directory ``` Сам симлинк внутри **виден** (`lrwxrwxrwx target -> /tmp/symtest/outside`), но цель резолвится **относительно корня контейнера**, где этого пути нет. Docker монтирует только реальные пути — симлинки не разворачивает. **Следствие:** папка с симлинками на внешние цели в контейнере нерабочая, даже если на хосте всё открывается. --- ## 2. Live `mount --bind` (nsenter) контейнеру НЕ виден Схема обычная для Linux-хоста: ```bash docker run --rm --privileged --pid=host alpine nsenter -t 1 -m \ mount --bind /host/source /host/agents/target ``` ⚠️ **Первая ловушка (до самого mount):** `nsenter -t 1 -m` заходит в **VM**, а не в macOS. Пути **мира macOS там не существуют**. Поэтому хост-путь для VM нужно создавать **внутри VM** — например, в том же `nsenter -t 1 -m sh -c '...'`. Проверено **тремя** прогонами, результат одинаковый: | Прогон | Условие | Результат | |---|---|---| | 1 | каталог создан **после** старта контейнера + live bind | в контейнере пусто | | 2 | ожидание 5 с (гипотеза «кэш атрибутов virtiofs») | пусто | | 3 | каталог существовал **до** старта контейнера + bind **поверх** него | **пусто** | Изнутри VM всё корректно: ``` VM: cat /tmp/bindtest2/agents/existing/f.txt → "DATA2" ✓ файл есть mount: BIND_OK ✓ смонтировалось контейнер: cat /agents/existing/f.txt → No such file or directory ✗ ``` **Вывод:** live-bind для добавления каталогов в контейнер на macOS **недоступен**. Единственный рабочий путь — **`-v` в compose + пересоздание контейнера** (`docker compose up -d` = 2-3 сек, `--privileged` не нужен). --- ## 3. Сетевые шары (SMB/NFS) в контейнер НЕ протянуть тем же путём - `mount_smbfs`/`mount_nfs` в macOS создаёт точку в **мире macOS** → virtiofs её не видит → `-v /Volumes/share:/data` **не работает**. - mount **внутри VM** через `nsenter` технически проходит, но: - **не переживает рестарт Docker** (нужен startup-хук); - требует `--privileged`; - SMB/NFS не дают hardlink между экспортами, блокировки файлов ведут себя иначе — сборщики/редакторы на шаре работают хуже, чем на локальном диске. **Рекомендация:** не тянуть шару в контейнер. Если данные на NAS — **`rsync` в локальную папку** по требованию. Копия переживает всё и не требует privileged. --- ## 4. Hardlink — чего он НЕ может (независимо от Docker) Это свойство ФС, не Docker: 1. **Hardlink на каталог не делается.** Ни в APFS, ни в ext4, ни в NTFS. `ln dir1 dir2` → `Operation not permitted`. У каталога появилась бы ссылка на себя → цикл в дереве. Запрещено на уровне syscall. 2. **Между томами не работает.** Только внутри одной ФС (один `st_dev`), иначе `EXDEV: Cross-device link`. **Следствие:** «подключить проект-каталог hardlink'ом извне» — **физически невозможно**. Hardlink годится только для **файлов внутри одного тома**. --- ## 5. Матрица: чем подключать путь в контейнер | Способ | Папку? | Через тома? | Видно в контейнере (macOS) | Итог | |---|---|---|---|---| | symlink | да | да | ✗ битый | ❌ для внешних целей | | hardlink | **✗ нет** | ✗ нет | — | ✓ только файлы внутри тома | | live `mount --bind` (nsenter) | да | да | ✗ | ❌ на macOS | | **`-v` в compose + recreate** | да | да | ✓ | ✅ **рабочий путь** | | rsync-копия | да | да | ✓ | ✓ когда данные на шаре/NAS | --- ## 6. Команды воспроизведения ```bash # 1. Симлинк в контейнер mkdir -p /tmp/symtest/outside /tmp/symtest/bind echo "REAL CONTENT ON HOST" > /tmp/symtest/outside/file.txt ln -sfn /tmp/symtest/outside /tmp/symtest/bind/target docker run --rm --platform linux/arm64 -v /tmp/symtest/bind:/mnt alpine:3.20 \ sh -c 'ls -la /mnt/; cat /mnt/target/file.txt' # → No such file or directory # 2. Live bind (внимание: пути создавать ВНУТРИ VM) docker run -d --name t -v /tmp/bindtest2/agents:/agents alpine:3.20 sleep 300 docker run --rm --privileged --pid=host alpine:3.20 nsenter -t 1 -m sh -c \ 'mkdir -p /tmp/bindtest2/agents/existing /tmp/bindtest2/ext2 echo DATA2 > /tmp/bindtest2/ext2/f.txt mount --bind /tmp/bindtest2/ext2 /tmp/bindtest2/agents/existing' docker exec t cat /agents/existing/f.txt # → No such file or directory # 3. Hardlink на каталог (должен упасть) ln /tmp/a /tmp/b # → Operation not permitted ``` ⚠️ Тестовые `docker run` тянут образы — гонять их на Mac, не на слабом хосте (Kraken: buildkit-кэш и слои лягут на диск зря). --- ## 7. Что делать в реальном проекте 1. **Внешние каталоги** — только через **`-v` в compose** + `docker compose up -d`. Манифест монтажей (список `name` + `host_path`) генерирует `compose.override.yaml` — composefile не правится скриптом вручную. 2. **auth/секреты CLI — на хосте, ВНЕ рабочего каталога агента.** Иначе агент, которому рабочий каталог смонтирован на запись, получает доступ к своим же токенам. Монтировать отдельным `-v`. 3. **Шары** — `rsync` в локальную папку, не прямой mount. 4. **Hardlink** — только файлы внутри одного тома, с проверкой `st_dev`. 5. **Не строить архитектуру на live-mount'ах** — на macOS это тупик. --- ## Связанные заметки - [[personal/plans/balda-mm-phase4-agents]] — применение этих выводов (Фаза 4) - [[personal/projects/balda/balda-mm-delegation-plan]] — общий план проекта - скилл `eagle-dashboard` — сервис-реестр, health-типы, launchd - скилл `docker-service-administration` — lifecycle контейнеров