Building a fast WordPress development stack with Docker, Bedrock and the Rocketstack
Improve your WordPress coding practices

Are you a WordPress developer or are you thinking of becoming one? Have you heard that WordPress is not amenable to modern development and is that bothering you or even stopping you from using such an amazing tool? Well, it turns out that indeed, WordPress by itself has some design choices that go against programming best practices, but being all open source, it’s always possible to adapt it with some work.
The fellows at Roots have been giving this problem a serious thought and came up with several solutions. Chief among them is Bedrock, a way of using WP and its plugins as managed dependencies with Composer, which feels a lot more like normal development. If you heard about the 12 Factor App, then Bedrock tries to move WP in that direction.
I knew Bedrock for some time, but never gave it a try, until a few weeks ago, when I was asked to do a WordPress site. I decided to do it ‘the right way’ and that would include Bedrock.
Right away I hit a problem though. The community at Roots has created a very good and fast server that you can use both in development and production, hence approaching development-production parity, one of the tenants of the 12 Factor App. This server environment it’s called Trellis, and works with Vagrant.
Don’t get me wrong, Vagrant it’s also a great tool. But I’m more of a Docker guy now, so I thought I would find a similar server environment in Docker. Easy piece, right? Somebody must have built one. Well, probably somebody had, but after a few days of searching I couldn’t find the exact fast, production ready, Docker-based server I wanted. After a while, I remembered the ‘Rocketstack’ and decided to dockerize it.
The Rocketstack is a software stack proposed by David Hilditch at this article to serve WP sites fast. A couple of years ago (feels like centuries) I was using the Rocketstack as a local development server under, you guess it, Vagrant, and I really loved the speed of it.
Piece by piece, I included the elements of the Rocketstack in the Docker configuration until everything worked. A few changes were needed for Bedrock because the Rocketstack works with standard WordPress which has a different directory distribution.
At the end I was really pleased with the results, so much that I really want to share the stack with as many people as would listen (even my mom). It’s fast, it’s elegant, it deploys very quickly and easily with docker-compose, and the fact that you have Composer means you can use any PHP dependencies you want within your code! I called it ‘Wordflow’ cause I’m clever with words (‘WordPress’ + ‘Flow’, bet you didn’t see that coming). And thanks to my fantastic sales skills, you should be dying to try it. Keep reading then.
Installation
Now let’s explain how to get you started with the stack. It could be as quick as 10–15 minutes if you have a fast network and everything goes without error.
Docker
Install Docker and docker-compose
In Debian based linux distributions it might be enough just to do
sudo apt install docker docker-compose
PHP dependencies
Install PHP Composer
Clone the repository
git clone https://github.com/ArmandoRiveroPi/wordflow
Install the dependencies with Composer
cd bedrock && composer install
Launch the containers
There are two .env
files you'll need to create before you run your WordPress site, one for Docker and the other for Bedrock.
You will see in the root directory a .env.example
file. You should copy it into a .env
file and edit it. When you create your MySQL Docker image, Docker will use the parameters in this .env
file to set the root password and to create a new MySQL user that will own the database intended for WP. In my opinion, the data should go into an external Docker volume, otherwise it will be lost forever if you do docker-compose down
. Once you create the MySQL image and put the database into the volume, you cannot change the credentials simply by editing the .env
file, though. You'll either need to destroy the volume an rebuild the image, or change the credentials inside MySQL with an ALTER USER
query.
I called the volume wordflow_data
but you can name it any way you want, just remember to edit the docker-compose.yml
accordingly. You can create the volume with
docker volume create --name=wordflow_data
After you have the volume, move into the bedrock
directory, there should also be an bedrock/.env.example
file you can copy into bedrock/.env
. You'll see the usual WP stuff like database credentials and the security salts that you need to fill. Also, you'll see the parameter WP_ENV
which is part of the Bedrock genius, because you can use it to separate production, staging and development environments. Anywhere within your code you can check for instance if(defined('WP_ENV') && WP_ENV === 'production')
and you can take special actions per environment.
At this point you can build the Docker images running this
cd <directory with docker-compose.yml>
docker-compose up -d
It should take a few minutes to download the base images and build them.
If you didn’t get any errors, your site should be available in http://localhost. A nice trick, of course, is to edit your /etc/hosts
file (or the Windows equivalent) so you can develop using a pretty url like http://mygreatsite.dev. HTTPS is also enabled, but with a self signed certificate that you'd need to explicitly accept in your browser.
The first time you open the URL you will be welcomed by the WordPress “famous 5 minutes installation” screen.
One of the surprises Bedrock will give you is that the admin url is at http://localhost/wp/wp-admin instead of http://localhost/wp-admin.
How to use
So, now you have the site running and you’re eager to start developing? This is how you go
Developing themes and plugins
Bedrock uses a web
directory with two subdirectories: bedrock/web/wp
and bedrock/web/app
. The first, wp
is for the default WordPress installation an is managed by Composer. You shouldn't touch this. Composer will erase it when upgrading WP versions. Instead, your code should go in bedrock/web/app
, which is the WordPress content directory (what will usually go into wp-content
). Just put your themes into app\themes
and your plugins into app\plugins
and start coding.
You’ll also notice that the plugins you install with Composer (I’ll explain how bellow) go into app\plugins
as well. These plugins are also managed by Composer and shouldn't be touched. In general you leave all installations and upgrades of PHP packages up to Composer to make your stack reproducible in all hosts.
Install a plugin, theme or a different version of WordPress
You might know the way Composer works. You have a composer.json
that describes the version of each dependence that you want. When you change this file, you can run
cd <to the folder with your composer.json>
composer update
This will put your dependencies exactly in the state described by composer.json
installing what's needed and removing what's not.
To install plugins or different WordPress versions you need to include them in this section of the json
"require": {
"php": ">=7.1",
"composer/installers": "^1.8",
"vlucas/phpdotenv": "^4.1.8",
"oscarotero/env": "^2.1",
"roots/bedrock-autoloader": "^1.0",
"roots/wordpress": "^5.7",
"roots/wp-config": "1.0.0",
"roots/wp-password-bcrypt": "1.0.0",
"rhubarbgroup/redis-cache": "^2.0",
"wpackagist-plugin/nginx-cache": "^1.0"
}
The line that describes the WP version is "roots/wordpress": "^5.7",
. The expression ^5.7
means the latest 5.7.x
version. When you run composer update
even if you don't touch that line, Composer will check if there's a newer 5.7.x
WordPress version in the Roots repository and will install it. This is recommended because minor versions usually bring bug fixes and security patches that you want to have ASAP. Also minor updates should never break your site. However, keep in mind that dependencies changes, like code changes, should always flow from development to production, testing them first locally before pushing them to the live site.
The plugins come courtesy of WP Packagist, a great project that makes WordPress themes and plugins available as Composer packages. For instance, if you wanted to install Ninja Forms, you’d (1) find the plugin slug ninja-forms
and the latest version ^3.5
in the WP plugin directory, (2) add it to your requirements section "wpackagist-plugin/ninja-forms": "^3.5"
(3) run composer update
and (4) activate the plugin in the WP admin interface. When you use Bedrock, DO NOT EVER install a plugin directly via the WP admin.
Dump the database
You can use any tools you want, maybe a general purpose SQL client. I’ll just leave you here the native MySQL way through the command mysqldump
mysqldump -h localhost -u username -p --protocol TCP databasename > dump.sql
The --protocol
parameter is important because, although your database is accessible at localhost, it's not directly running in the host OS as MySQL would expect and hence cannot be accessed through a system socket.
Have different configurations per environment
You have probably seen the bedrock/config
folder. The application.php
file there is a configuration file where you can set constants and do whatever needed to dictate the behavior of your application. bedrock/config/application.php
is always executed regardless of your WP_ENV
, but the files in bedrock/config/environments
are environment-dependent, for instance development.php
will only be executed when WP_ENV == 'development'
and so on. Hence, you can have your PHP configuration files per each environment there. For the sake of security, you should also have different .env
and bedrock/.env
files per environment, with different passwords and salts. And don't forget to set WP_ENV
appropriately at bedrock/.env
.
Do it yourself
I’m far from a genius, so if I could do it, you can also create your dockerized high performance WordPress stack, and it might be even better. I would recommend to read the Rocketstack article and also read about Bedrock. On the other hand, if you just want to make a tweak to Wordflow, I’d be more than happy to listen to you. Please, create an issue, pull request or open a discussion at the github repo or just leave your comment here and I’ll get in touch. This is actually my first open source code that might be used by somebody (it’s so coooool)
Still missing
This project is still very fresh and will need a lot more work to support more features and gain robustness. Just a few things from the top of my head:
- SSL with Letsencrypt (there are some instructions in the Rocketstack article). It’s easy to change the nginx configuration to use the certificates you have, but I still need to figure out how to generate them automatically and renew them with Letsencrypt
certbot
. - Automated tests, maybe something with phpunit and codeception