Happy new year! I hope you enjoyed the holidays (assuming you didn’t have to work) and are now back, eager to learn. I sure am and I’m back with a fantastic tutorial for you: how to use Docker for WordPress development. I actually announced it a few days ago via Twitter (in Catalan):
Let me start with a brief story: I started my journey as a WordPress developer using LAMP (Linux + Apache + MySQL + PHP). Unfortunately, I quickly realized that, when working on more than one project at the same time, the setup was a mess, as they all used the same server, the same database… I then moved to Vagrant (VVV in particular), an environment designed for WordPress development, but it was a pain in the ass: my 6-year old computer had a lot of trouble running multiple Virtual Box instances and everything was extremely slow. I then tried to move to Local by Flywheel, because Antonio seemed to enjoy the environment a lot… but it never arrived to Linux! Wasn’t there a fast, reliable, easy-to-use option for me?
In the end, I decided I had to learn more about Docker and see if this tool I’d heard of would fit my needs. If you want to learn more about Docker too and want to use it in your WordPress developments, don’t miss this guide – learn how to build development environments quickly and efficiently.
Docker
Docker is a computer program that performs operating-system-level virtualization, also known as “containerization”. This has two main advantages compared to the classic LAMP setup:
- containers are isolated from each other and the communication between them is limited, which increases security
- tools, libraries, config files, and everything the container needs to run is in the container itself, which means we’ll have a clean and tidy setup – once we’re done with the container, we can remove it and everything will be gone.
In my opinion, the main advantage Docker has over other virtualization softwares is the virtualization environment it uses by default: runC. This environment runs all containers on the host OS (instead of running a full virtual machine), which makes things way faster and more lightweight. That means the host (our PC) and the guest (the container) will share a lot of resources and everything will run like a charm in an “old” computer.
Docker Installation
To install Docker on Linux (Debian/Ubuntu), just run the following command:
Follow the instructions on screen and wait until apt
is done.
Once Docker is installed, we can use it with the docker
command. The only problem with Docker is the fact that you’ll need to run it with admin privileges using sudo
. But we can solve this. If you’d rather run docker
without sudo
(and thus avoid typing your password often), add your user to the docker
group:
where your-user
is obviously your username in Linux. BTW, you might need to log out and log back in to actually apply this change.

Nelio A/B Testing
Native Tests for WordPress
Use your WordPress page editor to create variants and run powerful tests with just a few clicks. No coding skills required.
How to Install Docker on Mac or Windows
If you’re using Mac or Windows, you can also install Docker. Just follow the instructions on Docker’s website:
How to Use Docker
So, now that we have Docker set up and ready to be used, it’s time we give it a shot. As I said, Docker is a tool for managing contained apps. If we want to launch a container, we need to run the following command:
For example, try out this command:
to launch the classic Hello World within a Docker container. If everything worked as expected, you’ll first see something like this in your terminal:
This gives us some interesting information. First, Docker looked for an image named hello-world
, but it couldn’t find it. Second, it looked for that image in an online image repository, and this time it succeeded and was able to download it. And that’s one of the great things about Docker: it has an image repository with several packed apps we can use as Docker containers (similar to what we have in WordPress, with the plugin and theme directories).
Now, back to our terminal, we can see the actual output of the Hello World container:
And that’s it! You’ve already run your first Docker container ?
If you now type the following command:
you’ll see the container Docker created using the hello-world
image:
To remove it, just type:
making sure that you type in the appropriate CONTAINER ID
.
And that’s basically all there is to Docker… So let’s jump into yet another tool (Docker Compose) and learn how to use it as WordPress developers ?
Docker Compose
As you can imagine, in Docker’s image repository there’s a WordPress image available. You might think that we simply need to pull that image and we’d be able to start a new WordPress site, but that’s actually not true:
- On the one hand, this image doesn’t include the database system. So, if we want to start our WordPress site, we’ll need to install MySQL somewhere (or use a MySQL container).
- On the other hand, the WordPress image requires some configuration parameters (such as, for instance, where’s the database and the credentials to access it). So everytime we run the instance, we need to specify those arguments.
Using the docker
command alone can be complicated, specially when we have to start multiple containers all at once and some depend on others (as WordPress does). Luckily, there’s a tool called docker-compose
that uses a configuration file to describe all the containers we need, the dependencies they have with each other, and their specific setup.
Let’s start by creating a new directory where Docker Compose’s config file will reside. For example, create ~/docker/test
and add a file named docker-compose.yml
with the following content:
I know this file might seem complicated, but it isn’t. Look carefully: the file simply defines the two services (or containers, if you will) that we need to run WordPress: mysql
and wordpress
. The former (mysql
) contains information about the image we need (which is obviously a MySQL image, version 5.7) and some config parameters. The most relevant are probably the port mapping (we’ll be able to access the database using port 8081 in our host, which maps to 3306 in the guest) and the database setup itself (user, password, and so on).
The latter is WordPress itself (wordpress
) and it follows a similar approach. Here we tell Docker Compose that we want WordPress to be accessible via the port 8080 in our host. We also specify that WordPress relies on the other service: mysql
. And we finally add some additional configuration parameters (essentially, those to access the database).
Once we’re done, save and exit the file, and type the following command:
and Docker will download the WordPress and MySQL images and will start our WordPress.
Just wait for a couple of minutes and, once it’s done, go to http://localhost:8080
with your web browser (notice how the port we’re using here matches the one we specified in the configuration file) and you’ll see your new WordPress site:

From this moment on, if we want to create new WordPress instances, you’ll simply have to repeat the steps we’ve discussed. That is, you’ll create a new directory in ~/docker/
, you’ll add the docker-compose.yml
file, and you’ll set it up. Just make sure that you’re using different ports for this new instance, or you won’t be able to run them both at the same time (for example, use 8082
for WordPress and 8083
for MySQL).
Once you’re done, you can stop the container running the following command:
Just make sure you use stop
instead of down
:
as down
will not only stop the container, but also remove it completely. That is, if you restart a container using docker-compose up
after you shut it down using the down
command, your WordPress will start from scratch (an empty database, an uninstalled WordPress, etc).
Docker and WordPress Development
Great! You almost have all you need to develop WordPress plugins and themes using Docker effectively. I hope my explanations so far have been helpful and you now better understand what Docker is and how it works.
In my opinion, there are only two things missing to be 100% effective with this setup:
- How to add your plugin or theme in the Docker container.
- How to use domain names like
http://content.local
instead ofhttp://localhost:port
with Docker.
Adding Your Project into a Docker Container
Every time I have to work on a new plugin or theme, I create a new Docker container. If you follow the guide so far, you’ve probably realized that the WordPress installation created by Docker is completely empty and doesn’t contain any of our plugins or themes. So, assuming I’m working on a new plugin, how do I add that plugin in the wp-content/plugins/
folder that lies in the container?
There are multiple options to expose directories from the container and make them available in our host machine. The one I like the most maps the directory of our project (and only that directory) to a directory in WordPress. Let’s see this with a concrete example.
Assume I’m working on my plugin with my laptop. This plugin is currently in ~/dev/nelio-content/
. I obviously want this plugin to be included in my development environment, so that I can test it and see that all the changes I apply work as expected. The first thing I’ll do is add a docker-compose.yml
file in my plugin. The content of said file will be an exact copy of what we’ve already seen, but with one extra option:
Essentially, we simply added a new directive named volumes
and we specify the mapping we want. That is, we want the current directory (namely, our plugin’s directory, identified with a dot .
) mapped to /var/www/html/wp-content/plugins/nelio-content
(which is a directory in the WordPress container). Easy, right? After all, the syntax we’re using is the same we used for port mapping, but we’re applying it to directories now.
And that’s it! If we now start our container, we’ll see that our plugin is available in the Dashboard » Plugins screen.
Using Domain Names
I personally don’t like accessing development installations using localhost and a port. I think using domain names like http://content.local
is way easier. So, how can we achieve that? Well, we need a few things…
First, we need to tell our computer that a certain domain name (for instance, content.local
) is the computer itself. This is as easy as editing the /etc/hosts
file and mapping this new domain name to the localhost
IP:
Great! Right now http://content.local
is exactly equivalent to http://localhost
. But that’s not exactly what we want… What we want is to map http://content.local
to http://localhost:8080
. To do that, we need a proxy server that maps requests to the former address to the latter. This is also very easy to achieve. Just create a new folder named ~/docker/proxy
with the following docker-compose.yml
file:
Then, run docker network create proxy
and start your new container using docker-compose up -d
. This will create a network that all our WordPress projects will use (more on that in a minute) and install the proxy that’ll do all the magic.
Finally, we simply need to modify our WordPress container so that it automatically notifies the proxy about its existence:
Okay, so we’ve done a few things here. Let’s take a closer look at all the changes we applied:
- In the WordPress
environment
section we added two new attributes:VIRTUAL_HOST
andVIRTUAL_PORT
. These are the attributes that our proxy will use to map theVIRTUAL_HOST
tolocalhost:VIRTUAL_PORT
. - Since we want our WordPress service to talk to our proxy, we need to make sure both containers are in the same network. This is pretty easy: we simply add a
networks
option in WordPress and include thefrontend
network (you can use whatever keyword you want). Then, at the end of the file, I added a few more rules. In particular, I added anetworks
section and specified that there’s an external network identified by the keywordfrontend
with the name proxy. Notice this external network is the same one we used in the proxy’sdocker-compose.yml
. - Finally, I want to make sure that WordPress and MySQL can talk to each other too, so I have to make sure they’re also in a common network. To do that, I added them both in another network named
backend
, which is internal and, therefore, nobody else can access.
And that’s it! You now know everything I know about this tool. I hope you enjoyed this guide and, if you did, please share it with your colleagues and let me know in the comment section below!
Featured Image by Abigail Lynn.
Leave a Reply