Docker for Local WordPress Development
Great question. Fortunately, someone who was paid to make this sound good has already answered this:
Docker takes away repetitive, mundane configuration tasks and is used throughout the development lifecycle for fast, easy and portable application development.From the docker.com homepage
As it pertains to this guide, Docker is a tool that will enable you to build portable, reproducible local development environments for your WordPress projects. Bravo.
👌Ease of use. Once configured, using it is as simple as one command to start the environment when you begin work, and another command to stop it when you are finished for the day.
🛋️Simple LAMP stack setup. This guide will provide us with ye olde reliable LAMP stack for WordPress, but Docker can be used for many different environments. For example, you could use NGINX instead of Apache, or MariaDB instead of MySQL.
🤓When paired with version control, Docker allows us to keep environment parity between developers. Gone are the days of, “Well it worked on my machine.” A good analogy is that a Docker container is like a picnic basket, which contains all the food and the dishes for the picnic, or your app. Anyone with this picnic basket could have the same picnic.
🚀Docker can also be used to keep parity between your local, staging, and production environments, although we are not going to cover that here.
To kick it off, head on over to the Docker website and download a copy of Docker Desktop. Install that.
This process should work on macOS or Windows with WSL2. I’ve tested it on both, but your mileage may vary, particularly with Windows.
It’s crazy easy. Just create your project directory. Then, inside of it, add the following
version: '3.1' services: # mysql database container db: image: mysql:5.7.16 volumes: - ./db:/var/lib/mysql restart: always environment: MYSQL_ROOT_PASSWORD: rootpass MYSQL_DATABASE: exampledb MYSQL_USER: user MYSQL_PASSWORD: pass # wordpress container wordpress: depends_on: - db image: wordpress volumes: - ./wp-content:/var/www/html/wp-content:cached ports: - 8080:80 restart: always environment: WORDPRESS_DEBUG: 1 DEVELOPMENT: 1 WORDPRESS_DB_HOST: db WORDPRESS_DB_USER: user WORDPRESS_DB_PASSWORD: pass WORDPRESS_DB_NAME: exampledb
Save that, pop open your terminal, and in your project directory, run:
This will run for a minute as it builds your container for the first time. You’ll know it’s complete when the terminal stops printing. The final output will likely be:
Version: '5.7.16' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server (GPL)
Once finished, you can navigate to localhost:8080 in your browser. From here you can go through the WordPress installation… and you’re in. All done, kbye!
🤔Wait, What just happened?
docker-compose.yml file acts as the instructions to build our containers. Let’s break it down:
version defines the docker-compose version we are using.
3.1 is fine for this purpose. Read more about the versions here.
services: # mysql database container db: # wordpress container wordpress:
services are how we define the containers that will be build. We have two:
wordpressfor Apache, PHP, and WordPress.
image is where we define the base image we want to use for our database. MySQL is popular, but you can also use MariaDB. I’ll note that I’ve used MySQL 5.7.16 for compatibility with Windows, but 5.7 worked fine for me on macOS.
volumes: - ./db:/var/lib/mysql
volumes is where we define the bind mount for the database which ensures persistence across restarts of the container. Basically this makes sure you don’t lose your data.
restart: always as a precaution.
environment: MYSQL_ROOT_PASSWORD: rootpass MYSQL_DATABASE: exampledb MYSQL_USER: user MYSQL_PASSWORD: pass
environment is where we define the environment variables for the container, such as the database name, MySQL user, and their password.
depends_on: - db
depends_on specifies the database container as a dependency and ensures it is started first.
image: wordpress will pull the latest stable version of WordPress. Note that you may want to specify a WordPress version here so that you can ensure compatibility of your project when WordPress updates. To specify the version, use a tag with the version number. For example:
image: wordpress:6.1. Here is a list of all of the various image tags for WordPress.
volumes: - ./wp-content:/var/www/html/wp-content:cached
volumes is where we add the bind mount for the
/wp-content directory. Read more about this bind mount below. I’ve added a consistency of
cached, which in my experience can make a significant improvement in performance if you’ve got a lot in the
ports: - 8080:80
ports: 8080:80 maps our local port
8080 to the container’s port
80 where WordPress is running, allowing us to access it locally through
restart: always once again.
environment: WORDPRESS_DEBUG: 1 DEVELOPMENT: 1 WORDPRESS_DB_HOST: db WORDPRESS_DB_USER: user WORDPRESS_DB_PASSWORD: pass WORDPRESS_DB_NAME: exampledb
And finally, we define the
environment variables for WordPress.
development set as
1 for a local development environment with debug mode enabled. Followed by the database variables, which must match those defined in the
db service’s environment variables.
The container has a bind mount configured between the
/wp-content directory in the project folder and the
/wp-content directory in the container. This ensures that the files in these directories will remain in sync as you change them locally, or in the container. This will allow you to build your custom theme right here in your project’s
/wp-content/themes directory. In short, changes you make locally are reflected in your running container, which you can access at localhost:8080.
Working with Docker
Now that your container is running, you can start developing your theme. Any uploads to WordPress will be saved in the
/wp-content/uploads directory, and all changes to your CMS content will be tracked in the database in the
When you’re finished for the day and you want to stop your local environment, run
docker-compose stop. This will stop the running containers.
When it’s time to fire up the dev environment again, run
docker-compose start to start your previously stopped containers.
docker-compose down is similar to
docker-compose stop, except that
down removes the containers after stopping them. The reason I usually prefer a
start workflow over
up is that it does not produce a bunch anonymous volumes every time the container goes
down. While seemingly trivial, these volumes can slowly build up, taking up space and slowing your Docker containers down. If you run into that situation try a
docker volume prune.
Adding Version Control
The portability of Docker really shines when you combine it with version control like Git. By adding a
docker-compose.yml file to your git repo, you are ensuring that not only is your app under version control, but so is the environment in which it runs. This can be tremendously helpful for projects with more than one developer.
So, go ahead and throw the whole thing into a git repo, but you’ll probably want to add a
.gitignore to prevent some files from being tracked. This will vary based on your requirements but you might want to consider including the following:
/db /wp-content/uploads /wp-content/plugins
/db will exclude the database from the repo. The database can be huge and will result in lots of git changes if it is included in the repo. If you need to share your database with other developers, I’d consider using a different option (maybe a plugin or WP-CLI).
/wp-content/uploads will exclude Media Library uploads from the repo.
wp-content/plugins will exclude the plugins from the repo. This way you’re not managing plugin updates through version control.
Taking it further
And there it is. Hopefully you’re now in a good place to be able to start using Docker to build portable local environments for your WordPress projects. And now that you’ve had just a wee taste of what Docker can do, let me give you some thoughts to chew on.
Many experienced WordPress developers will use the same custom starter theme and/or setup to begin every WordPress project. You should too! And you can bake a Docker configuration right into it. Just think of how much time could be saved with a workflow that solely consists of cloning your Skeleton’s repo into your new project, and running
docker-compose up. Now you immediately have a pre-configured local development environment for your new project. You can move straight into the fun parts. You’re getting a raise for that one. Good job.
I’m not going to get into it here, but it is good to be aware that some web hosts will support Docker containers. You can write a set of instructions for your environments as Dockerfiles, which will afford parity between your local, staging, and prod environments, a level of predictability that can otherwise be hard to come by.