Dockerizing WordPress, part 1 — running WordPress locally in Docker
A zero-to-hero guide on how to dockerize a WordPress site
A precaution: commands in this article were tested on a Mac, but they should work an a Linux and could work on Windows if you’re using Cygwin.
Table of contents
Dockerizing WordPress is a surprisingly long topic, so I split it into a bunch of articles:
- (this page) Dockerizing WordPress, part 1 — running WordPress locally in Docker
I plan to release the follow-ups:
- Dockerizing WordPress, part 2 — mutable vs immutable data in WordPress
- Dockerizing WordPress, part 3 — deploying a single WordPress site
- Dockerizing WordPress, part 4 — deploying multiple WordPress sites
- Dockerizing WordPress, part 5— testing WordPress in Docker
- Dockerizing WordPress, part 5 — monitoring WordPress in Docker
Now, let’s get straight to the first part of the series.
Running WordPress locally in Docker
Make sure:
- You have followed installation instructions for Docker Community Edition,
- You can
docker run hello-world
successfully.
With that out of the way, let’s start our first WordPress site locally, on our machine. Googling “docker wordpress” guides us to the official (maintained by the Docker team) WordPress page on DockerHub. Readme invites you to execute the following command:
# not gonna work for now, don't executedocker run --name some-wordpress --link some-mysql:mysql -d wordpress
Let me mark it with digits:
docker run --name some-wordpress(2) --link some-mysql(3):mysql(4) -d wordpress(1)
It says:
- (1) Use the
wordpress
image to start a Docker container, - (2) name the new container
some-wordpress
, - set up its network settings so it can reach another running container named (3)
some-mysql
under alias (4)mysql
. The alias is effective just forsome-wordpress
container and makes it possible forsome-wordpress
to resolvemysql
hostname to ip address ofsome-mysql
container (you can pingmysql
from withinsome-wordpress
and reachsome-sql
).
Note that --link
it a legacy feature of Docker, its use is discouraged for actual development and production, but is still encouraged for use in documentation.
As I mentioned, it won’t work. Why? Because we don’t have a mysql
container running yet! Let’s head to the official DockerHub page for MySQL to solve that.
Start mysql container with
# go run it!docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=secret -d mysql:8 --default-authentication-plugin=mysql_native_password
Does MySQL actually work? Let’s check it with docker ps
. The some-mysql
container should be in the output.
Now we can start the WordPress container with
# run this and then navigate to http://localhostdocker run --name some-wordpress --link some-mysql:mysql -p 80:80 -d wordpress
Note that I’ve added -p 80:80
so we can reach the Apache web server in the container on http://localhost
, and made it run in the background with -d
.
Hopefully, after you open http://localhost
, you see this:
Troubleshooting
If you encounter any errors, make sure both containers are running with docker ps
, if there are less than two, discover the dead ones with docker ps -a
and check out their logging with docker logs <containerId>
.
If you encounter an error similar to
docker: Error response from daemon: Conflict. The container name "/some-wordpress" is already in use by container "ffed863335b4a7ffa2f41dee712f96f0e4a90b813e138adbb45bf1897356ddc0". You have to remove (or rename) that container to be able to reuse that name.
just remove the container with
# removing a container with docker rm <containerId>docker rm ffed863335b4a7ffa2f41dee712f96f0e4a90b813e138adbb45bf1897356ddc0
(you may need docker rm -f
to remove a container that’s still running. Another option to remove a running container is to stop it first with docker stop <containerId>
, and then remove with docker rm <containerId>
.
The persistence question
Now you may wonder: where does the data go to?
In the setup above, mysql
container holds the database and wordpress
container (that runs Apache, php and WordPress code) holds the rest of WordPress files. All the data is there, right in the the containers (in the mutable layer that sits on top of immutable layers that containers consist of). The problem is, Docker containers are supposed to be spawned and thrown away on a regular basis, so expect to lose that data pretty quickly.
There are two solutions in Docker world for persisting data: volumes and bind mounts. Let’s go with the first option and create a database volume and a volume for WordPress files:
docker volume create wp_db
docker volume create wp_files
Now remove all the running containers (I suppose you’re just starting with Docker and two containers we just created is all you have):
docker rm -f $(docker ps -q)
And spawn containers again, with volumes attached:
docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=secret -d -v wb_db:/var/lib/mysql mysql:8 --default-authentication-plugin=mysql_native_passworddocker run --name some-wordpress --link some-mysql:mysql -p 80:80 -d -v wp_files:/var/www/html wordpress
Now your files are safely stored in wp_db
and wp_files
containers, and you can copy data from and to the volumes using temporary, throw away containers.
What about Docker Compose
?
Throughout the tutorial we used Docker command line interface (the docker
command in the terminal). You may also install docker-compose
, a Python script that, roughly speaking, reads multiple docker run
options for multiple containers from a YAML file docker-compose.yml
and starts/stops those containers.
Both docker
and docker-compose
work the same way — there is Docker daemon running in the background, and they talk to the daemon and ask it to start or stop containers with particular options.
If you’re just starting out with Docker, I suggest you to get grips with docker
command, be able to run multiple containers manually, and only then move on to the container orchestration solutions (Docker Compose, Docker Swarm, Kubernetes and so on).