,

Docker Container to Develop a WordPress Plugin

I asked Gemini to write a docker-compose file to create a wordpress development environment with mariadb. Note that this is using old software, like MariaDB 10.6. This is pretty typical of LLMs.

version: '3.8'

services:
  wordpress:
    image: wordpress:latest
    restart: always
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: mariadb
      WORDPRESS_DB_USER: ${MYSQL_USER}
      WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
      WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
    volumes:
      - wordpress_data:/var/www/html
      - ./plugin:/var/www/html/wp-content/plugins/plugin # Bind mount for plugins
    depends_on:
      - mariadb
    networks:
      - mynetwork

  mariadb:
    image: mariadb:10.6
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - mariadb_data:/var/lib/mysql
    networks:
      - mynetwork

volumes:
  wordpress_data:
  mariadb_data:

networks:
  mynetwork:

(The prompt was more complicated, and I did a back and forth with it.)

Here’s the .env file:

MYSQL_ROOT_PASSWORD=wordpress
MYSQL_DATABASE=wordpress
MYSQL_USER=wordpress
MYSQL_PASSWORD=wordpress

In the same directory, do:

mkdir plugin

Then, in the plugin directory, use this file, plugin.php:

<?php
/*
Plugin Name: Minimalist Plugin
Description: A very basic plugin example.
Version: 1.0
Author: Your Name
*/

function minimalist_plugin_message() {
    echo '<p>This message is from the Minimalist Plugin!</p>';
}

add_action('wp_footer', 'minimalist_plugin_message');
?>

This setup requires you to call your plugin “plugin”. If you want to name it something else, you can alter the docker-compose.yml config file.

If you want to develop a more complex plugin that, for example, relies on other plugins, alter the docker-compose.yml file and put the entire plugin directory into your work directory.

Linux Configuration

On the Linux side, I’m using MINT 22. I just used the default docker installation, which is out of date, but worked:

johnk@mini ~ % sudo apt search docker | grep ^i
i docker-compose - define and run multi-container Docker appl
i A docker.io - Linux container runtime
i A python3-docker - Python 3 wrapper to access docker.io's con
i A python3-dockerpty - Pseudo-tty handler for docker Python clien

johnk@mini ~ % docker version
Client:
Version: 26.1.3
API version: 1.45
Go version: go1.22.2
Git commit: 26.1.3-0ubuntu1~24.04.1
Built: Mon Oct 14 14:29:26 2024
OS/Arch: linux/amd64
Context: default

Server:
Engine:
Version: 26.1.3
API version: 1.45 (minimum version 1.24)
Go version: go1.22.2
Git commit: 26.1.3-0ubuntu1~24.04.1
Built: Mon Oct 14 14:29:26 2024
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.7.24
GitCommit:
runc:
Version: 1.1.12-0ubuntu3.1
GitCommit:
docker-init:
Version: 0.19.0
GitCommit:
johnk@mini ~ % docker-compose version
docker-compose version 1.29.2, build unknown
docker-py version: 5.0.3
CPython version: 3.12.3
OpenSSL version: OpenSSL 3.0.13 30 Jan 2024
johnk@mini ~ %

I’m still having some trouble with having a reasonably fast system boot, and running Docker. I tried this:

# start a sudo shell, and then run commands
sudo -s
systemctl disable docker
systemctl enable docker.socket
systemctl start docker.socket

Starting and Stopping the Container to Work

When you start working, fire up a terminal and do this:

cd <to the work directory>
docker-compose up

You’ll see the log messages (good). You should be able to see your server at localhost:8080

To shut down:

# press CONTROL-C. This initiates the shutdown process.
# It doesn't remove the containers, though.
docker-compose down

You’ll see docker-compose remove the two containers for WordPress and MariaDB.

Note About Containers: they contain state

A container is like a little “computer” with an image (aka files), processes, and CPU. When you press control-C, you just tell the process to exit. You can still tell these containers to start by running the “docker-compose start” command.

That’s why you need to run the “docker-compose down” command, to destroy the containers.

You might be wondering about the database state. That isn’t kept in the container. Instead, Docker has a feature called named volumes, under the volumes key, which are containers that aren’t destroyed. To see the named volumes:

docker volume ls
DRIVER    VOLUME NAME
local wp-plugin-dev_mariadb_data
local wp-plugin-dev_wordpress_data

Backups

Backup the Work Volumes

Use vackup, a script that produces compressed tarballs from volumes. I did this, and it worked well:

vackup export wp-plugin-dev_mariadb_data m.tgz

Also consider using Docker Desktop’s extension, if you are using Docker Desktop.

This saves out your file uploads, settings, and other difficult-to-restore things in your environment.

Be warned that this is all state. You shouldn’t be creating a lot of state in the containers. It’s better to create scripts to create the state (setup scripts, upload tools that use WP-CLI, etc.) or the setup should be included in the plugin.

Backup the Application Data with an Export

It’s generally best to perform backups of the application data through the WordPress Import/Export tool. The main advantages are: it tests that export and import work; it’s database neutral.

The WordPress Import tool supports custom post types. So if you implement your “database” as custom posts (rather than traditional database tables) you get export and import without writing code. If you use a tool like Advanced Custom Fields or another plugin GUI to create CPTs, you can design this part of your plugin interactively.

(To really exercise the code, you will need to make docker-compose configs that also use different versions of the databases, different versions of PHP, WordPress, etc.)

Wiping Away the State

Every once in a while, you need to wipe out all the state in your dev environment (but not your code, of course), and start fresh. To do this:

  • Stop and delete all your project containers.
  • Delete all the volumes used by your containers.
  • Delete the images used to build the containers.

ToDo

  • Set up Git
  • Set up a remote Git repo on GitHub
  • Set up a remote Git repo over SSH
  • Create releases and push to live