skip to Main Content

My Dockerfile extends from php:8.1-apache. The following happens while developing:

  • The application creates log files (as www-data, 33:33)
  • I create files (as the image’s default user root, 0:0) within the container

These files are mounted on my host where I’m acting as user (1000:1000). Of course I’m running into file permission issues now. I’d like to update/delete files created in the container on my host and vice versa.


My current solution is to set the image’s user to www-data. In that way, all created files belong to it. Then, I change its user and group id from 33 to 1000. That solves my file permission issues.

However, this leads to another problem:
I’m prepending sudo -E to the entrypoint and command. I’m doing that because they’re normally running as root and my custom entrypoint requires root permissions. But in that way the stop signal stops working and the container has to be killed when I want it to stop:

~$ time docker-compose down
Stopping test_app ... done
Removing test_app ... done
Removing network test_default

real    0m10,645s
user    0m0,167s
sys     0m0,004s

Here’s my Dockerfile:

FROM php:8.1-apache AS base

FROM base AS dev
COPY entrypoint.dev.sh /usr/local/bin/custom-entrypoint.sh

ARG user_id=1000
ARG group_id=1000

RUN set -xe 
    # Create a home directory for www-data
    && mkdir --parents /home/www-data 
    && chown --recursive www-data:www-data /home/www-data 
    # Make www-data's user and group id match my host user's ones (1000 and 1000)
    && usermod --home /home/www-data --uid $user_id www-data 
    && groupmod --gid $group_id www-data 
    # Add sudo and let www-data execute it without asking for a password
    && apt-get update 
    && apt-get install --yes --no-install-recommends sudo 
    && rm --recursive --force /var/lib/apt/lists/* 
    && echo "www-data ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/www-data

USER www-data

# Run entrypoint and command as sudo, as my entrypoint does some config substitution and both normally run as root
ENTRYPOINT [ "sudo", "-E", "custom-entrypoint.sh" ]
CMD [ "sudo", "-E", "apache2-foreground" ]

Here’s my custom-entrypoint.sh

#!/bin/sh
set -e

sed --in-place 's@^RemoteIPTrustedProxy.*@RemoteIPTrustedProxy '"$REMOTEIP_TRUSTED_PROXY"'@' $APACHE_CONFDIR/conf-available/remoteip.conf

exec docker-php-entrypoint "$@"

What do I need to do to make the container catch the stop signal (it is SIGWINCH for the Apache server) again? Or is there a better way to handle the file permission issues, so I don’t need to run the entrypoint and command with sudo -E?

2

Answers


  1. You can use user namespace to map different user/group in your docker to you on the host.

    For example, the group www-data/33 in the container could be the group docker-www-data/100033 on the host, you just have be in the group to access log files.

    Login or Signup to reply.
  2. What do I need to do to make the container catch the stop signal (it is SIGWINCH for the Apache server) again?

    First, get rid of sudo, if you need to be root in your container, run it as root with USER root in your Dockerfile. There’s little value add to sudo in the container since it should be an environment to run one app and not a multi-user general purpose Linux host.

    Or is there a better way to handle the file permission issues, so I don’t need to run the entrypoint and command with sudo -E?

    The pattern I go with is to have developers launch the container as root, and have the entrypoint detect the uid/gid of the mounted volume, and adjust the uid/gid of the user in the container to match that id before running gosu to drop permissions and run as that user. I’ve included a lot of this logic in my base image example (note the fix-perms script that tweaks the uid/gid). Another example of that pattern is in my jenkins-docker image.

    You’ll still need to either configure root’s login shell to automatically run gosu inside the container, or remember to always pass -u www-data when you exec into your image, but now that uid/gid will match your host.

    This is primarily for development. In production, you probably don’t want host volumes, use named volumes instead, or at least hardcode the uid/gid of the user in the image to match the desired id on the production hosts. That means the Dockerfile would still have USER www-data but the docker-compose.yml for developers would have user: root that doesn’t exist in the compose file in production. You can find a bit more on this in my DockerCon 2019 talk (video here).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search