Testing Wordpress with different php versions with docker-compose and traefik

2023 update: if you are just looking for a basic and updated php-based Dockerfile for Wordpress, with its own Traefik-based docker-compose.yml, check out this post. If you are interested in testing older php versions, read on.


As of Autumn 2019, many Wordpress users will likely have found a warning suggesting they must update php on their server. As php 5.6 has reached end of life in 2018, this is well needed. Yet, many Wordpress installations that have been running for years may be using a plugin or a theme that is not compatible with php 7.3.

Being to some extent responsible for a number (undisclosed, but not negligible) of Worpress installations scattered across a number of servers, I needed a way to make sure that updates go smoothly, and be best-positioned to change back and forth between php versions to introduce all necessary fixes.

As some of these websites have been around for many years, not all of them are currently using a secure connection, so I take this also as a chance to make sure they will all be on https after the update.

Warning: not much of what you’ll find here follows best practices in terms of security or privacy. This works for me, and should work for some basic testing purposes, but… you have been warned.

Step 1: Set up a basic Docker image with php 5.6 and php 7.3

The first step is to prepare a basic php Docker image with apache installed. I also installed some basic libraries such as zip and pdo which are used by a number of plugins. You may need to install others, keeping in mind that you need to take care of installing dependencies in the Dockerfile yourself.

You can safely^[Safely, that is, keeping in mind you’ll be using a php version with known vulnerabilities] use the Docker images I created, giocomai/php_5_6_20. For more recent versions, you’ll find giocomai/php_7_3 and giocomai/php_7_4.

Custom Dockerfile

Here’s my full Dockerfile for php 5.6.20.

FROM php:5.6.20-apache

RUN apt-get update

RUN apt-get install -y libzip-dev zlib1g-dev php5-mysql

RUN docker-php-ext-install zip mysqli pdo pdo_mysql

RUN a2enmod rewrite

RUN service apache2 restart

Here’s my full Dockerfile for php 5.6.40.

FROM php:5.6.40-apache

RUN apt-get update

RUN apt-get install -y libzip-dev zlib1g-dev 

RUN docker-php-ext-install mysql zip mysqli pdo pdo_mysql

RUN a2enmod rewrite

RUN service apache2 restart


And here’s my full Dockerfile for php 7.3

FROM php:7.3-apache

RUN apt-get update

RUN apt-get install -y libzip-dev zlib1g-dev 

RUN docker-php-ext-install zip mysqli pdo pdo_mysql

RUN a2enmod rewrite

RUN service apache2 restart

Again, keep in mind that you’ll need to install dependencies in your Dockerfile, so installing php extensions may not necessarily be as straightforward as expected. As usual, error messages and search engines are your friends.

This is, for example, what you’d need to get the IMAP php extension in the above php 7.3 image.


RUN apt-get install -y libc-client-dev libkrb5-dev
RUN docker-php-ext-configure imap --with-kerberos --with-imap-ssl \
    && docker-php-ext-install imap

Finally [updated on 2020-04-30], here’s a Dockerfile for 7.4 that includes imagick and gd modules, and does not raise any flags with the latest Worpress site health check.

FROM php:7.4-apache

RUN apt-get update

RUN apt-get install -y libzip-dev zlib1g-dev 

#imagick
RUN apt-get -y install gcc make autoconf libc-dev pkg-config
RUN apt-get -y install libmagickwand-dev --no-install-recommends
RUN pecl install imagick
RUN docker-php-ext-enable imagick

RUN docker-php-ext-install zip mysqli pdo pdo_mysql gd exif

RUN rm -r /var/lib/apt/lists/*

RUN a2enmod rewrite

RUN service apache2 restart

[2022-10-14 - update with php 8]

FROM php:8.0-apache

RUN apt-get update

RUN apt-get install -y libzip-dev zlib1g-dev 

#imagick
RUN apt-get -y install gcc make autoconf libc-dev pkg-config
RUN apt-get -y install libmagickwand-dev --no-install-recommends
RUN pecl install imagick
RUN docker-php-ext-enable imagick

RUN docker-php-ext-install zip mysqli pdo pdo_mysql gd exif

RUN rm -r /var/lib/apt/lists/*

RUN a2enmod rewrite

RUN sed -i -e 's/Listen 80/Listen 80\nServerName localhost/' /etc/apache2/ports.conf

RUN service apache2 restart

EXPOSE 80

Step 2: Get your docker-compose up and running

Here’s a basic docker-compose.yml with Traefik that should get you up and running. Search for all instances of “example.com” and put your own domain. Make sure the relevant domain (or subdomain) points at your server. Also, make sure you set a random password for both database and wordpress host, and make sure they correspond (of course, e.g. the database name, user, and password you set up in the _db docker should be the same that Wordpress uses to access them). I also included a phpmyadmin Docker just in case, this can of course safely be removed.

version: "3.3"

services:

  traefik:
    image: "traefik:v2.1"
    container_name: "traefik"
    restart: always
    command:
      #- "--log.level=DEBUG"
      - "--api"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myhttpchallenge.acme.httpchallenge=true"
      - "--certificatesresolvers.myhttpchallenge.acme.httpchallenge.entrypoint=web"
      #- "--certificatesresolvers.myhttpchallenge.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
      - "--certificatesresolvers.myhttpchallenge.acme.email=youremail@example.com"
      - "--certificatesresolvers.myhttpchallenge.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    labels:
      - "traefik.enable=true"
      # global redirect to https
      - "traefik.http.routers.redirs.rule=hostregexp(`{host:.+}`)"
      - "traefik.http.routers.redirs.entrypoints=web"
      - "traefik.http.routers.redirs.middlewares=redirect-to-https"
      # middleware redirect
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
      # dashboard
      - "traefik.http.routers.traefik.rule=Host(`traefik.example.com`)"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.routers.traefik.tls.certresolver=myhttpchallenge"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.middlewares=authtraefik"


  example_com_db:
    image: mysql:5.7
    volumes:
      - example_com_db:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: Ez2oogha5ogh4woh
      MYSQL_DATABASE: ieseehahF0oo
      MYSQL_USER: ii4eshaeZ9ta
      MYSQL_PASSWORD: lae7gia4MohW

  example_com:
    depends_on:
      - example_com_db
    image: giocomai/php_7_4:latest
    volumes:
      - example_com:/var/www/html/
    ports:
      - "8000:80"
    restart: always
    environment:
      WORDPRESS_DB_HOST: example_com_db:3306
      WORDPRESS_DB_NAME: ieseehahF0oo
      WORDPRESS_DB_USER: ii4eshaeZ9ta
      WORDPRESS_DB_PASSWORD: lae7gia4MohW
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.example_com.rule=Host(`test.example.com`)"
      - "traefik.http.routers.example_com.entrypoints=websecure"
      - "traefik.http.routers.example_com.tls.certresolver=myhttpchallenge"

  phpmyadmin:
    depends_on:
      - example_com_db
    image: phpmyadmin/phpmyadmin:latest
    restart: always
    links:
      - example_com_db:db
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.phpmyadmin.rule=Host(`phpmyadmin.example.com`)"
      - "traefik.http.routers.phpmyadmin.entrypoints=websecure"
      - "traefik.http.routers.phpmyadmin.tls.certresolver=myhttpchallenge"

volumes:
  example_com:
  example_com_db:
  

Step 3: Move your website with Wordpress Duplicator plugin

The installation will work fine, but as you’ll move on to finalise your installation going to the admin part of your wordpress installation, it just won’t work.

This is due the fact that we’re using a reverse proxy (i.e. Traefik), so as described in the Wordpress documentation we need to add the following chunk at the beginning of our wp-config.php file. 1

define('FORCE_SSL_ADMIN', true);
// in some setups HTTP_X_FORWARDED_PROTO might contain 
// a comma-separated list e.g. http,https
// so check for https existence
if (strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false)
$_SERVER['HTTPS']='on';

  1. I can’t stress enough the at the beginning part, since this is not mentioned in the official Wordpress documentation, and you’ll get only silly error messages if you paste this code at the bottom of the wp-config.php file. ↩︎

Avatar
Giorgio Comai
Researcher, data analyst