7 min read
Tips on containerising Moodle on a LAMP stack, with Docker multistage builds
We start by following this Semaphore CI guide folding in:
- Bret Fisher
- Nick Janetakis
- Phil Redmond
- PHP FPM bloke
Which container base image?
Many guides suggest Alpine in all circumstances
Bret Fisher's Docker Con talk from 2019 covered why he didn't use Alpine for Node.
I find that most instruction guides for Moodle assume Ubuntu or Debian, so that e.g. commands like
In this guide, we'll go for:
- an "official" PHP image
- based off Debian
- with Apache baked in
Moodle requirements for 3.9 - PHP 7.2 is required. PHP 7.3 and 7.4 are supported. I'll therefore choose 7.4
Go to the Docker Hub page for official PHP images
- browse the available tags
- the newest ones are all "rc", suggesting that the Release Candidate for PHP 8.0 is out, so we'll look for the previous release, by filtering.
By looking at the digests, we see that these three tags are all the same underlying image:
Note it looks like for PHP 7.3, there was the option of
stretch images (Debian 9) or
buster(Debian 10), but as of PHP 7.4, this has moved to
If we click through to the underlying image page we see the 32 commands which comprise the image history, ending in:
WORKDIR /var/www/html EXPOSE 80 CMD ["apache2-foreground"]
What's in our container?
Run this command to:
- pull down the image (if you don't have it already)
- create a container which will be removed when you exit
- map port 8000 on your local machine to port 70 on the container
- give you an interactive terminal
docker run -it -p 8000:80 --rm php:7.4.9-apache-buster
In your browser, go to http://localhost:8000 and you should see
You don't have permission to access this resource.
Apache/2.4.38 (Debian) Server at localhost Port 8000
And on the command line, you'll see
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message [Fri Aug 28 20:40:58.698283 2020] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.38 (Debian) PHP/7.4.9 configured -- resuming normal operations [Fri Aug 28 20:40:58.698362 2020] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND' 172.17.0.1 - - [28/Aug/2020:20:41:10 +0000] "\x16\x03\x01\x02" 400 0 "-" "-" [Fri Aug 28 20:41:14.282539 2020] [autoindex:error] [pid 19] [client 172.17.0.1:58284] AH01276: Cannot serve directory /var/www/html/: No matching DirectoryIndex (index.php,index.html) found, and server-generated directory index forbidden by Options directive 172.17.0.1 - - [28/Aug/2020:20:41:14 +0000] "GET / HTTP/1.1" 403 493 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0"
Exit the command line with Ctrl + C, and you should see
^C[Fri Aug 28 20:41:27.288194 2020] [mpm_prefork:notice] [pid 1] AH00169: caught SIGTERM, shutting down
--rm flag means that when the container stopped, it was deleted.
Now we want to create our first Dockerfile
Create a directory -
Initialise git -
Make a file
Look at Moodle's PHP dependencies
- Semaphore CI's guide on Dockerfile.development, for the correct local permissions.
- Nick Janetakis on a single Dockerfile, adding bits in.
install-php-extensions supported extensions.
- github.com/wodby/mariadb has all the environment variables
- WordPress, Nginx FPM: gist.github.com/md5/d9206eacb5a0ff5d6be0
- FPM healthcheck: github.com/twinscom/dockerfiles/blob/master..
Work with Docker
- Entrypoint sh or bash: stackoverflow.com/questions/51508150/standa..
Work with full Ubuntu
docker-compose up -d --force-recreate --no-deps --build nginx
| -d | Detached mode: Run containers in the background, print new container names. Incompatible with
| --force-recreate | Recreate containers even if their configuration and image haven't changed |
| --build | Build images before starting containers. |
| --no-deps | Don't start linked services. |
- lots of good style here, particularly the curl command:
RUN curl --silent --show-error --fail --location \ --header "Accept: application/tar+gzip, application/x-gzip, application/octet-stream" -o - \ "https://caddyserver.com/download/linux/amd64?plugins=http.expires,http.realip&license=personal&telemetry=off" \ | tar --no-same-owner -C /usr/bin/ -xz caddy
Paul Redmond's book on LeanPub: leanpub.com/docker-for-php-developers
From Bret Fisher
- use Docker Compose v2 if a single developer (rather than in Swarm / Kubernetes)
- Compose v2: docs.docker.com/compose/compose-file/compos..
From Nick Janetakis
- log to stdout
Performance, S6: uvd.co.uk/blog/migrating-php-docker-perform..
- completely build your own: github.com/wemakewaves/php-docker-bench/blo..
Juan Treminio: jtreminio.com/blog/all-in-one-php-fpm-nginx..
- all in one containers, plus Traefik.
interesting fork: jtreminio.com/blog/all-in-one-php-fpm-nginx..
- DAMP - Docker, Apache, MariaDB & PHP-FPM
- github.com/aamnah/bash-scripts/blob/master/.. -- functions for installing Apache on Debian for WordPress