This blog post is going to detail my journey with running a Node & PHP (WordPress) development environment on Windows 10 using WSl2, Docker and VS Code. I will share the bare config in a final, separate post at the end.
The Goal
I am a React developer by trade, so all I really need to be happy is VS Code and Node. As part of a previous project I entered the WordPress realm and authored several WPGraphQL integration plugins. Embarrassingly, I did all of this work with a deployed WordPress installation and vanilla VS Code. I didn’t want the hassle of installing PHP and XDebug on my personal machine. I even used FTP to deploy my code changes to the server – gross.
So, having used VS Code remoting and Docker at work I felt this was a winning solution. However I do not have the time to learn how to build a Docker container development environment from scratch. Cue the Docker for Windows, WSL2 and VS Code integration. This is perfect!
The requirements:
- VS Code should open my working directory in a Docker container
- PHP intellisense and XDebug should be available
- Node should run the client app from the container
- Everything should shut down with VS Code to free system resources for gaming
And I finally succeeded!
Step One: Get environment ready
I followed the official Microsoft guide for getting this pre-release environment installed. Most of the beta channel builds have filtered down since March 2020 so you might already be ready to go.
Step Two: Docker configuration
I use a mixture of Docker Compose and a Dockerfile to build the required system.
The Docker Compose configuration does a few things.
- Declares
www
service for running WordPress and Node - Declares
db
service for running MySQL 5.7 - Uses a
.env
file to preload MySQL and WordPress configuration options as environmental variables - Mounts a
delegated
volume underwp-content/plugins/my-plugin
so my work is available in WordPress - Defines a Docker Volume for persisting the database files and mounts it in
/var/lib/mysql
The Dockerfile mainly installs WordPress and XDebug. This was mostly lifted from the XDebug for Docker image.
I reopened my folder in the remote container and watched my first Docker containers building. I navigated to localhost
and saw the WordPress set up page – woo!
Step Three: WordPress dependencies
My React app relies on a few dependent WordPress plugins – mainly Meta Box and WPGraphQL (and of course my integration plugins!). I contribute to all of these, so I want to mount my local repositories in wp-content/plugins
. I did not want to mount these in a Docker volume as it was important to manage the files from the host.
I created a .docker
directory in my repository and ignored it in .gitignore
. This is where I will put anything to share between host and container. In .docker/wp-content
I cloned plugins
, themes
and uploads
required for my WordPress site to run. These were mounted as delegated
volumes for performance, but this is risky as container changes aren’t persisted on the host in real-time.
Finally, I added a ‘docker blocker’ tmpfs
volume mounted over wp-content/plugins/my-plugin/.docker
. This will hide the .docker
folder from the container. This is important for not seeing duplicate PHP files in XDebug etc. I set nocopy
and a max size of 0
so I am not tempted to put anything in there.
I really should set some environmental variables and clean this up!
Step Four: Node
At this point I had a working PHP development environment. At the time I was running Node in a second VS Code window on my host, while doing PHP in my container.
To migrate my JavaScript development into my Docker container I had to overcome a few issues:
- Create React App cannot hot reload in a container.
- Fixed with CHOKIDAR_USEPOLLING environmental variable.
- File permissions/poor performance of
node_modules
. This was harder to diagnose, I went through several iterations of trying to get this to work.- Running VS Code on a Windows directory (
C:\dev\src\my-repo
) would fail to compile in Docker. - Running VS Code from a WSL2 folder (
~/src/my-repo
) would compile but perform horribly. - Finally used a ‘docker blocker’
node_modules
volume.
- Running VS Code on a Windows directory (
I added the Node Source URI to apt-get
and installed nodejs
in my Dockerfile.
Step Five: Polish that dev experience
At this point I have WordPress and XDebug running with their dependencies. I also can run Node and view my React app alongside the deployed WordPress version. The only thing left is to improve the developer experience a little.
Things I want:
- Persist my
bash_history
- VS Code extensions (ESLint, IntelliPhense etc)
- VS Code settings
- Install a
.bashrc
file with some helper functions - Create a
share
directory to allow easy dragging and dropping of files into the container and vice versa
I created the .bashrc
file I wanted, mostly a duplicate of my WSL .bashrc
.
I saved this in my repository as .bashrc.docker
and used Dockerfile COPY
to sync it on container build. I also added a .gitconfig
file with some defaults and COPY
that to /root
too.
In my .docker
directory (remember this is ignored and hidden from the Docker container) I add mounts for .bash_history
and share
under /root
.
And finally, a .devcontainer.json
file for VS Code settings.
And – done!
Recap
So, once all is said and done this configuration allows me to run a custom WordPress development Docker container (with PHP, XDebug and NodeJS). You can have a custom .bashrc
deployed and your command history is persisted in .bash_history
.
There is a share
folder mounted under /root
to allow SQL dumps or other file movements on and off the container.
WordPress dependencies are kept on-host and mounted in /var/www/html/wp-content
.
WordPress is forwarded on port 80
and my React app is on 3000
.
ESLint is scoped to development directory to avoid surfacing linting errors in other plugins.
PHP debugging and intellisense extensions are installed on the Docker container – not your local VS Code.
And most importantly, there are no services or frameworks installed on my host, other than WSL2 and Docker for Windows. And all of these can be shut down with VS Code freeing up system resources for play time.