WordPress starter template
If you always wondered how to configure a hassle-free WordPress development environment for developing plugins or themes, this might be a solution for you.
Introduction
Recently, I started getting interested in WordPress development. After reading the documentation, and trying the
recommended path to set up a development environment with wp-env
I came to the conclusion that wp-env
is not very
flexible and is hiding too many details behind the scene. So I decided to build my own Docker container stack. Let me
show you my solution incrementally step by step.
Variables
First create a .env
file with all values as variables which might be useful being able to change while developing. The
.env
will be automatically used by docker compose
to populate variables inside the compose.yaml
.
DB_PASSWORD=password
DB_NAME=wordpress
DB_PREFIX=wp_
WP_USER=admin
WP_PASSWORD=admin
WP_PORT=3000
Database
At the beginning, you need a database for WordPress and the password variable from the .env
file to set up the
database. And instead of adding a container like PhpMyAdmin for editing the database, I thought it was a more flexible
solution to just expose the database port. For example, like this, you can use the database tools baked into PhpStorm.
services:
db:
image: mariadb
ports:
- '3306:3306'
environment:
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
CLI
In the next step, you need to add the WordPress CLI tool, which will be later used to download WordPress and generate some mock data. To make everything work properly, you need to add a health check to the database. This will make sure that the CLI tool will not run before the database server is fully initialized. You also have to add a volume where to store the WordPress installation, which will be downloaded by the CLI.
+ volumes:
+ data:
+ external: false
services:
db:
image: mariadb
+ container_name: wordpress_db
ports:
- '3306:3306'
environment:
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
+ healthcheck:
+ test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
+ start_period: 10s
+ interval: 10s
+ timeout: 5s
+ retries: 3
+ cli:
+ image: wordpress:cli
+ container_name: wordpress_cli
+ volumes:
+ - data:/var/www/html
+ depends_on:
+ db:
+ condition: service_healthy
Initializing
The next step on the list is creating a init.sh
script, which contains all the CLI commands. This example script will
download WordPress, create the database, create an admin user and generate some mock posts. This script can be modified
depending on the needs of the project. The critical point is the --dbhost
which has to be the container name of the
database container because Docker is using the container names to make the containers inside the docker network
reachable by this name.
#!/usr/bin/env bash
wp core download
wp config create \
--dbprefix="$DB_PREFIX" \
--dbname="$DB_NAME" \
--dbuser="root" \
--dbpass="$DB_PASSWORD" \
--dbhost=wordpress_db
wp db create
wp core install \
--url="http://localhost:$WP_PORT" \
--title="Example" \
--admin_user="$WP_USER" \
--admin_password="$WP_PASSWORD" \
--admin_email="email@example.com"
wp post generate --count=50
Now you need to hand over the variables from the .env
file as environment variables. If you wonder how this works:
DB_PASSWORD
is a shorthand for: DB_PASSWORD=${DB_PASSWORD}
. Next, you need to mount the script into the container
and add the entrypoint
to execute the script on container start.
volumes:
data:
external: false
services:
db:
image: mariadb
container_name: wordpress_db
ports:
- '3306:3306'
environment:
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
start_period: 10s
interval: 10s
timeout: 5s
retries: 3
cli:
image: wordpress:cli
container_name: wordpress_cli
volumes:
+ - ./init.sh:/init.sh
- data:/var/www/html
+ environment:
+ - DB_PASSWORD
+ - DB_NAME
+ - DB_PREFIX
+ - WP_USER
+ - WP_PASSWORD
+ entrypoint: ["sh", "-c", "/init.sh"]
depends_on:
db:
condition: service_healthy
Web server
Finally, you need to add a server to run WordPress. I decided to use the official WordPress docker image. Like this, you
don’t have to worry about any web server setup. You also need to mount the volume which is already prepared by the CLI
tool. The second mount point is for mounting the actual source code which you want to develop. You can mount any file or
directory to any point into the WordPress folder structure. This makes this setup super flexible. In the end, you only
have to add the same health check for the database like the CLI tool is doing. And we have to make sure that the web
server is only starting after the CLI tool has finished the initialization. This can be done by a simple
service_completed_successfully
check.
volumes:
data:
external: false
services:
+ app:
+ image: wordpress
+ container_name: wordpress_app
+ volumes:
+ - data:/var/www/html
+ - ./src:/var/www/html/wp-content
+ depends_on:
+ db:
+ condition: service_healthy
+ cli:
+ condition: service_completed_successfully
+ ports:
+ - "${WP_PORT}:80"
db:
image: mariadb
container_name: wordpress_db
ports:
- '3306:3306'
environment:
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
start_period: 10s
interval: 10s
timeout: 5s
retries: 3
cli:
image: wordpress:cli
container_name: wordpress_cli
volumes:
- ./init.sh:/init.sh
- data:/var/www/html
environment:
- DB_PASSWORD
- DB_NAME
- DB_PREFIX
- WP_USER
- WP_PASSWORD
entrypoint: ["sh", "-c", "/init.sh"]
depends_on:
db:
condition: service_healthy
Conclusion
That’s it! Now you have a development environment which will always create a clean installation of WordPress and also being able to modify the initialization to the project’s needs just with a single dependency, Docker.
For starting the environment, just run the following command.
docker compose up
And to clean up everything after stopping the services, you need to run this to also clean up the volume. If you skip this step, the initialization with the CLI will fail because the previous initialized data will be used.
docker compose down --volumes
You can find the complete template in my GitHub Repository. You are welcome to use it or open an issue if you find some.