Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Docker volumes - www-data Debian/Ubuntu + Alpine

Docker volumes - www-data Debian/Ubuntu + Alpine

Sharing host OS www-data directories as a volume is tricky for Alpine images. User ID (UID) and Group ID (GID) are different.

UID/GID Ubuntu Alpine
33 www-data xfs
82 - www-data

tl;dr run chown 82:82 app.log on Host OS.


Example - sharing sessions/logs directory

  • php-fpm worker runs as www-data
  • volume mounts a log app.log

Host OS (Ubuntu/Debian)

$ echo HOST > app.log
$ cat app.log
HOST
$ sudo chown www-data:www-data app.log
$ ls -l app.log
-rw-rw-r-- 1 www-data www-data 0 čen 22 09:21 app.log
$ ls -n app.log
-rw-rw-r-- 1 33 33 0 čen 22 09:21 app.log

Debian/Ubuntu docker images

  • immediately works because www-data has same UID/GID on Host and Docker
docker run --rm -it \
    --volume "$(realpath app.log):/var/www/html/app.log" \
    --user www-data \
    php:7.0-fpm sh
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ ls -l app.log
-rw-rw-r-- 1 www-data www-data 4 Jun 22 07:26 app.log
$ ls -n app.log
-rw-rw-r-- 1 33 33 4 Jun 22 07:26 app.log
$ echo DEBIAN >> app.log
$ cat app.log
HOST
DEBIAN

Alpine docker images

  • Permission denied - www-data (UID 82) is trying to access xsf (UID 33) file
docker run --rm -it \
    --volume "$(realpath app.log):/var/www/html/app.log" \
    --user www-data \
    php:7.0-fpm-alpine sh
$ id
uid=82(www-data) gid=82(www-data) groups=82(www-data)
$ ls -l app.log
-rw-rw-r--    1 xfs      xfs              5 Jun 22 08:24 app.log
$ ls -n app.log
-rw-rw-r--    1 33       33               5 Jun 22 08:24 app.log
$ echo ALPINE >> app.log
sh: can't create app.log: Permission denied
$ cat /etc/passwd | grep www-data
www-data:x:82:82:Linux User,,,:/home/www-data:/bin/false
$ cat /etc/group | grep www-data
www-data:x:82:www-data

A) Runtime hotfix - chown

  • use chown in Docker after:
    • a container has started
    • file permissions have changed on Host OS
docker run --rm -it \
    --volume "$(realpath app.log):/var/www/html/app.log" \
    --user root \
    php:7.0-fpm-alpine sh
$# id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
$# chown www-data:www-data app.log
$# ls -l app.log
-rw-rw-r--    1 www-data www-data         5 Jun 22 08:24 app.log
$# ls -n app.log
-rw-rw-r--    1 82       82               5 Jun 22 08:24 app.log
$# echo ALPINE >> app.log
$# cat app.log
HOST
ALPINE
$# exit

# Host OS now has UID/GID 82 too
$ ls -l app.log
-rw-rw-r-- 1 82 82 12 čen 22 10:27 app.log
$ ls -n app.log
-rw-rw-r-- 1 82 82 12 čen 22 10:27 app.log

Fix a running container

  • USER - works if a container is running as a root
docker exec -u root -t container chown www-data:www-data app.log

B) Custom docker image - addgroup

  • need to ensure that www-data (82) has access to whatever UID/GID from Host OS
  • the simplest solution is adding www-data to xsf group in a custom docker image
    addgroup www-data xfs

1) Build the image

cat > Dockerfile << EOF
FROM php:7.0-fpm-alpine
RUN addgroup www-data xfs
EOF
docker build --file Dockerfile --tag php-fpm-wwwdata:test ./

2) Use the image

docker run --rm -it \
    --volume "$(realpath app.log):/var/www/html/app.log" \
    --user www-data \
    php-fpm-wwwdata:test sh
$ id
uid=82(www-data) gid=82(www-data) groups=33(xfs),82(www-data)
$ ls -l app.log
-rw-rw-r--    1 xfs      xfs             12 Jun 22 08:27 app.log
$ ls -n app.log
-rw-rw-r--    1 33       33              12 Jun 22 08:27 app.log
$ echo ALPINE >> app.log
$ cat app.log 

3) Does it work on production?

Unfortunately no. Linux console works well, but the php-fpm work doesn't work! I came across many issues:

It's a solvable. You can listen.group = xfs. You can handle file permissions by chmod -R g+rw and chown -R 82:www-data. It's a lot of troubles for keeping a mapping between Ubuntu and Alpine OS. It's very complex and error-prone.

Solution for all OS!

Linux file can be owned by a non-existent user! Don't try to map user names (www-data), map the user ids. Set the UID from Docker image on Host OS. No custom docker image. No addgroup. chown 82:82. KISS.

sudo chown 82:82 app.log
docker run --rm -it \
    --volume "$(realpath app.log):/var/www/html/app.log" \
    --user www-data \
    php:7.0-fpm-alpine sh
$ id
uid=82(www-data) gid=82(www-data) groups=82(www-data)
$ ls -l app.log
-rw-rw-r--    1 www-data www-data        19 Jun 22 08:30 app.log
$ ls -n app.log
-rw-rw-r--    1 82       82              19 Jun 22 08:30 app.log
$ echo ALPINE >> app.log
$ cat app.log
HOST
ALPINE

It works for us. No extra config inside Docker. Root user on Host OS can backup shared folders. We don't mind "hacked" UID/GID on Host OS. So we've replaced www-data by the corresponding UID.

# old code before introducing Alpine images
chown -R www-data:www-data $SOME_PATH

# new code
if [ -z "$HAS_ALPINE_OS" ]; then
    chown -R 33:33 $SOME_PATH
else
    chown -R 82:82 $SOME_PATH
fi

It seems so obvious in hindsight...


Links

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.