Контейнер от непривилегированного пользователя не может писать в подключённый каталог: права это числа UID и GID, и они должны совпасть с хостом

Что было. Образ собран правильно: приложение запускается не от root, а от пользователя, созданного в Dockerfile с UID 5000. Каталог с хоста подключён через bind-mount для логов и данных. При старте cannot create log.txt: Permission denied. На хосте каталог принадлежит пользователю с UID 1000.

Почему. Ядро одно на хост и контейнер, и права на файлы проверяются по числовым UID/GID. Имя пользователя внутри контейнера ничего не значит: файл на хосте принадлежит 1000, процесс в контейнере работает от 5000, для ядра это чужой пользователь без права записи. Никакого «перевода» имён между хостом и контейнером по умолчанию нет.

Четыре рабочих способа.

  1. Запустить процесс с UID владельца каталога. docker run --user 1000:1000 … или в compose user: "1000:1000". Самый простой путь для своих сервисов. Недостаток: внутри контейнера у такого UID может не быть записи в /etc/passwd, некоторые программы на это жалуются.
  2. Сменить владельца каталога на хосте под UID контейнера. chown -R 5000:5000 /srv/app/data. Хорошо, когда образ чужой и UID фиксирован (у популярных образов он указан в документации, например 999 для баз данных, 1000 для многих приложений). Файлы на хосте будут «принадлежать» несуществующему пользователю, это нормально.
  3. Именованные тома вместо bind-mount. volumes: - appdata:/data: Docker при первом запуске копирует права из образа, и приложение пишет без вопросов. Данные лежат в /var/lib/docker/volumes, бэкапить их через docker run --rm -v appdata:/data -v $PWD:/backup alpine tar czf /backup/data.tgz /data.
  4. Параметризовать UID при сборке. В Dockerfile ARG UID=1000 и useradd -u $UID app, при сборке --build-arg UID=$(id -u). Удобно для сред разработки, где у каждого свой UID.

Что не делать. chmod -R 777 на каталог хоста снимает симптом и делает данные доступными любому процессу на машине; на сервере это недопустимо. Запуск контейнера от root «чтобы работало» возвращает все риски, ради ухода от которых создавали непривилегированного пользователя.

Диагностика за минуту.

docker run --rm -v /srv/app/data:/data образ id       # какой UID у процесса
ls -ln /srv/app/data                                  # числовые владельцы на хосте

Если числа не совпадают, применить один из способов выше. Для файлов, которые контейнер уже создал от root (частая история после первого запуска без --user), исправить владельца на хосте один раз.

Про SELinux. На системах с включённым SELinux нужен ещё суффикс :z или :Z у подключаемого каталога, иначе отказ будет даже при совпадающих UID.

Bind-mount описаны в документации Docker.