Docker is supposed to be a cross-platform tool. It runs on MacOS, Windows and Linux. You can use the containers everywhere, so it should be cross-platform. No. In practice, Docker is not that easy when you use it cross-platform. In this blog, I will explain our experience of how to make your docker-compose cross-platform compatible on MacOS, Windows and Linux.
The example files for making your docker-compose.yml cross-platform compatible are hosted on Github.
Docker-compose on MacOS
I develop using Docker containers on MacOS. While it certainly has its challenges (e.g. performance issues when sharing volumes), it does work. There are no permissions errors nor modifications required to a regular docker file or the docker-compose file. Good to know: Docker uses Hyperkit to create a virtual machine on your Mac.
Docker-compose on Windows
When you use the Docker Desktop version, it is most likely that you won’t run into issues. However, one of our customers used Windows 7 (which is EOL…) and used Docker toolbox. Because it was Windows 7, for some reason Docker Desktop did not install. The reason why I mention this, is because Toolbox does not have proper support for sharing volumes. Therefore, updating to Docker Desktop is recommended to prevent weird issues like not seeing shared volumes.
Docker-compose on Linux
So far, all is good with MacOS and Windows (if you use the latest version). In both MacOS and Windows, Docker runs as a virtual machine with Linux. Permission issues are resolved automatically. And here, the problem comes. File system permissions are not automatically resolved. This is by design as explained by thaJeztah.
To overcome deployment issues, thaJeztah explains that you would need to set the correct permissions every time on Linux. This is far from ideal as with every git update you have permission issues and need to fix them again. This solution is not the best solution.
Let’s assume that we have the user ubuntu with the id 1001 (which you can find using the command id). István Döbrentei’s solution is to fix the user id’s by adding the following line, where 1001 is the id of the user that owns the file on the host:
RUN usermod -u 1001 www-data
How to make the docker-compose file cross-platform
In this explanation I will use an example where we will setup a PHP + Apache + MySQL setup with Docker. For this, we need the following images:
- mysql:5.7.22
- php:7.4-apache
MySQL – determine the user
This image does not have the ps command, which could make things a bit tricky. In this case, I made the guess that the user is mysql. You could install procps using apt, but in this case my guess was correct.
PHP – determine the user
Usually, webservers run as www-data. To be sure, we can run the image and check what services are running.
docker run -d php:7.4-apache
bbab0180d4f2d010ec25247bf0e4eec9f12c8c80fdbbf4be06b3a73bfd81a42b
A string is returned. Use this to run bash in Docker:
# docker exec -it bbab0180d4f2d010ec25247bf0e4eec9f12c8c80fdbbf4be06b3a73bfd81a42b ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.1 0.2 83304 24336 ? Ss 10:28 0:00 apache2 -DFOREG
www-data 16 0.0 0.0 83328 6864 ? S 10:28 0:00 apache2 -DFOREG
www-data 17 0.0 0.0 83328 6864 ? S 10:28 0:00 apache2 -DFOREG
www-data 18 0.0 0.0 83328 6864 ? S 10:28 0:00 apache2 -DFOREG
www-data 19 0.0 0.0 83328 6864 ? S 10:28 0:00 apache2 -DFOREG
www-data 20 0.0 0.0 83328 6864 ? S 10:28 0:00 apache2 -DFOREG
root 27 0.0 0.0 7640 2672 pts/0 Rs+ 10:29 0:00 ps aux
# docker rm bbab0180d4f2d010ec25247bf0e4eec9f12c8c80fdbbf4be06b3a73bfd81a42b
We have now identified that the user is www-data. You can use this trick for virtually any docker image.
Create the cross-platform Dockerfile
First, we will create the docker files. Before we do that, create the following folder:
- docker
- docker/mysql
- docker/php
- mysql_data
- public
The docker files simply use the original images, but have two additions. First, we defined the user_id as 1000. We use the argument (ARG) method for that, since this allows us to change the user_id using the docker-compose file.
Second, we run usermod. This will change the id of the user to $user_id, as defined in the argument.
docker/mysql/Dockerfile
FROM mysql:5.7.22
MAINTAINER Daniel Koop
ARG user_id=1000
RUN usermod -u $user_id mysql
docker/php/Dockerfile
FROM php:7.4-apache
MAINTAINER Daniel Koop
ARG user_id=1000
RUN usermod -u $user_id www-data
The docker-compose file
The docker-compose file looks like a regular compose file. It refers to the dockerfiles, forwards the ports, sets the volumes and dependencies. In this example, I assume that you are familiar with how docker-compose works. If you do not, check out vsupalov’s blog about docker-compose files.
As mentioned with the dockerfile, we have configured the files to listen to arguments. The only difference is that we have changed the build setting. To pass an argument, we need to change the reference to the dockerfile:
build: ./docker/php
build:
context: ./docker/php
args:
user_id: ${USER_ID}
We will do this for every container we want to run:
docker-compose.yml
version: '3'
services:
web:
build:
context: ./docker/php
args:
user_id: 1000
image: wedevelopcoffee/php-74-apache
container_name: php
ports:
- "80:80"
- "443:443"
depends_on:
- mysql
volumes:
- "./public:/var/www/html"
mysql:
build:
context: ./docker/mysql
args:
user_id: ${USER_ID}
image: mrkoopie/mysql
container_name: mysql
ports:
- "3306:3306"
environment:
- MYSQL_DATABASE=testdb
- MYSQL_ROOT_PASSWORD=root
- MYSQL_USER=user
- MYSQL_PASSWORD=pass
volumes:
- "./mysql_data:/var/lib/mysql"
How to run cross-platform docker-composer
Running the containers is easy and does not work any different than normal:
docker-compose up
If you already have build the images before without the change, make sure to add the —build flag. You will only need to do this when you change the Dockerfile or the user_id.
docker-compose up --build
Where to host your Docker container?
To host your Docker container publicly, pick up one of our virtual servers. For just € 3.95 per month, you can use your Docker file in production or develop on stable servers. Learn more about virtual servers for Docker.
Conclusion
Congratulations! You have made your docker environment cross-platform proof. You can further extend the flexibility by using a .env file. There is a great article by Vsupalov.com explaining how you can use a .env file with Docker.