Serious WordPress Development – Part 1 – Local Development Setup

This article is Part 1 of my Serious WordPress Development series. All code can be found here. For more context please read the introduction.

Regardless what application is being developed, it all starts with setting up your local machine in a simple and automated fashion, so that a new team member can get on-boarded quickly. If each developer has the very same environment, then the “works on my machine” effect is avoided. Hence, we reduce the risk of wasting time on single and localised errors caused by environmental differences.

Setting up a new local environment must be done with the following goals in mind:

  • Standard – the setup should use well-known and well-supported packages and infrastructure setup primitives.
  • Speed – the process should take a few minutes, and not hours.
  • Simple – the initial setup should be automated, done with a minimal set of simple commands (ideally one), without requiring the developer to know about its intricacies.
  • Self-contained – once setup, the need for “external” dependencies – i.e. remote applications, servers, databases, cloud services – should be limited to package repositories (e.g. yum, docker, NPM, maven repos etc…)

My team initially decided to use the free MAMP local WordPress server environment. MAMP is downloadable as an OS native package, and can also be installed with Homebrew on MacOSX. MAMP comes installed with PHP, Apache, and MySQL, and provides a user interface that lets you manage your local WordPress sites.

I had several issues with MAMP. The initial installation is a multi-step process requiring installing MAMP and configuring it (e.g. what ports the site should run on). It is largely a manual process done through the MAMP graphical user interface. More importantly we were relying on a third party software for doing something pretty standard. So this setup definitely failed the Standard, Speed, and Simple test.


I therefore replaced MAMP with a Docker setup using Docker Compose.  I often use Docker Compose for orchestrating multiple containers locally. I find it very easy to get started with, as it requires only one deployment descriptor (docker-compose.yml) and a set of simple commands (up, down, stop, start). It is also relatively well documented, with an active community of users.

Below is an example I re-created for this article that is very similar to what we had:

version: '3.1'


    image: wordpress:5.3.2-php7.3-apache
    container_name: wordpress
    restart: always
      - 80:80
      WORDPRESS_DB_HOST: mysql
      WORDPRESS_DB_USER: wp_db_user
      WORDPRESS_DB_PASSWORD: wp_db_password
      WORDPRESS_DB_NAME: wp_db
      - ./wordpress:/var/www/html

    image: mariadb:10.3.22
    restart: always
    container_name: mysql
      - 3306:3306
      MYSQL_DATABASE: wp_db
      MYSQL_USER: wp_db_user
      MYSQL_PASSWORD: wp_db_password
      - ./mysql/data:/var/lib/mysql

    image: phpmyadmin/phpmyadmin
    container_name: phpmyadmin
      - mysql
    restart: always
      - 8989:80
      PMA_HOST: mysql

This setup enables a few things that are really important to me:

1. The whole stack is orchestrated as one fleet of resources, composed of three integrated containers:

    • the wordpress app
    • the mysqldatabase
    • the phpmyadmin app

Note: WORDPRESS_DB_HOST: mysql and PMA_HOST: mysql allow the WordPress and MyPhpAdmin apps to connect to the database using the mysql service name on the Docker Compose virtual network.

2. All WordPress files are mounted to our host folder ./wordpress. It means that, while the container runs, any change to a PHP file will be automatically picked up.

3. All MySQL database files are mounted to our host folder ./mysql/data. It makes it easier to tear down if necessary and re-create the site from scratch (we will use that in a later part of my series about dB changes management).


If you want to try this stack you can do the following:

git clone
git checkout 9662a4a5379840
docker-compose up -d 

The initial creation of the three containers on my machine with a pruned docker system (i.e. cleaned of any image, volume, or network) takes under a minute. Once finished you should have something like that:

 ➜  wordpress-blog git:(master)docker ps
CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                    NAMES
cb2aec9b914e        phpmyadmin/phpmyadmin           "/docker-entrypoint.…"   30 minutes ago      Up 30 minutes>80/tcp     phpmyadmin
e1e95cfb1a95        wordpress:5.3.2-php7.3-apache   "docker-entrypoint.s…"   30 minutes ago      Up 30 minutes>80/tcp       wordpress
0a963bda3776        mariadb:10.3.22                 "docker-entrypoint.s…"   30 minutes ago      Up 30 minutes>3306/tcp   mysql

Then all you have left to do is:

  • Setup your WordPress site, username, and password by visiting http://localhost, and after a few clicks you should be able to access the WordPress admin at http://localhost/wp-admin.
  • Visit the MyPhpAdmin at http://localhost:8989 using the root/root or wp_db_user/wp_db_password credentials, where you will see that the wp_db schema has been created.


In this article I explored how you can initially setup your WordPress project with one single Docker Compose descriptor file. In my next article I will talk about how to manage environment-specific WordPress configurations, which is necessary as soon as you want to have a Continuous Delivery process in place.

I hope this article can be useful to you.