How to develop a WordPress theme inside a container

Hi folks, today we will learn about developing a WordPress theme inside a container.

We will use Docker compose because infrastructure as code is the way to go nowadays.

If you don't know yet Docker compose, please go to https://docs.docker.com/compose/ and come back to this tutorial.

If you know already everything and just want to clone a working template, here is the GitHub repo : https://github.com/Atem18/docker-wordpress

For reference, here is the Docker Compose we will use:

docker-compose.yml

version: '3'
services:
  db:
    container_name: wordpress-db
    image: mariadb:10.5.8
    restart: unless-stopped
    volumes:
      - ./dev/db/data:/var/lib/mysql
      - ./dev/db/entrypoint:/docker-entrypoint-initdb.d
    ports:
      - "3306:3306"
    networks:
      - mytheme
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress
  wordpress:
    depends_on:
      - db
    container_name: wordpress-fpm
    build:
      context: ./wordpress
    restart: unless-stopped
    volumes:
      - ./wordpress/mytheme.ini:/usr/local/etc/php/conf.d/mytheme.ini
      - ./dev/wordpress:/var/www/html
      - ./mytheme:/var/www/html/wp-content/themes/mytheme
    networks:
      - mytheme
    environment:
      WORDPRESS_DB_HOST: wordpress-db
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
  node:
    container_name: wordpress-node
    build:
      context: ./node
    restart: unless-stopped
    volumes:
      - ./mytheme:/app
  caddy:
    container_name: wordpress-caddy
    build:
      context: ./caddy
    restart: unless-stopped
    environment:
      DOMAIN: www.mydomain.com
    volumes:
      - ./caddy/certs:/etc/caddy/certs
      - ./dev/wordpress:/var/www/html
      - ./mytheme:/var/www/html/wp-content/themes/mytheme
    ports:
      - "80:80"
      - "443:443"
    networks:
      - mytheme
networks:
  mytheme:

As you can see upper in the compose file, we are using MariaDB, WordPress, Node.js and Caddy. It's just as exemple, feel free to swap any component.

MariaDB

Nothing fancy here, we just use the official MariaDB image.

What only changes is that we mount data into a local folder so it's not lost when we restart the container.

And we also mount a local folder so we can easily restore a dump from production.

WordPress

Here comes the first interesting part.

Dockerfile

FROM wordpress:php7.4-fpm
COPY mytheme.ini /usr/local/etc/php/conf.d/mytheme.ini
EXPOSE 9000

We declare a custom Dockerfile for Wordpress because we want to be able to declare our own PHP configuration file.

mytheme.ini

file_uploads = On
memory_limit = 64M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 600
expose_php = off

As you can see, we are tuning some variables. Please refer to the documentation to adjust the values or add values according to your needs.

Node.js

This part is only used in development and because I develop my own Wordpress themes, so feel free to skip it if you don't need it.

Dockerfile

FROM node:lts-alpine as develop-stage
WORKDIR /app
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]

docker-entrypoint.sh

#!/bin/sh
set -e

npm install

npm run watch

The whole point here is to install the node dependencies and watch using the tool of your choice (webpack, vite, etc.). Of course, don't forget to mount the folder /app on your system.

Caddy

Nothing complex as well here, we want to build a Dockerfile with a custom Caddyfile which you can find below. The only thing we are doing is to generate a fake SSL certificate using mkcert. If you don't have it, install it for your OS : https://github.com/FiloSottile/mkcert

mkcert "*.local.mydomain.com"
FROM caddy:2.4.5-alpine
ADD ./certs /etc/caddy/certs
ADD ./Caddyfile /etc/caddy/Caddyfile
{$DOMAIN} {
  root * /var/www/html
  php_fastcgi wordpress:9000
  file_server
  encode gzip zstd
  tls /etc/caddy/certs/_wildcard.local.mydomain.com.pem /etc/caddy/certs/_wildcard.local.mydomain.com-key.pem
}

Also don't forget to add the following in your DNS

127.0.0.1 www.local.mydomain.com

That's all folks!