I have used Vagrant for local WordPress development and decided to move to Docker. However, configuring Docker is painful. I didn’t understand how containers work together, how to configure existing images, and etc. I wanted to write down my process to figure the Docker configurations. This instruction is based on the Dennis Andersson‘s post:

Install Docker

Installing Docker is quite easy in this day. Just visit the Docker website and follow the installation guide.

Create the base Docker containers

Save the file below on your host machine* and execute the command on a terminal from the same directory you saved the file.

* Host machine means your desktop or laptop. I was sick with poor explanation like ‘set the port number’: which port number? host? or container? or whatever else? I will use this term below to prevent misunderstanding.

docker network create local_default
docker-compose up -d

Docker Composer

Docker is a game changer, but it supports only command line, so it’s hard to memorize. Docker composer is a tool for downloading images and creating containers automatically with .yml blueprint. If you created MariaDB with command line, it would be like this.

docker run --name mariadb -e MYSQL_ROOT_PASSWORD= password -p 3306:3306 --restart always -v mysql:/var/lib/mysql -d mariadb

The.yml file above creates MariaDB, NGINX proxy server, MailHog, and Portainer containers, and you can create this at any time with saved .yml. The command docker-compose up -d means booting up the composer with default configuration file (docker-compose.yml) with the option -d, which is the detached mode — run containers in the background, print new container names.

When you execute the docker-composer up command, it will download the server images and make containers. Once your host machine downloads the image, creating a new container with the image would be much faster.

Container and Image

Docker image is read-only source. Docker image itself doesn’t work and isn’t executable. The image is like .dmg disk image file. When you mount the image into your host machine, the container will be created. The container is changeable. For example, the MariaDB image contains the database, but it is not a source file. It is ready to use. When you insert this image into your host, it makes a container. You can create more than one containers from same image, and making many containers from one image does not create the clone of the image. The container just saves changes related on the container. When you insert some records into your MariaDB container A, it will be saved into the container A, not an image.

What the docker-compose.yml is doing

Well, let’s dig in how the .dmg and composer working. Under the services:, there’re four servers. First one is MariaDB. Let’s look at the line 4 to 14. container_name: mariadb, obviously the name of the container. Without this option, Docker composer names it as dirname_imagename_1. I personally like clear naming.

The second one is image: mariadb. It will find the image from your host machine and Docker repository — Docker Hub. You don’t have any MariaDB image on your host machine, so it will download the image from the Docker Hub.

The third one is environment:. This can set environment variables that the image supports. You can find what you can set on the Docker Hub page —

Next one is a volumes. This setting is the most important for beginners. Volumes option connects files and directories between your host machine and the container. Remember that the container keeps the changes that you make to an image. That means if you delete the container, then your records/tables/databases are deleted along with it. Forever. Doomsday! Got fired. If you upgrade the database version — downloading a new image and create a new container, and replace the existing container –, it will delete the data as well. So, you should keep your data on the host machine. - mysql(host location):/var/lib/mysql(container location) creates and connects the database files on your host machine. This volume, mysql, is declared on line 45. Docker will creates the volume named mysql in the default location (for Mac, it’s on the deeply-hidden location /var/lib/docker/volumes). If you specify the location like - ~/mysql:/var/lib/mysql, you’ll see the ~/mysql directory on your home directory. With this setting, you can keep the MySQL data safe.

The ports: - 3306:3306 setting is similar with volumes. It connects between the host machine’s port and container ports. When you calls the 3306 port (first), it calls the container’s 3306 port(last). If you run several websites on your host machine for development, you can separate ports number like site 1 is localhost:8080 (- 8080:80), and site 2 is localhost:8081 (- 8081:80). Now, host machine’s 3306 port points the container, so you can call the database from your host machine. For example, my beloved MySQL GUI tool, Sequel Pro.

Sequel Pro, and how to connect your MariaDB container

The restart option makes the container restarts automatically when it stopped, and the network defines the network of container working. The network should be the same with WordPress container’s network.

The Containers: NGINX Proxy

The proxy server allows using the virtual host for the other web server containers. With the NGINX proxy, you can access the other WordPress containers with a host name (http://wordpress.test) not a port number (http://localhost:8080). The command option run the shell script to increase max body size for increasing upload size.

The Containers: MailHog

The MailHog captures emails from your web servers. You can access here by typing http://localhost:8025 on your browser.


The Containers: Portainer

Portainer provides the web frontend for managing the Docker. This is quite useful to see missing, unused images, networks, and volumes. You can access here by typing http://localhost:9000 on your browser.


Create the WordPress Image

The official WordPress image is wordpress. You can download image and create container with an option image: wordpress:latest or image: wordpress:php7.2 (See the Docker Hub for more information). The problem for the official image is that has nothing on it. It is minimized configured version of  WordPress and web server, and it is suitable for live server. As a developer, I wanted set something more. For example, xDebug is not installed. I can install it by accessing the container with a command line, but I should install that for every WordPress containers that I created. I decided to make a new WordPress image for development.

To create a new image, we should make a text file named Dockerfile and execute this command.

docker build -t sujin2f/wordpress .

The docker build command… yes builds a new image on your host machine. -t option is tag, so your image’s name  will be sujin2f/wordress with a tag, :latest. In the Dockerfile, we can find which is the base image (wordpress:php7.2 in this case) and Docker will execute several commands for the new image based on the Dockerfile. Consequently, your new image contains xDebug, MailHog support, PHP ZipArchive module, and increased upload size on php.ini.

Create the WordPress Container

It’s the final step! Let’s create the WordPress container with the image you created. Save this file on your project root and rename it docker-compose.yml. I prefer to create wp-content directory for each project when I develop a whole website. Depends on your situation, the project root could be a theme or a plugin. Let’s make the container!

docker-compose up -d

Put the container_name: as you want, and make the image: same as the image name you created. If you don’t know the image name, check the Portainer. external_links: option connects your MariaDB container and MailHog container to the WordPress container. On the environment: setting, set the VIRTUAL_HOST any hostname you want. I prefer .test domain because Google Chrome redirects our beloved domain .dev to https://. I don’t want to set many things for development. If you need to set SSL on the development level, put - 443:443 on the NGINX proxy port and - 443 on the WordPress port, and create SSL certificates, and blah blah blah...

Set the WORDPRESS_DB_NAME, and Docker will create the database on MariaDB. WORDPRESS_DB_USER and WORDPRESS_DB_PASSWORD are the similar. Docker will create the database user. If you like to enable WP_DEBUG as true, put the WORDPRESS_DEBUG: 1. volumes: option would be - ./:/var/www/html/wp-content if you want your project root as a wp-content. You can change it like - ./plugins/your-plugin:/var/www/html/wp-content/plugins/your-plugin depends on your situation. ports: would be - 80. Docker will make a random port for the container. It’s like - 32483:80.

All is done. You can connect to the WordPress container with http://localhost:xxxx (the random port number/ Check it on Portainer). Something is weird. Where is my .test domain? Well, if you put the host setting on your host machine, you can use it. Run sudo vim /etc/hosts and put wordpress.test. Yea! Your request will call NGINX proxy, and the proxy will call your WordPress container. You are all set! In this step, you can create the super fast and light WordPress container as many as you want.

One last step: dnsmasq

If you are so lazy like me, you might not want to type sudo vim /etc/hosts whenever you add a new website. You can make your host machine to resolve a specific domain to a specific IP address. Follow this step.

  1. Install Homebrew
  2. Install dnsmasq via Homebrew
    brew install dnsmasq
  3. Make the dnsmasq configuration to resolve .test request to
    mkdir -pv $(brew --prefix)/etc/
    echo 'address=/.test/' > $(brew --prefix)/etc/dnsmasq.conf
  4. Register LaunchDaemons so you wouldn’t run this again
    sudo cp -v $(brew --prefix dnsmasq)/homebrew.mxcl.dnsmasq.plist /Library/LaunchDaemons
    sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist
  5. Create resolver for .test domain and set the name server
    sudo mkdir -v /etc/resolver
    sudo bash -c 'echo "nameserver" > /etc/resolver/test'

If you are really hurry, kill and restart dnsmasq.
sudo kill -9 $(pgrep dnsmasq)

All done. Enjoy Docker!

Note: You may recognized how to set the Portainer, MailHog, and MariaDB address to http://portainer.test or http://mailhog.test