Запуск Docker-контейнера от пользователя Linux

При использовании готовых Docker-образов обычно не приходится задумываться о пользователе внутри контейнера. Но для локальной разработки, например с PHP, часто возникают неудобства с правами доступа к файлам.

Проблема

По умолчанию контейнеры Docker собираются и запускаются от имени root. Это не вызывает сложностей, пока вы работаете только через IDE. Но как только вы генерируете файлы через консоль внутри контейнера (например, с помощью php artisan make:model), а затем пытаетесь отредактировать их в IDE – появляется проблема.

При попытке сохранить файл в PhpStorm или VS Code вы увидите сообщение: Failed to save 'Test.php': Insufficient permissions. Select 'Retry as Sudo' to retry as superuser.

Почему так происходит? Файлы, созданные ранее через IDE, имеют владельца с UID 1000 (ваш пользователь на хосте), а файл Test.php, созданный через консоль внутри контейнера, принадлежит root.

root@7e8b1135c23f:/app# ls -l app/Models/
total 16
-rwxrwxr-x 1 1000 1000 1028 Feb 19 04:46 Post.php
-rwxrwxr-x 1 1000 1000  825 Feb 19 04:46 Settings.php
-rw-r--r-- 1 root root  107 Mar 15 09:32 Test.php
-rwxrwxr-x 1 1000 1000  587 Feb  2 07:55 User.php

Такой подход делает разработку неудобной: приходится либо постоянно менять владельца файлов, либо использовать sudo в IDE. Решение – собирать образ с пользователем, соответствующим пользователю на хосте.

Мои настройки Docker окружения

В своей практике я использую три основных файла для настройки окружения:

1. Makefile

Makefile помогает централизовать команды и автоматически передавать UID текущего пользователя:

.PHONY: build up down start stop

HOST_USER_ID:=$(shell id -u ${USER})
export HOST_USER_ID

build:
	docker compose build --no-cache
up:
	docker compose up -d
down:
	docker compose down --remove-orphans
start:
	docker compose start
stop:
	docker compose stop

id -u ${USER} возвращает числовой UID пользователя, который затем передается в окружение Docker Compose.

2. Dockerfile

В Dockerfile создается группа и пользователь с тем же UID, что и на хосте. Это гарантирует, что файлы, созданные внутри контейнера, будут принадлежать пользователю хоста.

FROM php:8.0-fpm

EXPOSE 9000

ARG HOST_USER
ARG HOST_USER_ID

WORKDIR /app

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
    # ...
    # Создаем пользователя и группу без домашнего каталога
    && addgroup --gid ${HOST_USER_ID} ${HOST_USER} \
    && adduser --quiet --disabled-password --gecos "" --no-create-home --ingroup ${HOST_USER} --uid ${HOST_USER_ID} ${HOST_USER}

USER ${HOST_USER}

Важно:

3. docker-compose.yml

В Compose-файле передаются аргументы сборки, а также монтируются нужные директории:

services:

  php-fpm:
    build:
      context: ./docker/php
      dockerfile: Dockerfile
      args:
        # подставляются из окружения, переданного через Makefile или командную строку
        HOST_USER: ${USER}
        HOST_USER_ID: ${HOST_USER_ID}
    image: myapp-php-fpm:latest
    restart: unless-stopped
    working_dir: /app
    volumes:
      - ./docker/php/10-myapp.ini:/usr/local/etc/php/conf.d/10-myapp.ini:ro
      - ./:/app

Результат работы

После сборки и запуска контейнера все файлы, созданные через консоль внутри контейнера, будут принадлежать пользователю хоста:

master@69e33465d1c3:/app$ ls -l app/Models/
total 16
-rwxrwxr-x 1 master master 1028 Feb 19 04:46 Post.php
-rwxrwxr-x 1 master master  825 Feb 19 04:46 Settings.php
-rw-r--r-- 1 master master  107 Mar 15 10:06 Test.php
-rwxrwxr-x 1 master master  587 Feb  2 07:55 User.php

Теперь можно спокойно редактировать файлы в IDE без запросов прав суперпользователя. Консольные команды и IDE работают с одними и теми же правами доступа, что делает разработку более комфортной.