Running WordPress with Docker
There are many ways to run WordPress locally on your development machine.
- You can install MAMP/WAMP/XAMP or any other bundled LAMP stack, configure a database and install WordPress
- You can install Local by Flywheel, start it up and get a WordPress instance
There is one more way to do it: You can use Docker.
Docker provides a way to run container-based applications on your local system. All it requires is that you have Docker Engine (for Linux) or Docker Desktop (for macOS and Windows) installed.
As a quick introduction: Our WordPress installation using Docker requires two images:
- A WordPress image containing both Apache properly configured and the WordPress code ready to install
- A MySQL or MariaDB image to store the site's data
You can optionally add a phpMyAdmin image to manage the database via a GUI
The database and the code for WordPress are passed through to the local directory where we ran
docker-compose from. That way the changes will persist when we shut down the container.
Yes, I know that this has already been done multiple times by developers far more experienced in Docker than I am. This is still something I would use in my projects so I'm ok with learning using this combination.
Getting Started: Looking at the pieces #
We'll break the
docker-compose.yaml file into its component services. This will help explain how they interact with each other.
We first specify the version of Docker Compose that we're using. 3.9 is the latest version.
The services section presents the three services we will use for our WordPress installation:
wordpress and the optional
db service configures the MySQL database we will use for the project.
We use the latest version of the Docker image for MySQL container, specified by the image attribute pointing to the name and version of the application we want, in this case:
If we need to specify a version, we can replace the value latest` with a specific version.
The volumes section specified the external volume name and the associated directory inside the image.
The environment section specified environment variables that will be passed to the container.
Because this is an internal service, we don't expect people to access the database directly, only via phpMyAdmin or WordPress, we don't specify a container to host port translation.
The WordPress service is the core of the application. This container will host WordPress and interact with the database. Because of its importance, this is where we do most of the work.
depends_on explicitly tells Docker Compose that
wordpress depends on the
db service. This will cause the following to happen:
docker-compose upstarts services in dependency order.
dbwill start before
- If you start a specific service using
docker-compose up SERVICE, docker automatically includes SERVICE’s dependencies. In our case, docker-compose up wordpress also creates and starts
docker-compose stopstops services in dependency order.
wordpresswill stop before
using the volumes directive, we create a volume associating a directory on the host with the directory inside the container hosting the WordPress application. This will make the code available even if the container is not running.
The ports directive maps a port on the container to a port on the host. WordPress maps port 80 in the container to port 8000 on the host. To access WordPress, just point your browser to
restart indicates the restart policy for the container. The possible values are:
- always: The container always restarts
- on-failure: Restart a container if the exit code indicates an on-failure error
- unless-stopped Restarts a container, unless the container is stopped (manually or otherwise)
The environment directive holds environment variables that will be passed to the container.
phpMyAdmin container is similar to the
wordpress container but it's not as complicated.
The only differences are:
- The container name (
- The image that we use (
- The port we expose (phpmyadmin will use port 8181)
- The values under
The final image we'll work with is one for the WordPress CLI. Rather than installing it in a custom image, I found out that there are prebuilt images we can use.
volumes directive makes the specified volumes available to other containers. We don't need the functionality but it is still good to have in case our project grows.
Part 2: Customizing individual images #
The code works fine as is using the container defaults but there may be times when you want to customize the images.
What we'll do is create a separate Dockerfile for each of the containers we want to customize and then reference them from the
Creating a Dockerfile #
Customizing an image usually means creating a Dockerfile with your custom configuration and then building the image or referencing the image from a
For this example, we'll create a
Dockerfile to modify the
The Dockerfile will do the following tasks:
- Use the latest beta version of the WordPress image, indicated by
FROM wordpress:beta-6.0-beta2-php8.1-apache. 6.0 Beta 2 is the latest version of WordPress as of this writing
- Update all the packages in the image and install Vim and wget
php.iniwith the one in the local directory
RUN apt update &&
apt upgrade -y &&
COPY php.ini /usr/local/etc/php
We then modify the
docker-compose file to reference the custom
Assuming that the files for the custom image are in a subdirectory of the current directory, we can modify the
docker-compose.yml file to reference the custom image:
docker and docker-compose commands #
Here are some commands that will be necessary to build and use the project.
Building and starting the containers #
The first thing to do is to build the containers.
docker-compose up will build the containers and start them.
- docker-compose: the command to run
- up: Creates the networks and containers specified in the docker-compose.yml file. It then runs the application
- -d: Run in detached mode. This means the containers will spin up and
docker-composewill return you to the shell. Otherwise, the terminal will wait until you kill the
ctrl + C
docker-compose up -d
We only need to do this once, after building the images, we can run
docker-compose start to start the containers without building them. This assumes you've run
docker-compose up once before.
Stopping and shutting down the containers #
The inverse command of
docker-compose start is
docker-compose stop. This will stop the containers but keep them in place so you can start them up later without having to rebuild them.
docker-compose down is the inverse command to
docker-compose up. It will stop and remove all the containers associated with the
Building the custom image #
When working with
docker-compose referring to the build system is enough to pick up the right version. But it will not be enough if you want to share the custom image or if you want to use it in other projects.
For that, the solution is to build the image using
docker build. This will build the image based on the Dockerfile we create and using the
-t flag create a name and a tag so we can distinguish our image from others we may have installed.
docker build -t wordpress_local:wp5_custom_1.0 .
Logging into the containers #
The final command to run is
docker exec. This will run the specified command in the specified container.
The command uses two flags:
- -t: Attach to STDIN, STDOUT and STDERR of specified container
- -i: Run in interactive mode
You can combine the two flags into one,
docker exec -ti wordpress bash
For our project, this is important, otherwise we wouldn't be able to run the WordPress CLI we installed in the custom image.
The example command will run the Bash shell inside the
In this post, we've talked about building a WordPress installation using Docker.
What I particularly like is that I have access to the container so I can add and remove themes, and plugins and even run the WordPress CLI without having to rebuild the image or have compilers and developer tools installed.
It is just a beginning exercise to explore how we can use integrate multiple tools and applications into a single Docker-based project.
More Reading #
I found Use Docker to create a local WordPress development environment particularly useful when writing this post.