Wordpress development made easy using Docker

Docker-compose + wordmove = ❤

Mirjam
Seriously.digital
7 min readApr 1, 2017

--

Installing and developing wordpress locally used to be quite cumbersome. You needed to install a web server, a database server and then wordpress itself. If that wasn’t painful enough, the details of each component differed depending whether you were working on a machine running linux, osx or windows. And this in turn made maintenence, collaboration and deployment all the more difficult.

Thankfully, it’s 2017 and we have better ways/tools to tackle and even hide away this complexity — introducing Docker, a software container platform.

Docker automates the repetitive tasks of setting up and configuring development environments so that developers can focus on what matters: building great software

The easiest way to think about containers is like black boxes packed with software that you can run, halt and move around easily. Docker allows you to think about what you want, and preferably not how to get it.

In our case the black boxes, i.e. containers, which we would like to run are a wordpress instance and a database.

If you’re anything like me, you learn by doing. So let’s get down to it and run a Wordpress instance on your computer.

Setting up

First, in case you don’t have docker installed locally, download and install Docker Community Edition on your machine. To make sure everything was installed properly run these commands in the terminal:

$ docker --version && docker-compose --version
> Docker version 17.03.0-ce, build 60ccb22
> docker-compose version 1.11.2, build dfed245

Next, let’s make a your project’s directory where our configuration and local wordpress code will live. To do it run these commands in the terminal:

$ mkdir <project-name>
$ cd <project-name>
$ touch docker-compose.yml

The last line creates a new file called docker-compose.yml. It is a Docker Compose configuration file where we will define all wanted containers.
Open it and copy/paste the following:

version: '3'
services:
database:
image: mysql:5.7
command:
- "--character-set-server=utf8"
- "--collation-server=utf8_unicode_ci"
ports:
- "3307:3306" # (*)
restart: on-failure:5
environment:
MYSQL_USER: wordpress
MYSQL_DATABASE: wordpress
MYSQL_PASSWORD: wordpress
MYSQL_ROOT_PASSWORD: wordpress
wordpress:
depends_on:
- database
image: wordpress:latest
ports:
- "8000:80" # (*)
restart: on-failure:5
volumes:
- ./public:/var/www/html
environment:
WORDPRESS_DB_HOST: database:3306
WORDPRESS_DB_PASSWORD: wordpress
phpmyadmin:
depends_on:
- database
image: phpmyadmin/phpmyadmin
ports:
- 8080:80 # (*)
restart: on-failure:5
environment:
PMA_HOST: database
wordmove:
tty: true
depends_on:
- wordpress
image: mfuezesi/wordmove
restart: on-failure:5
container_name: my_wordmove # (+)
volumes:
- ./config:/home
- ./public:/var/www/html
- ~/.ssh:/root/.ssh # (!)
#######################################
#
# In case you want to reuse this config
# for another Wordpress project and
# them have both run *simultaneously*
# you'll need to:
#
# - change wordmove's container_name
# to anything else - marked as (+)
# e.g. wordmove123
#
# - change the first number of ports
# to another one - marked as (*)
# "<new_host_port>:<container_port>"
# e.g.
# - change wordpres port numbers
# from "8000:80" to "8001:80"
# - change phmyadmin port numbers
# from "8080:80" to "8081:80
# - change database port numbers
# form "3307:3306" to "3308:3306"
#
# (!) Copy your local .ssh keys only if
# necessary to connect to remote
#######################################

Let’s quickly unpack the contents of the file. In it we have defined 4 services: a MySQL database, a Wordpress instance, PhpMyAdmin and Wordmove.

In truth, we only need the first two. The latter two are just useful tools for development.
Phpmyadmin provides a web interface to interact with the local database.
Wordmove is a command line tool which enables syncing Wordpress and database data between the local machine and a remote server.

All of the services will be run together in an isolated environment. However, each of them will run in its own container and they will communicate with each other over the network using ports and environment variables defined in the docker-compose.yml.
As an example, let’s look at the Wordpress service we defined, it:

  • depends_on: — database
    Depends on the existence of a database
  • image: wordpress:latest
    Uses a Wordpress image which will be downloaded only once from Docker registry. The latest tag will use the latest wordpress version. If you need to use another version you can set wordpress:<version>. The full list of wordpress versions can be found here
  • ports: -"8000:80"
    Enables access to Wordpress by navigating to http://localhost:8000 in your browser. This is done by forwarding the exposed port 80 on the container to port 8000 on the host machine
  • restart: on-failure:5
    Tries to restart the service maximum of 5 time in case of failure
  • volumes: — ./public:/var/www/html
    Copies the default wp installation from the container to <project-name>/public/ directory allowing you to modify the code there directly.
    This is done by mounting the local directory <project-name>/public to the /var/www/html directory inside the container
  • environment: ...
    Sets some database related environment variables

The last thing left to do is spin up the containers by running the command:

$ cd <project-name>
$ docker-compose up -d

The first time this command is executed it will have to download the missing software. But any subsequent run will skip this step.
Note: The -d flag runs the containers in the background. Run the command without it if you want to see the services’ log messages for debugging purposes or otherwise

To make sure all four service are running, execute the following command:

$ cd <project-name>
$ docker-compose ps
This is an example output. Your actual service names may vary depending on your project’s name

That’s it for the setup!
Now you should be able to navigate via your browser to:

  • your Wordpress site at localhost:8000
  • PhpMyadmin web ui at localhost:8080 and gaining access using:
    — username: wordpress
    — password : wordpress

When you’re done with development you can stop the services with:

$ cd <project-name>
$ docker-compose stop

In case you want a fresh installation of wordpress run:

$ cd <project-name>
$ rm -rf public/
$ docker-compose rm -v -f

Note: If you have sudden problems with not seeing anything at localhost:800 — try deleting your browser’s history, restarting your terminal session or running:

$ docker system prune -a

Development

After you have your services up and running, it’s time to start coding. Your project directory structure should look like this:

project-name/
— config/
— public/
— docker-compose.yml

While coding, make sure you don’t overwrite public/wp-config.php and public/.htaccess. To be on the safe side, let’s copy the them over to config/ for safekeeping.

$ cd <project-name>
$ cp public/wp-config.php config/
$ cp public/.htaccess config/

The public/ directory is where your wordpress code lives and I highly recommend using git to version control your code.

Wordmove

As you are probably aware, Wordpress’ posts and a lot of its configuration is stored directly in the database. To replicate/sync those changes between local machine and the server you have two options:

  • Mimic everything you did on the server locally (and viceversa)
  • Use a command line tool like Wordmove

In order to use Wordmove, a configuration file called Movefile is needed. There we’ll define our two environments, namely local and production.

$ cd <project-name>
$ touch config/Movefile

Open the Movefile in your editor of choice and copy/paste the following:

local:
vhost: "http://localhost:8000"
wordpress_path: "/var/www/html/"
database:
name: "wordpress"
user: "root"
password: "wordpress"
host: "database"
charset: "utf8"
production:
vhost: "<your_domain_name>"
wordpress_path: "<absolute_path_to_wp_installation>"
database:
name: "<db_name>"
user: "<username>"
password: "<password>"
host: "localhost"
charset: "utf8"
ssh:
host: "<hostname>"
user: "<user>"
# password: "<password>"
# password is optional, will use public keys if available.

Since the production configuration data is sensitive and should be kept private, you’ll have to fill out those fields yourself. Note that the names local and production are arbitrary — you can use any names you see fit. It is also worth noting that the Movefile configuration is not limited to only these options nor to only two environments.

Now that we have our configuration ready, we can actually sync production and local databases. To do this make sure your containers are up and running, then enter into the wordmove container and pull from (or push to) the production database using Wordmove:

$ cd <project-name>
$ docker-compose ps
$ docker exec -it my_wordmove /bin/bash
# pull from production to local database
$ wordmove pull -e production -d
# push from local to production database
$ wordmove push -e production -d
$ exit

Upon executing the pull command and before applying the server changes to the local database — wordmove backs up the local database to
wp-content/local-backup-<timestamp>.sql
Similarly upon executing the push command, wordmove first backs up the production database to wp-content/production-backup-<timestamp>.sql

Backups are great in case you wish to undo the changes after a pull or a push command. For example, to revert your local database to a previous state — identify which backup you want to revert to and either:

  • Use PhpMyAdmin by navigating to localhost:8080, then select the wordpress database and import the sql backup via the ui
  • Use the command line and applying the sql backup from the container
$ cd <project-name>
$ docker-compose ps
$ docker exec -it my_wordmove /bin/bash
$ mysql -h database -u root -p wordpress wordpress < \
/var/www/html/wp-content/local-backup-<timestamp>.sql
$ exit

There is more to Wordmove than just syncing databases. It can also be used to sync uploads, themes and even plugins. If you found the tool useful, I recommend reading more on its many features.

That’s it! I hope you found this tutorial helpful :)

Happy coding ❤

--

--