Docker + Windows + WordPress + MySQL + PHPMyAdmin = Nirvana

The last time I did any WordPress development was over a year ago so I no longer have PHP and MySQL installed.  I started marching off down my well beaten path.  Download PHP and MySQL, install on my machine, do dev work.  Just after clicking on the MySQL download I suddenly thought, what the hell am I doing?  DOCKER!!!!  I already have Hyper-V installed so all I need is a container and some YUMMY YAML.

I’ve used Docker once before, for around 5 minutes, so I’m a complete noob really.  So going through this process to work it all out for myself was initially extremely painful.  I failed on my first session and ended up having to walk away from it all as I just wasn’t getting it.

I did a lot of reading and Googling and just wasn’t finding the explanation I needed to grok it all properly.  There are lots of explanations of how to get this working but they all seemed to stop at the crucial point for me.  They covered off some yml to get MySQL and WordPress containers up and running but stopped there.  What about persistence?  Or deployment?  Where the hell are all the WordPress files?

Some of them seemed to demo the solution I wanted but seemed to miss out on how they achieved it, or were doing it on different platforms.  I needed a noobs Docker for Dummies walk through.  So I’m going to document what I’ve found out in the hope that it crosses some of the Ts and dots the Is for others getting started.

Docker is The New VM

Don’t get me wrong virtual machines are great and very necessary but they’re also a bit overkill a lot of the time.  This is where Docker comes in.  It still requires virtualisation technology under the hood but it’s now transparent and not directly controlled.

Microsofts own virtualisation technology is Hyper-V.  Docker on Windows uses this by default but it could be used with VirtualBox from Oracle as well.  I’ve had lots of success running virtualised OSes on top of Hyper-V and more or less utter failure using it for Microsofts own emulators, the irony here isn’t lost on me by the way.

Docker is a container technology that wraps up specific services, such as databases, runtime or any other dependencies tasks require.  It lets you run just what you need for a given task without installing these services on the host OS. Fantastic.  Lets dig in.

Installing Docker

Dead easy.  Make sure Hyper-V is installed on your Windows box (Home users you’re out of luck here btw).  Go here, download for your OS and architecture, install.  Done.

The Docker installation is a very painless process.

Check Installation

Once installed., open a command line (Windows + X -> Command Prompt (Admin)) and execute:

docker version

You should then see some version information:

 Version:      17.06.0-ce
 API version:  1.30
 Go version:   go1.8.3
 Git commit:   02c1d87
 Built:        Fri Jun 23 21:30:30 2017
 OS/Arch:      windows/amd64

 Version:      17.06.0-ce
 API version:  1.30 (minimum version 1.12)
 Go version:   go1.8.3
 Git commit:   02c1d87
 Built:        Fri Jun 23 21:51:55 2017
 OS/Arch:      linux/amd64
 Experimental: true

If you see an error mentioning the daemon, Docker may well still be setting itself up in the background.  You can also create a Docker ID on the Docker site and configure your install to use this ID though I’ve not needed to so far so cannot comments on this aspect.

Next run this:

docker info

This gives you some useful info about your Docker environment, like so:

C:\WINDOWS\system32>docker info
Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 1
Server Version: 17.06.0-ce
Storage Driver: overlay2
 Backing Filesystem: extfs
 Supports d_type: true
 Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
 Volume: local
 Network: bridge host ipvlan macvlan null overlay
 Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: cfb82a876ecc11b5ca0977d1733adbe58599088a
runc version: 2d41c047c83e09a6d61d464906feb2a2f3c52aa4
init version: 949e6fa
Security Options:
  Profile: default
Kernel Version: 4.9.36-moby
Operating System: Alpine Linux v3.5
OSType: linux
Architecture: x86_64
CPUs: 2
Total Memory: 3.837GiB
Name: moby
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): true
 File Descriptors: 15
 Goroutines: 25
 System Time: 2017-08-06T15:06:13.8983022Z
 EventsListeners: 0
Experimental: true
Insecure Registries:
Live Restore Enabled: false


It’s probably worth having a look over your Docker settings before doing anything else.  You can right click on the Task Tray Docker icon to see various options, Click on Settings first and have mooch about to see if things are setup as you’d like.

The General tab is pretty self-explanatory and controls how Docker starts up and some security settings.  You can probably leave most of these alone.

The Shared Drives page is also pretty self explanatory.  Using this page you can control which host machine drives are available to services running within containers.  Since I’m using my H drive for my projects I’ve shared this drive with Docker.  This allows processes running inside your Docker containers to access stuff on the host machine drives.

docker shared drives settings

The Advanced tab is worth reviewing.  If you have used Hyper-V before and have customised it’s setup you’ll find that Docker automatically picks up some of these settings.  I’d configured Hyper-V to put VM images on one of my large external drives so Docker will install it’s Moby Linux VM here.

docker advanced settings

I’ve also upped the available RAM Docker can use to 4Gb, my dev box has 24Gb so I’ve plenty to throw at Docker.  Since it’s running on Linux 4Gb RAM should be more than enough to keep things running at a decent speed.

The Network, Proxies and Daemon pages are fine in their default state for now.

My Requirements

I wanted a WordPress development environment with persisted data.  If Docker is restarted or the host machine reboots, I want the state of both stored.  Both in terms of the WordPress state such as themes, plugins and so on and the database.  I’m fast with WordPress development but not that fast!

So Docker should accept all the responsibility of hosting Apache, PHP, MySQL and phpMyAdmin.  Docker also takes care of all the networking and configuration of those services.  The host Windows machine exposes a drive and hosts the WordPress files and MySQL databases on it’s “normal” file system.  These files are stored in directories that are mapped into the containers which allows for simple deployment once the development is complete.

I’m sure in time and as I learn more about Docker I’ll find a lot of this can be handled better.  For now this is where I am in the learning curve and it’s working.

Yummy YAML & Docker Compose

The idea here is to produce a file that tells Docker what you want to do.  It specifies one or more images to use to create one or more containers that provide the services you need.  Images are templates for containers, much in the same way classes can be thought of as templates for objects in an OO sense.  There are some issues here in terms of terminology which I don’t get.  Although we are creating containers, in the .yml files they are called services.  It’s probably my limited knowledge here but it would be clearer to new users if they just stuck to using the same terms.

What I was struggling to understand and configure was volumes.  I’m still a little in the dark to be honest and I’m not entirely sure I have this configured in the best way.  But what I’m showing here is working and it suits the requirements I mentioned above.

Directory Structure

As I showed above I’ve shared the host machines H drive.  Within this drive I have the following directory structure:


In the root directory (H:\js2017) I have created a Docker compose YML file called docker-compose.yml.  This is where the magic happens.  The file contains this YAML:

version: '2'
      - db
    image: wordpress
    restart: always
      - ./ui:/var/www/html
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_PASSWORD: p4ssw0rd!
      - 8082:80
      - back
    image: mysql
    restart: always
      - ./database:/var/lib/mysql
      MYSQL_ROOT_PASSWORD: p4ssw0rd!
      - back
      - db
    image: phpmyadmin/phpmyadmin
    restart: always
      - 8083:80
      PMA_HOST: db
      MYSQL_ROOT_PASSWORD: p4ssw0rd!
      - back

So in this file we have 3 services defined.  We have wordpress, db, phpmyadmin.  We also have a network aliased back (backend).  You could almost certainly take this file, alter the ports if need be and the volume entries and have this up and running pretty quickly.

You can see the references to the database and ui directories in the volumes declaration for the wordpress and db services.  The “.” notation is the relative path from the docker-compose.yml file in the project root directory.  These are mapped by docker to the containers internal file system running on top of Linux within the Docker virtual machine.  So anything written to the /var/www/html directory within the container ends up in the ui directory on the host machine and the same for the /var/lib/mysql directory for the databases.

The port mappings for the services are mapping host machine ports to container ports (hostmachineport:containerport).  So to view the WordPress site, I navigate to localhost:8082 in my host machines browser and this is forwarded to port 80 in the container and serves the page.


Going over the deployment of the MySQL database to a live server is beyond the scope of this article but to deploy the WordPress site it’s just a case of taking the contents of the ui directory and uploading it to the public html directory of your web server.

I’m sure there is a better way of managing this but for now with my limited understanding of the finer details of Docker and it’s container model this works for me.  Hopefully this has gotten you started and I’m sure I’ll revisit this again in the not too distant future with some better solutions and tips.

Happy Docking!!


  1. Mark · November 22, 2017 Reply

    Great article. Thanks! But where’s the PHP and web server? No images for them were loaded. It looks like the WordPress image may include PHP. But the web server? Apache? NGINX?

    • jammer · November 27, 2017 Reply

      These are loaded as depedencies of the images you want to use so no need to load them explicitly.

  2. Alex Bennett · June 6, 2018 Reply

    This helped me a bunch and I’m not even on windows. 🙂

Leave a Reply

Your email address will not be published.


This site uses Akismet to reduce spam. Learn how your comment data is processed.