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