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.
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.
In Debian based linux distributions it might be enough just to do
sudo apt install docker docker-compose
Install PHP Composer
Clone the repository
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.
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/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>
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
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 -h localhost -u username -p --protocol TCP databasename > dump.sql
--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
bedrock/.env files per environment, with different passwords and salts. And don't forget to set
WP_ENV appropriately at
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)
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
- Automated tests, maybe something with phpunit and codeception