Professional Documents
Culture Documents
https://css-tricks.com/intro-bedrock-wordpress/ 1/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
Why should you care about Bedrock? Let’s take one step back and think about a typical
way to work on a WordPress project.
You can download and install WordPress both on your local machine and production
server. You can have a git repository for your theme. Deployment can be FTP transferring
of theme or git pulling the repo from the server. For some projects, you might want to
keep the entire site (even WordPress files) under git as well.
Here’s an example of what I mean. You update a plugin locally because you need some
new functionality that the updated plugin provides. You write code based on that new
functionality. Now it’s time to deploy. You:
1. Install or update the plugin on the production site (make sure it’s the same version!)
2. Compile assets
3. Update the theme on the remote server (using whatever deployment method)
4. If it’s a new plugin, make sure it’s been activated on the production site
https://css-tricks.com/intro-bedrock-wordpress/ 3/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
This is a rather manual approach and can be quite error prone. Most importantly, it can
cause downtime or fatal errors. For instance, you update the plugin and it’s not
backwards-compatible, so it throws errors until the deployment is finished. Or you deploy
first and the site throws errors until you update the plugin.
You’ll end up with a self-contained WordPress project that installs an explicit version of
WordPress and required plugins (composer install) which can be easily configured. All
configuration is kept in a .env text file) and deployed (cap production deploy).
Less troubles and more automation! Even onboarding a new developer is easier.
https://css-tricks.com/intro-bedrock-wordpress/ 4/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
You also get a multistage environment for free. You can have a development configuration
for working locally and, a staging configuration for tests, and a production configuration
for the live site. You can also define custom configuration, such as backup where you can
have a 1:1 copy of the production environment, on a different server.
(#requirements) Requirements
Here are the requirements for working with Bedrock:
https://css-tricks.com/intro-bedrock-wordpress/ 5/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
This is in addition to all the normal WordPress server requirements, like MySQL.
Keeping Bedrock up to date is certainly a good thing (improvements, security, etc) but it
can be tricky. You may need to hand-pick changes or try to merge two upstreams. Since it
is a starting point, downloading it means you can set it up once and forget about it.
(#installation) Installation
Now that you have downloaded the repo, you can initialize your own repository for the
project.
https://css-tricks.com/intro-bedrock-wordpress/ 6/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
unzip master.zip
rm master.zip
mv bedrock-master ~/Sites/example.dev
cd !$
git init
I keep all my web projects in the Sites folder in my home directory (I develop on OS X) and
keep them indexed by their name. I use the `.dev` TLD so I can configure nginx with
virtual hosts. This way, even when working locally, I have a configuration that’s almost
identical to the live website. (Bonus points compared to using localhost: you get a new
environment to work on: cookies, local storage, URLs for caching, etc are not shared with
any other site you previously worked on.)
On my machine, I use this minimal configuration to instruct nginx how to host the
website. Please note that the root folder isn’t the same as the project. Bedrock puts all
public files in the web folder.
nginx Configuration
server {
listen 80;
server_name example.dev;
root /Users/MJ/Sites/example.dev/web;
location / {
https://css-tricks.com/intro-bedrock-wordpress/ 7/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
location ~ .php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi.conf;
fastcgi_intercept_errors on;
fastcgi_pass unix:/tmp/php5-fpm.sock;
Command Line
composer install
As you can see by the output, Composer will install dependencies listed in the
composer.lock file, including WordPress itself2 (#footnote-2-down) .
Composer is a tool written in PHP that lets you manage PHP dependencies (just like Ruby
gems, npm modules, CocoaPods, etc). These days all languages have a dependency
manager.
You declare the packages you need in the composer.json file, then declare them with
constraints (https://getcomposer.org/doc/articles/versions.md) . Having constraints lets
you define how Composer will update them when you run composer update.
https://css-tricks.com/intro-bedrock-wordpress/ 8/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
For example, Bedrock requires an explicit version for WordPress (4.5 at the time of
writing) and any version
Every time you execute composer update Composer will try to update your packages to
the latest version allowed, and will write down that version number to the composer.lock
file.
Now, when you run composer install (just like you did earlier) it’ll install that specific
version no matter what, reading from the composer.lock file (This is a reason that using
Composer is very safe, just like gem and CocoaPods, and unlike npm, unless you use the
shrink-wrap feature).
If you now list the contents of the web and web/wp directory, you will see something you’re
familiar with: Composer has moved the wp-config.php file to web and all WordPress files
are in the web/wp folder. If you inspect that file you can understand why it’s there.
Command Line
cp .env.example .env
vim .env
https://css-tricks.com/intro-bedrock-wordpress/ 9/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
7. The URL for the admin page. Leave it as it is and you’ll access WordPress admin pages at
http://example.dev/wp. If you change this you’ll also need to move the wp folder contents
accordingly
8. Authorization keys and salts that you can generate online (https://roots.io/salts.html)
After you’ve saved the file you can proceed to install WordPress (it needs to create all the
tables in the database!). You can do this by visiting the website on http://example.dev/
or by using WP-CLI (http://wp-cli.org) :
We can commit what we did (git add . && git commit -m 'First commit').
1. Download Bedrock;
2. Run composer install;
3. Edit the .env file;
4. Install WordPress (automated with the wp core install … command).
If other developers later join the team, they’ll clone the project’s repo and start at step 2.
If we inspect the folder structure you can see how Bedrock organizes the files:
File System
.
├── config
https://css-tricks.com/intro-bedrock-wordpress/ 11/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
├── vendor
├── web
│ ├── wp
config: this is where you configure WordPress. These files can’t be accessed from the
Internet, because we set nginx to serve files from the web folder.
config/application.php: this file contains the usual WordPress configuration and is
intended to include base settings that are common to all environments.
config/environments/*: these contain environment-specific settings. For example in
production it disables errors output.
vendor: dependencies managed by Composer will be installed there, except WordPress
plugins and themes; if you inspect the composer.json file you’ll see that these kind of
packages will be moved in web/app/{mu-plugins,plugins,themes}/.
web: files included in this directory are publicly available
— only the files that are required are in the web folder (see config above).
web/app: this is the old wp-content folder. It’s been renamed to better reflect its content
(this also matches other frameworks conventions, such as Rails and Symfony). This is
where your plugins and themes will end up.
web/wp: the whole WordPress package. This should be put in vendor but can’t be
because of WordPress limitations.
https://css-tricks.com/intro-bedrock-wordpress/ 12/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
web/wp-config.php: this file is well-known, but in Bedrock it acts as a loader (it loads
settings from the config directory). It needs to stay here because WordPress core hard
codes paths.
The key aspect is this: if it’s something that’s sensitive (a password or an external service
access key) you should put that in the `.env` file and read that with the env('<name>')
function. Sensitive information will never be stored in your git repository (the `.env` file is
ignored by git) which is a huge benefit. It also allows you to define a different
password/key for each machine. Imagine if every developer has to generate their Twitter
consumer key or has to generate a new one for testing purposes: if the key is stored in a
file that’s tracked by git you have to remember not to add that file to the staging area.
.env configuration
[…]
DB_PASSWORD=<password>
[…]
https://css-tricks.com/intro-bedrock-wordpress/ 13/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
PHP
define('DB_PASSWORD', env('DB_PASSWORD'));
(#plugins) Plugins
If your plugin is available in the official plugin registry, you can install using Composer
and WordPress Packagist (https://wpackagist.org) , which is a Composer repository that
mirrors WordPress’ official plugin and theme registry.
https://css-tricks.com/intro-bedrock-wordpress/ 14/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
Command Line
composer require wpackagist-plugin/<name>
You can provide a constraint for the version (the previous command would use the ^
constraint (https://getcomposer.org/doc/articles/versions.md#caret) ):
Command Line
composer require wpackagist-plugin/memberful-wp ~1.0
You can achieve the same if you manually edit the composer.json file and run composer
install.
If your plugin isn’t available on the official registry (like custom or paid plugins), you will
need to put it in `web/app/plugins̀ and remove it from the ignore list:
Command Line
mv ~/Downloads/my-plugin web/app/plugins/
https://css-tricks.com/intro-bedrock-wordpress/ 15/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
Even better, if you have a git repository for the plugin, you can use Composer directly.
Add the following to the repositories array of the composer.json file:
composer.json
{
"type": "git",
The repository needs to contain a `composer.jsoǹ manifest file in the root, such as this:
composer.json
{
"name": "macstories/wp-push-plugin",
"type": "wordpress-plugin",
"license": "proprietary",
"require": {
"php": ">=5.5",
"composer/installers": "~1.0.12"
When everything above is set, you can run composer require macstories/wp-push-
plugin to install it. Composer expects to have at least one git tag on that repository.
Now we have the plugin in place, we can enable it. Head over the WordPress admin page
or once again use WP-CLI (http://wp-cli.org) :
https://css-tricks.com/intro-bedrock-wordpress/ 16/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
Command Line
wp plugin activate memberful-wp
This time we don’t need the wpackagist-plugin prefix, as WordPress doesn’t know about
Composer.
If plugins need to create files or folders, they should be ignored by git, adding them in the
.gitignore file. One example of this is WP Super Cache: it creates two PHP files
containing settings.
If plugins ask you to modify the wp-config.php file, move those new settings to
`application.php` (or the environment specific one) as explained earlier.
(#themes) Themes
Themes work the same way as plugins, with two differences:
If you use a theme that’s available on the official WordPress registry you can install it with
Composer:
https://css-tricks.com/intro-bedrock-wordpress/ 17/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
Command Line
composer require wpackagist-theme/activello
If you’re going to develop your own theme, create its folder under web/app/themes and
you’re ready to go. There’s no need to exclude it from the .gitignore file.
In your theme you can query the current environment and take advantage from that. For
example, on MacStories (https://www.macstories.net/) (a site I developed with Bedrock), I
don’t include the minified assets in development:
PHP
$main = '';
} else {
At the time of this writing, WordPress is at 4.5.2. Let’s say 4.5.3 version comes out. We
need to change the required version. You can edit the composer.json file and run
composer install or you can just issue the following command:
Command Line
composer require johnpbloch/wordpress 4.5.1
It will update the `composer.jsoǹ file (and also the lock file, since we upgraded a
dependency) and install the dependency.
Usually after each WordPress update there’s a database schema upgrade to be performed
which you can do by visiting the WordPress admin page. But that’s tedious, so WP-CLI
has a command to perform that:
There’s also another thing to keep in mind when upgrading the database schema: if you
use the web UI to do that, and the operation takes too much time, you may encounter
HTTP timeouts. WP-CLI skips the HTTP layer and performs the operations running PHP
from the command line, which doesn’t have any timeout issues.
https://css-tricks.com/intro-bedrock-wordpress/ 19/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
https://css-tricks.com/intro-bedrock-wordpress/ 20/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
Capistrano (http://capistranorb.com) is the de-facto tool used for deploys, and you can
use it to deploy WordPress sites.
There are plenty of guides on the Internet and many conference talks, but to sum it up:
Capistrano will connect to your server using SSH and then follow this flow
(http://capistranorb.com/documentation/getting-started/flow/) to update a repository on
the server and run tasks. You add hooks on every task to perform what you need.
It’s designed to work with SSH keys (so you don’t have to use passwords) and it should
connect to the server as an unprivileged user. It can issue sudo commands, but if you need
to do that, there’s probably something wrong with your setup. You can learn more about
this on the Authentication & Authorisation
(http://capistranorb.com/documentation/getting-started/authentication-and-
authorisation/) documentation page. I use SSH agent forwarding so Capistrano is able to
pull from the git host using my local SSH key.
https://css-tricks.com/intro-bedrock-wordpress/ 21/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
On the server, Capistrano works by keeping multiple directories in the directory you set as
the target of deploys:
├── releases
├── repo
└── revisions.log
├── shared
current: this is a symlink to the current version deployed (either the latest one or a
previous one if you rolled back).
releases: every time you deploy Capistrano will create a new folder with a timestamp.
You can configure the number of releases to keep, and Capistrano will clean up old
releases (defaults to keeping 5 releases).
repo: Capistrano keeps the repository on the server and checkouts the correct revision on
the server — nothing is transferred from your machine to the remote server.
revisions.log after every deploy or rollback this log will be updated.
shared: contains files and folders that must be shared between releases, such as user
uploads.
Each deploy is run in isolation and has its own folder in the releases folder. If something
goes wrong, nothing happens to the current version. When it’s done, Capistrano will
https://css-tricks.com/intro-bedrock-wordpress/ 22/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
In your project you’ll have a Capfile that requires the tasks to run (see the example from
the flow (http://capistranorb.com/documentation/getting-started/flow/) ) and a
deploy.rb file that contains settings.
The only requirement is that you run composer install on the server, so Composer can
pull down WordPress, plugins, and any other dependency your site needs. Otherwise
you’ll have a sort of zombie website with just application code but not the application
itself.
The Roots team (the team behind Bedrock) created a separate repository
(https://github.com/roots/bedrock-capistrano) to host the Capistrano integration for
Bedrock. This is because Capistrano is not required by Bedrock but can be added at any
time.
Create or edit a Gemfile file in the root of the project and paste this content:
https://css-tricks.com/intro-bedrock-wordpress/ 23/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
Gemfile
source 'https://rubygems.org'
group :deployment do
gem 'capistrano-composer'
gem 'capistrano-wpcli'
end
Now run bundle to have it install the gems. This differs a bit from Roots’ setup, because we
grouped these gem in the deployment group and also installed the WP-CLI extension.
Command Line
git add 'Gemfile*' && git commit -m 'Install Capistrano'
Now we need to set up Capistrano. Capistrano’s configuration will be kept in the git repo.
You have to copy the Capfile file and the contents of the config directory from the
https://css-tricks.com/intro-bedrock-wordpress/ 24/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
Since we also installed the WP-CLI plugin, we have to require it in the Capfile. Add the
following line after we required the Composer plugin:
Capfile
require 'capistrano/wpcli'
Edit the config/deploy.rb and set the :application and :repo_url variables and remove
the :deploy_to line. If you want, you can also change other settings here. There are many
code comments and the documentation (http://capistranorb.com/documentation/getting-
started/configuration/) is great.
The :linked_files and :linked_dirs variables are important. If you recall from earlier,
Capistrano will create on the server a shared folder (shared between deploys). By default,
we’ll share the `.env` file and the `web/app/uploads̀ folder.
The `.env` file contains application settings and needs to be shared for the site to work,
and also because this file isn’t under version control. The `web/app/uploads̀ folder is the
old `wp-content/uploads̀ folder and needs to be shared because otherwise uploads would
exist only within a release.
https://css-tricks.com/intro-bedrock-wordpress/ 25/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
You can probably ignore the rest of the config/deploy.rb file. After line 22 it defines two
custom tasks. The first is empty and disabled by default. It can be used for example to
restart the web server. If you have something to restart after a deploy, update the task and
enable it.
The second task has been added by the roots team and is disabled by default. It’s used
update the stylesheet_root and template_root options of WordPress. I’ve never had a
reason to change them.
You have to update the server setting, because the production env is likely to be on a
different server than the staging one.
We’ll move the :deploy_to path setting here, as your staging path is probably different
from the production one. You can also override settings set in the main `deploy.rb` file.
The rationale of these files is the same as Bedrock environment files.
Now we configured Capistrano and everything should work. Capistrano comes with a
check task that, well, checks that everything works.
Command Line
bundle exec cap staging deploy:check
https://css-tricks.com/intro-bedrock-wordpress/ 26/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
Capistrano will:
1. Log in on the server with the user you defined (so you’re sure the SSH connection works)
2. List repository remote files (so you know Capistrano is able to connect and authenticate
with the git host)
3. Create the directory structure
4. Create linked folders
5. Check for linked files
"
ERROR linked file /path/to/:deploy_to/shared/.env does not exist on <host>
Push your changes (otherwise Capistrano will clone an empty or old repository) and run
the following to perform a real deploy:
Command Line
bundle exec cap <stage> deploy
https://css-tricks.com/intro-bedrock-wordpress/ 27/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
This is the command that you’ll need to remember and run every time you’ll want to
deploy your site.
Your web server needs to serve contents from the current folder, so if locally you had this
this setting for nginx:
nginx Configuration
root /Users/MJ/Sites/example.dev/web;
nginx Configuration
root <value of :deploy_to>/current/web;
https://css-tricks.com/intro-bedrock-wordpress/ 28/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
Installing WordPress on the remote server is just a matter of doing the same things you
did on your local machine. You can use the web UI or use WP-CLI, even from within
Capistrano.
When you deploy a new version and in that version you install a new plugin, you can use
the following command to enable the plugin:
Command Line
bundle exec cap <stage> wpcli:run['plugin activate <name>']
Command Line
bundle exec cap <stage> wpcli:run['core update-db']'
(#hooks) Hooks
You can define custom tasks for Capistrano to run, but before doing so, search for an
existing solution. Capistrano comes with many official plugins, like Bundler
(https://github.com/capistrano/bundler/) , npm (https://github.com/capistrano/npm/)
and many other community driven plugins
(http://capistranorb.com/documentation/third-party-plugins/) .
https://css-tricks.com/intro-bedrock-wordpress/ 29/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
For example, say you have a build script that compiles Sass assets. Because of the benefits
we talked earlier, you should require Sass in the Gemfile with:
Gemfile
gem 'sass', '~> 3.4'
(#capistrano-bundler) Capistrano-bundler
The first step is easy to achieve: we’ll require the capistrano-bundler gem by adding this
line to the :deployment group:
Gemfile
gem 'capistrano-bundler', '~> 1.1.2'
After that you run bundle to install the gem and also edit the Capfile to require it:
Capfile
require 'capistrano/bundler'
Done. Every time we deploy, Capistrano will run bundle install on the server, after the
deploy:updated task.
https://css-tricks.com/intro-bedrock-wordpress/ 30/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
deploy.rb
set :bundle_without, %w{deployment development test}.join(' ')
Doing so will exclude those groups of gems from being installed. Bundler will install less
gems (that we won’t need!) and our deploys will be faster.
We can create a custom task easily. Create your .cap (e.g. make.cap) file in
lib/capistrano/tasks to have it automatically loaded (otherwise you’d have to explicitly
require from the Capfile) with this content:
Capfile
namespace :make do
task :all do
on roles :all do
within release_path do
execute :make
end
end
end
https://css-tricks.com/intro-bedrock-wordpress/ 31/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
end
Capistrano will run our task before the deploy:updated task. Since we used the within
release_path do … bit, this command will be executed in the correct
release/<timestamp> directory that Capistrano is deploying.
You can add a new stage if you need to. An example could be a backup environment that is
100% equal to the production one. The database could be synced periodically or you
https://css-tricks.com/intro-bedrock-wordpress/ 32/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
could setup MySQL replica functionality. In case of failure you’d update your DNS to point
to the different host and nobody will notice anything.
If you have an existing WordPress site and want to convert it to Bedrock, you can treat it
as if it was a new project:
https://css-tricks.com/intro-bedrock-wordpress/ 33/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
1. Install Bedrock
2. Configure WordPress
3. Restore your theme
4. Install plugins with Composer
If you didn’t modify WordPress core files you’re done. If you modified WordPress core
files you’ll have to keep your custom WordPress version under version control (you’ll lose
easy-Composer-driven WordPress updates, but still have every other feature).
1. Remember that if [something] created [something else], you shouldn’t manage it. This
includes for example: files created by plugins, files installed by a package manager, files
uploaded by users, etc — don’t add them to git. If you ignore them, there’s also an high
chance they should end up in the linked_files setting of Capistrano.
2. Don’t store sensitive information in git.
3. Don’t forget to git push before deploying: I’ve deployed multiple times the same version
before realizing that I forgot to push (on the server Capistrano was pulling the same
version over and over again).
https://css-tricks.com/intro-bedrock-wordpress/ 34/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
4. Use WP-CLI whenever possible: it’s faster and can be executed by Capistrano. Examples
include:
wp plugin activate <name>
wp core update-db
(#recap) Recap
I hope you followed and enjoyed this tutorial. I encourage you to create a new site on your
local machine to have something to experiment with.
Self-contained repository
https://css-tricks.com/intro-bedrock-wordpress/ 35/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
Easy deploys
Easy multi-stage support
More consistency between environments
Separation between configuration files and everything-else
Congrats on making it through this tutorial. Sorry if it was long and difficult. It’s definitely
not for beginners! Hopefully along the way you learned some new tools and leveled up
your understanding of all the different moving parts.
The benefits of working like this are well worth it, in my opinion. Now go create
something nice!
[^1] (#footnote-1-up) : That’s not because you can’t ssh <user>@<host> (some hosts
permit that) but because you can’t install some of the required software — if you’re using a
script that runs on the server to build the assets you’re probably fine.
[^3] (#footnote-3-up) : In fact, you can deploy whatever you want with Capistrano: you’re
not constrained to web apps, you could deploy iOS apps to a remote server, if that makes
https://css-tricks.com/intro-bedrock-wordpress/ 36/37
9/20/21, 7:21 AM An Intro to Bedrock for WordPress | CSS-Tricks
sense to you.
[^4] (#footnote-4-up) : Later you’ll see cap commands prefixed by bundle exec: doing so
ensures that the local gem is used instead of a globally installed one (which might not
exist). You should do the same for Sass or any other gem.
https://css-tricks.com/intro-bedrock-wordpress/ 37/37