Phil Cross
Phil Cross

Reputation: 9302

Docker php-fpm running as www-data

I've recently been learning to build images and containers with Docker. I was getting fairly confident with it when using a Mac, but recently switched to Ubuntu, I'm fairly new to this side of development.

I'm using a standard new Laravel project as my "code", and am currently just using a php container and nginx container.

I'm using a docker-compose.yml file to create my containers:

version: "3.1"

services:
    nginx:
        image: nginx:latest
        volumes:
            - ./code:/var/www
            - ./nginx_conf.conf:/etc/nginx/conf.d/default.conf
        ports:
            - "80:80"
    php:
        image: php:7.3-fpm
        ports:
            - 9000
        volumes:
            - ./code:/var/www

There may or may not be a mistake in the code above just because I've just typed it out rather than copy and pasting - but it works on my machine.

The problem is:

However, when I access the application through the browser, I get a permission denied error on start up (when Laravel tries to create an error log file in storage). I think this is because php-fpm is running as www-data, but the storage directory has permissions drwxr-xr-x for owner / group phil:phil - my host owner and group.

I've tried the following, after hours of googling and trials:

I'm running out of ideas to try. The only other thing I can think of, is to recursively set owner and group of the code directory on my host to www-data and try run phpstorm as www-data instead, but that feels weird (Update: I tried to open phpstorm as www-data user, using sudo -u www-data phpstorm.sh, but i get a java exception - something to do with graphics -so this approach is unfeasible as well)

Now the only thing I can think of to try is to create a new php image from alpine base image and bypass php's images completely - which seems like an awful lot of inconvenience just because the maintainers want to use ENV instead of ARG?

I'm not sure of best practice for this scenario. Should I be trying to change how php-fpm is run (user/group)? should I be updating the directory owner/group on my host? should I be running phpstorm as a different user?

Literally any advice will be greatly appreciated.

Upvotes: 20

Views: 41131

Answers (5)

Hamid Mohayeji
Hamid Mohayeji

Reputation: 4285

Let's assume that you want to set the user of your PHP container and the owner of your project files to www-data. This can be done inside Dockerfile:

FROM php
.
.
.
RUN chown -R www-data:www-data /var/www
USER www-data # next instruction might face permission error if this line is not at the end of the dockerfile

The important fact here is that the original permissions in the Docker host are corresponded to the permission inside the container. Thus, if you now add your current user to www-data group (which probably needs a logout/reboot to take effect), you will have sufficient permission to edit the files outside the container (for instance in your IDE):

sudo usermod -aG www-data $USER

This way, the PHP code is permitted to run executables or write new files while you can edit the files on the host environment.

Upvotes: 0

Rhys Cabonita
Rhys Cabonita

Reputation: 129

[Disclaimer: Although, this works in development, I'm not sure if it's good practice in prod. I manually change some settings in prod as I haven't found the time to automate it]

Another solution to this is:

  1. Add /etc/passwd and /etc/group as read-only volumes (they store the system's user and group information)
services: 
    ...
    php:
        ...
        volumes:
            - ./code:/var/www/html
            - /etc/passwd:/etc/passwd:ro
            - /etc/group:/etc/group:ro
        ...

  1. usermod -a -G and chmod from either host or container.

usermod -a -G www-data <your_user>

chmod -R 775 ./code

We add our user to www-data group, and we change file permissions in chmod to allow for group members of www-data to have read, write, and execute permissions over the files


This should be enough to develop in UNIX. In prod, I usually remove the volumes: /etc/passwd and /etc/group from the compose.yml file and revert back to chmod -R 755 /var/www/html since I haven't studied the security implications of letting that be in prod.


This will mess up docker compose up for someone developing in Windows, so if you have team members developing in Windows, let them know, so they can either move their project to WSL or deal with the problem (I tried it in Windows to check if it breaks, but I don't have time to search for a fix ¯\(ツ)/¯ )

Upvotes: 0

br4nnigan
br4nnigan

Reputation: 739

@bnoeafk I'll just post this as a new answer although it has basically been said already. I don't think this is hacky, it works basically like ntfsusermap, certainly more elegant than changing all file permissions.

For the Dockerfile:

FROM php:7.4-apache

# do stuff...

ARG UNAME=www-data
ARG UGROUP=www-data
ARG UID=1000
ARG GID=1001
RUN usermod  --uid $UID $UNAME
RUN groupmod --gid $GID $UGROUP

Every user using this image can pass himself into it while building: docker-compose build --build-arg UID=$(id -u) --build-arg GID=$(id -g)

Upvotes: 10

Jean-Roch B.
Jean-Roch B.

Reputation: 553

You have many options depending on your system to do this, but keep in mind you may have to restart your running process (php-fpm for example)

Some examples on how to achieve this: (you can run the commands outside the container with: docker container exec ...)

  • Example 1:

usermod -g 1007 www-data

It will update the uid of the user www-data to 1007

  • Example 2:

deluser www-data

adduser -u 1007 -D -S -G www-data www-data

It will delete the user www-data and recreate it with the uid 1007

  • Get pid and restart process

To restart a a running process, for example php-fpm, you can do it that way:

First get the pid, with one of the following command:

pidof php-fpm

ps -ef | grep -v grep | grep php-fpm | awk '{print $2}'

find /proc -mindepth 2 -maxdepth 2 -name exe -lname '*/php-fpm' -printf %h\\n 2>/dev/null | sed s+^/proc/++

Then restart the process with the pid(s) you got just before (if your process support USR2 signal):

kill -USR2 pid <-- replace pid by the number you got before

I found that the easiest way is to update the host or to build your container knowing the right pid (not always doable if you work with different environments)

Upvotes: 2

Efrat Levitan
Efrat Levitan

Reputation: 5632

ran into the same problem a few weeks ago.

what actually happens is that your host and your container are sharing the same files via the volume, therefore, they also share the permissions.

in production, everything is fine - your server (the www-data user) should be the owner of the files, so no problem here. things get complicated in development - when you are trying to access those files from the host.

i know a few workarounds, the most hacky one seems to be to set www-data uid in the container to 1000, so it will match your uid in the host.

another simple one is to open 777 full permissions on the shared directory, since its only needed in the development build - (should never be done in production though, but as i mentioned before, in production you dont have any problem, so you must seperate the 2 processes and do it only in development mode)

to me, the most elegant solution seems to be to allow all group members to access the files (set 770 permissions), and add www-data to your group:

usermod www-data -a -G phill #// add it to your group

chown -r phill ./code #// make yourself the owner. might need sudo.

chmod 770 ./code #//grunt permissions to all group members

Upvotes: 12

Related Questions