Issue
Containerized a Laravel application using Docker Compose. It simply has 2 services: app
(running PHP8.0-FPM) and nginx
.
docker-compose.yml
:
version: "3"
services:
app:
build:
args:
USER: novruz
context: ./
dockerfile: Dockerfile
image: blogger
container_name: blogger-app
restart: unless-stopped
working_dir: /var/www/
volumes:
- ./:/var/www/
networks:
- laravel
nginx:
image: nginx:alpine
container_name: blogger-nginx
restart: unless-stopped
ports:
- 8000:80
volumes:
- ./:/var/www/
- ./.docker/nginx/:/etc/nginx/conf.d/
networks:
- laravel
networks:
laravel:
driver: bridge
Dockerfile
:
FROM php:8.0-fpm
ARG USER
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
git \
zip \
unzip \
libpng-dev \
libonig-dev \
libxml2-dev \
libzip-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN docker-php-ext-install bcmath
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Create a system user to run Composer and Artisan commands.
RUN useradd -G www-data,root -d /home/$USER $USER
RUN mkdir -p /home/$USER/.composer \
&& chown -R $USER:$USER /home/$USER
WORKDIR /var/www
USER $USER
These configurations build (docker-compose build app
) and run (docker-compose up -d
) the application successfully.
Problem
But when I run composer install --optimize-autoloader --no-dev
in the app
service container, it throws an exception:
[RuntimeException]
/var/www/vendor does not exist and could not be created.
This is a permission issue, but how can I give right permissions to the user novruz
which was created at build time (and added to the group www-data
)?
Debug
Here's the interesting part. Everything works correctly on my local machine (MacBook Air M1 2020) but it does not on Ubuntu 20.04 (Focal Fossa).
ls -l
output on my local machine:
-rw-r--r-- 1 www-data www-data 556 Sep 14 13:53 Dockerfile
-rw-r--r-- 1 www-data www-data 17 Sep 9 07:00 README.md
drwxr-xr-x 7 www-data www-data 224 Sep 7 14:33 app
-rwxr-xr-x 1 www-data www-data 1686 Sep 7 14:33 artisan
drwxr-xr-x 4 www-data www-data 128 Sep 7 14:33 bootstrap
-rw-r--r-- 1 www-data www-data 1766 Sep 9 07:37 composer.json
-rw-r--r-- 1 www-data www-data 338242 Sep 9 07:37 composer.lock
drwxr-xr-x 17 www-data www-data 544 Sep 13 07:22 config
drwxr-xr-x 7 www-data www-data 224 Sep 13 06:45 database
-rw-r--r-- 1 www-data www-data 713 Sep 14 14:32 docker-compose.yml
-rw-r--r-- 1 www-data www-data 1319 Sep 9 07:04 phpunit.xml
drwxr-xr-x 7 www-data www-data 224 Sep 7 14:33 public
drwxr-xr-x 4 www-data www-data 128 Sep 9 07:07 resources
drwxr-xr-x 6 www-data www-data 192 Sep 10 05:52 routes
-rw-r--r-- 1 www-data www-data 563 Sep 7 14:33 server.php
drwxr-xr-x 5 www-data www-data 160 Sep 7 14:33 storage
drwxr-xr-x 7 www-data www-data 224 Sep 9 12:02 tests
As you can see, www-data
is the owner of /var/www/
directory and the files in it. I can run any command without any problem.
ls -l
output on Ubuntu 20.04:
-rw-rw-r-- 1 998 997 599 Sep 14 13:41 Dockerfile
-rw-rw-r-- 1 998 997 17 Sep 14 10:16 README.md
drwxrwxr-x 7 998 997 4096 Sep 14 10:16 app
-rwxrwxr-x 1 998 997 1686 Sep 14 10:16 artisan
drwxrwxr-x 3 998 997 4096 Sep 14 10:16 bootstrap
-rw-rw-r-- 1 998 997 1766 Sep 14 10:16 composer.json
-rw-rw-r-- 1 998 997 338242 Sep 14 10:16 composer.lock
drwxrwxr-x 2 998 997 4096 Sep 14 10:16 config
drwxrwxr-x 5 998 997 4096 Sep 14 10:16 database
-rw-rw-r-- 1 998 997 720 Sep 14 10:16 docker-compose.yml
-rw-rw-r-- 1 998 997 1319 Sep 14 10:16 phpunit.xml
drwxrwxr-x 2 998 997 4096 Sep 14 10:16 public
drwxrwxr-x 4 998 997 4096 Sep 14 10:16 resources
drwxrwxr-x 2 998 997 4096 Sep 14 10:16 routes
-rw-rw-r-- 1 998 997 563 Sep 14 10:16 server.php
drwxrwxr-x 5 998 997 4096 Sep 14 10:16 storage
drwxrwxr-x 5 998 997 4096 Sep 14 10:16 tests
But the user is 998
and the group is 997
on Ubuntu. And I keep getting a runtime exception. Why is this happening?
Solution
The problem here is that the UID/GID of the user within the container is not the same as the UID/GID of the user outside the container.
There are three ways you can go about solving this problem:
Don't use bind mounts. (This is when a directory is mounted both on the host and the container.) The problem is that the host and guest disagree about which users correspond to which UID, so if you make them not share files, this issue will go away.
Instead of using a bind mount, you can use
COPY
within the Dockerfile.COPY . /var/www
The downside is that it can be useful to edit the container's files while the container is running.
Match UID/GID inside and outside the container. Rather than create the user at build time, override the UID/GID of the FPM process at run time. This can be done in docker compose like this: How to set uid and gid in Docker Compose?
Remap users using user namespaces. I've never tried this approach, so I can't really give you any guidance, but here's the Docker documentation on how to do this: https://docs.docker.com/engine/security/userns-remap/
Also, here's a thread about using user remapping with bind mounts: https://forums.docker.com/t/permission-denied-when-using-userns-remap-and-writing-to-a-directory-mounted-from-the-host/48379/2
Answered By - Nick ODell