Containers, containers, containers…

Dockerizing WordPress, part 1 — running WordPress locally in Docker

Mikhail Vasin
5 min readDec 22, 2018

--

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:

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 for some-wordpress container and makes it possible for some-wordpress to resolve mysql hostname to ip address of some-mysql container (you can ping mysql from within some-wordpress and reach some-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).

That’s it for the part 1 of “Dockerizing Wordpress” series. Stay tuned for more.

You can follow me on Twitter, GitHub and LinkedIn.

Until the next time!

--

--