Ошибка Permission Denied в Docker Compose может возникнуть по нескольким причинам. Прежде всего и проще всего, у вашего пользователя может не быть прав для взаимодействия с сервисом Docker. Кроме того, поскольку в контейнере находится отдельная файловая система со своими правами и пользователями, если процесс в контейнере пытается получить доступ к файлам в хост-системе, вы тоже получите такую ошибку.
В этой небольшой статье мы рассмотрим, почему может возникнуть ошибка Permission Denied в Docker, а также как её исправить.
Содержание статьи
Как исправить ошибку Permission Denied в Docker
1. Когда нет прав доступа к сервису Docker
Тут все очень просто. Поскольку сервис Docker запущен от имени суперпользователя, с ним могут взаимодействовать только суперпользователь или пользователи, которые находятся в группе docker. Например, вы можете получить ошибку, когда выполняете такую команду:
docker compose up
Во-первых, её можно выполнить с правами суперпользователя:
sudo docker compose up
Или же, вы можете добавить своего пользователя в группу docker:
sudo usermod -aG docker $USER
После этого нужно перелогиниться в системе или в терминале, и всё заработает.
Вы можете проверить, в какие группы добавлен пользователь, с помощью такой команды:
id
2. Доступ к папкам, смонтированным в контейнер из хост-системы
Иногда необходимо иметь доступ к файлам, смонтированным в контейнер из хост-системы. Независимо от того, запущен контейнер от суперпользователя или нет, если пользователь, которому принадлежат файлы в контейнере, и в хост-системе не совпадает, то вы при попытке открыть файл из хост-системы без прав root получите permission denied.
Связи между именами пользователей в контейнере и в хост-системе нет. Но есть связь между их идентификаторами. Например, в хост-системе есть пользователь serhii с идентификатором 1000. В контейнере есть пользователь mysql, который тоже имеет идентификатор 1000. Тогда ядро Linux будет считать, что это один и тот же пользователь, и разрешит serhii из хост-системы доступ к файлам, которые в контейнере принадлежат пользователю mysql.
Итак, для того чтобы все заработало, нужно, чтобы ID пользователя в системе и в Docker контейнере совпадал. Для этого многие контейнеры позволяют задать ID пользователя и его группу с помощью переменных UID и GID или PUID и PGID. Для того чтобы узнать точно, какие переменные использовать, нужно смотреть документацию контейнера, который вам нужно запустить. Например, для lscr.io/linuxserver/mariadb это будет выглядеть так:
services:
mariadb:
image: "lscr.io/linuxserver/mariadb:latest"
restart: "always"
volumes:
- "./data:/config"
environment:
PUID: 1000
PGID: 1000
MYSQL_ROOT_PASSWORD: "password"
MYSQL_DATABASE: "test_db"
MYSQL_USER: "test_user"
MYSQL_PASSWORD: "password"
Но это не единственный способ. Большинство официальных контейнеров такого не поддерживают. Поэтому можно изменить пользователя и группу, от имени которой запускается контейнер, на другую с помощью директивы user. Здесь можно указать либо имя пользователя и группы, либо ID. Например:
services:
mariadb:
user: "1000:1000"
image: "mariadb:latest"
restart: "always"
volumes:
- "./data/db:/var/lib/mysql"
environment:
MYSQL_ROOT_PASSWORD: "password"
MYSQL_DATABASE: "test_db"
MYSQL_USER: "test_user"
MYSQL_PASSWORD: "password"
Аналогично можно изменить пользователя с помощью директивы USER в Dockerfile. Это будет работать так же. Но в обоих случаях папку, которую вы монтируете, нужно создать заранее, потому что если папка не существует, её создаст демон Docker, а он запущен от root. Следовательно, папка будет тоже принадлежать root, и тогда сервис не сможет ничего записать:
mkdir -p data/db
docker compose up
ls -l data
Но если проблема уже случилась и вы не хотите удалять файлы, в следующем разделе рассмотрим, как исправить.
3. Нет прав на запись в смонтированную папку
Если вы столкнулись с ошибкой permission denied внутри контейнера и вы монтировали в этот контейнер папки из файловой системы, и пользователь, от имени которого выполняется программа в контейнере, не root, то проблема, скорее всего, в этом. Если папка в хост-системе, которую вы хотите смонтировать в контейнер, не существовала до запуска проекта, то Docker её создаст. Но поскольку Docker запущен от root, то папка будет принадлежать root. Если процесс, который выполняется в контейнере, выполняется не от имени root, то доступа к этой папке он иметь не будет.
Давайте рассмотрим это на примере контейнера mariadb. Вот конфигурация, которая запускает сервис в контейнере от имени пользователя mysql и монтирует в него локальную папку data для сохранения файлов базы данных:
vi docker-compose.yaml
services:
mariadb:
user: "mysql"
image: "mariadb:latest"
restart: "always"
volumes:
- "./data/db:/var/lib/mysql"
environment:
MYSQL_ROOT_PASSWORD: "password"
MYSQL_DATABASE: "test_db"
MYSQL_USER: "test_user"
MYSQL_PASSWORD: "password"
Если мы запустим контейнер в таком виде, но не создадим папок, то возникнет вот такая ошибка:
И вот почему: процесс mysql принадлежит пользователю mysql, но смонтированная папка доступна для записи только root:
ls -l ./
Для того чтобы исправить проблему, нужно либо поменять права на папку, либо использовать root внутри контейнера, как было по умолчанию, убрав директиву user. Для того чтобы проверить ID пользователя в контейнере, можно запустить контейнер с помощью команды run:
docker compose run --rm mariadb bash
id
Тут видим, что контейнер выполняется от имени пользователя mysql, и этот пользователь имеет ID 999. Поэтому мы можем выполнить такую команду, чтобы исправить права на папку:
sudo chown -R 999:999 ./data
И после этого можно запускать MySQL в контейнере не от root, и всё будет работать. Я специально в этом примере выбрал пользователя mysql, который есть в контейнере, чтобы статья была проще для восприятия. Если бы мы настроили пользователя с ID 1000:1000, всё тоже работало бы аналогично. Но поскольку такого пользователя в контейнере нет, то команда id и другие показывали бы много ошибок. Если вам нужно, чтобы ID пользователя в системе и в контейнере совпадал и нужно работать в терминале контейнера, то лучше контейнер перепаковать и заменить ID пользователя mysql в Dockerfile на нужный:
vi mariadb/Dockerfile
FROM mariadb:latest
ARG USER_ID=1000
ARG GROUP_ID=1000
RUN usermod -u ${USER_ID} mysql
RUN groupmod -g ${GROUP_ID} mysql
USER mysql
Теперь используем новый образ в docker-compose:
vi docker-compose.yaml
services:
mariadb:
build:
context: "./mariadb"
restart: "always"
volumes:
- "./data/db:/var/lib/mysql"
environment:
MYSQL_ROOT_PASSWORD: "password"
MYSQL_DATABASE: "test_db"
MYSQL_USER: "test_user"
MYSQL_PASSWORD: "password"
Тогда каталог с базой данных тоже будет выглядеть вот так:
Выводы
В этой статье мы рассмотрели, почему может возникнуть ошибка Permission Denied в Docker при использовании Docker Compose, а также как её исправить. В целом это типичная проблема Docker, вызванная тем, что демон Docker и контейнеры запускаются от root. Предполагается, что и процессы в контейнерах должны запускаться от root. А когда вы начинаете пытаться запускать их от имени обычного пользователя, начинаются проблемы с правами.












