Recently I've been integrating Docker into the development and deployment workflows at work.

It's mostly been a massive success, more than halving deployment times and simplifying the build and deployment process dramatically. We've gone from over 15 configuration files for the infrastructure of a project to 3. Yep, just 3. All pretty simple as well....but thats for another post!

There have also been a few issues and teething problems along the way, which I'm happy to say have all been resolved...for now!

One of the biggest issues we faced was getting the Docker development environment setup to work across operating systems without any configuration needed (apart from installing docker) on the developers side.

Linux Permissions

As I develop on a Mac the setup and configuration was rather simple. Docker provide OSXFS which makes filesystem related setup trivial (volumes, mounts, and such).

Just take a look at the "Ownership" section of the OSXFS documentation. This automatically managed the permission mapping between your local machine and the container. No permission magic required on the container side for volumes, it just thinks the volume is part of the container and owned by a normal user.

On Linux this is different. When you mount a volume on Linux, the UID and GID are exposed through to the container. A volume inside the container may look as if its owned by 1000:1000 instaed of the intended user (usually www-data). This is obviously an issue when a service tries to write to this folder (or even read from it), services such as nginx or php can have problems here.

After some research and looking into the issue, it seems the simplest workaround is to detect the uid of the uer you want the folder to belong to, check if it does or not, and then set the uid/guid of that user to your local Linux user.

For example, if the user you want to be using within the container is www-data and the group is www-data, you would detect your UID and GID (for example, UID: 1000 GID: 1000) and set the UID and GID of www-data to this. So on the container, www-data now has the UID and GID of 1000.

This can be done pretty easily on the container in the entrypoint script in shell.

# Calculate user/group ids and set if required. (Mostly for linux)
WWW_DATA_DEFAULT=$(id -u www-data)

# If the permissions of the application root directory do not contain the www-data uid...
if [[ -z "$(ls -n '/var/app' | awk '{print $3}' | grep $WWW_DATA_DEFAULT)" ]]; then

  # Get the UID and GID from the folder or existing environment variables.
  WWW_DATA_UID=${WWW_DATA_UID:-$(ls -ldn /var/app | awk '{print $3}')}
  WWW_DATA_GID=${WWW_DATA_GID:-$(ls -ldn /var/app | awk '{print $4}')}

  export WWW_DATA_UID
  export WWW_DATA_GID

  # If the new uid is not 0 and is not the uid of www-data, set the uid and gid of www-data to the new values.
  if [ "$WWW_DATA_UID" != "0" ] && [ "$WWW_DATA_UID" != "$(id -u www-data)" ]; then
    echo "Changing www-data UID and GID to ${WWW_DATA_UID} and ${WWW_DATA_GID}."
    usermod -u $WWW_DATA_UID www-data
    groupmod -g $WWW_DATA_GID www-data
    chown -R www-data:www-data /var/app
    echo "Changed www-data UID and GID to ${WWW_DATA_UID} and ${WWW_DATA_GID}."
  fi
fi  

In the above script we do the following steps:

  1. Get the current/default uid of www-data.
  2. Check if the uid of the app directory (/var/app) do not match the default www-data uid.
  3. Get the new uid and gid from the directory (or already set environment variables).
  4. If the new uid does not equal 0 (root), we change the uid and gid of the www-data user to the new uid and gid we detected in step 3.
  5. Run chown on the directory to ensure all of the folder is owned by the detected user and group.

This script is pretty straight forward and resolved our issues with Linux and file permissions for Docker containers pretty simply. The best part is that it doesn't use any chmod or chown to modify the files to a user local to the container (thus causing the files to be inaccessible by Linux and your dev machine).

Hopefully this is helpful to some people as I know I wish I found this solution sooner!

© 2018. All Rights Reserved.