Nick Romney


Nick Romney


Containerising PHP

Nick Romney's photo
Nick Romney
·Aug 28, 2020·

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 apt-get will work.

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:

  • php:7.4-apache
  • php:7.4-apache-buster
  • php:7.4.9-apache
  • php:7.4.9-apache-buster

I'll choose php:7.4.9-apache-buster

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

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
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 Set the 'ServerName' directive globally to suppress this message
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 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' - - [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] AH01276: Cannot serve directory /var/www/html/: No matching DirectoryIndex (index.php,index.html) found, and server-generated directory index forbidden by Options directive - - [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

The --rm flag means that when the container stopped, it was deleted.

Now we want to create our first Dockerfile

Create a directory - mkdir docker-php Initialise git - git init Make a file Dockerfile

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.




Work with Docker


Existing images

Work with full Ubuntu

Docker Compose

docker-compose up -d --force-recreate --no-deps --build nginx

Options description

| -d | Detached mode: Run containers in the background, print new container names. Incompatible with --abort-on-container-exit | | --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 - \
      ",http.realip&license=personal&telemetry=off" \
    | tar --no-same-owner -C /usr/bin/ -xz caddy

Paul Redmond's book on LeanPub:

From Bret Fisher

From Nick Janetakis

  • log to stdout

Performance, S6:

Juan Treminio:

Other interesting

Share this