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:
- Get the current/default uid of
www-data
. - Check if the uid of the app directory (
/var/app
) do not match the defaultwww-data
uid. - Get the new uid and gid from the directory (or already set environment variables).
- 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. - 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!