How to make docker-compose cross-platform (Mac, Windows, Linux)

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
				
			
becomes
				
					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.

Leave a Reply

Your email address will not be published. Required fields are marked *