Wordpress development made easy using Docker
Docker-compose + wordmove = ❤
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 databaseimage: 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 hereports: -"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 machinerestart: on-failure:5
Tries to restart the service maximum of 5 time in case of failurevolumes: — ./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 containerenvironment: ...
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
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 ❤