2016-05-17

The following is a guest post by Alessandro Vendruscolo, who wrote to me excited to write a guest post about a WordPress tool that I didn't know much about: Bedrock. It's not a theme, it's a way to install, configure, and manage WordPress with security and modern development practices in mind.

Bedrock is an open source project made by Roots that you use as base for WordPress projects.

Why should you care about Bedrock? Let's take one step back and think about a typical way to work on a WordPress project.

Classic WordPress Workflow

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.

This has issues:

Updating WordPress/plugins from production may break your production site

You have to manually keep track of WordPress/plugin versions

Deploys can be cumbersome

Configuring WordPress is difficult, because the wp-config.php can't be under version control

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:

Install or update the plugin on the production site (make sure it's the same version!)

Compile assets

Update the theme on the remote server (using whatever deployment method)

If it's a new plugin, make sure it's been activated on the production site

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.

Enter Bedrock

If the issues above sound like something you have dealt with in the past, you'll like Bedrock. It brings:

Explicit dependencies tracked by Composer

Easy configuration with phpdotenv

Easy one-command deploys with Capistrano

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.

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

Here are the requirements for working with Bedrock:

You should be comfortable working on the command line.

You need shell access to the server: if your site is hosted on shared host you're probably out of luck1.

PHP >= 5.5

You need Composer on the server (install instructions).

You need to have a working Ruby and gem environment on your machine to perform deploys.

You need to have WP-CLI installed locally and on the server if you plan to use it. It's not required, but you'll see later that's useful (install instructions).

This is in addition to all the normal WordPress server requirements, like MySQL.

Getting Started

Let's assume a brand new WordPress project. First, you'll need a copy of Bedrock, which you can get from its repository.

Should I clone or download the repo?

Bedrock's README tells you to clone the repository, but in my opinion you're better off downloading it. If it's something that you plan to regularly keep up to date, go for the clone route; if it's something that you plan to set-and-forget, go for the download route.

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

Now that you have downloaded the repo, you can initialize your own repository for the project.

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.

Now we need to run a command to let the magic happen:

As you can see by the output, Composer will install dependencies listed in the composer.lock file, including WordPress itself2.

So, what's happening?

If you're new to Composer, I'll quickly explain what it is and how it works.

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. Having constraints lets you define how Composer will update them when you run composer update.

For example, Bedrock requires an explicit version for WordPress (4.5 at the time of writing) and any version

greater than 2 but less than 3 for phpdotenv.

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.

We've everything in place, it's now time to configure WordPress.

Copy the `.env.example` to `.env` and edit it:

It asks you for:

The name of the database you use (I'll use example_development)

The username that will be used to connect to the database (on my machine I have just one root user)

The password of that user

The host of the database (127.0.0.1 or localhost is fine)

The environment of this installation (development is fine, you can set whatever you want: usually you have development, staging, and production)

The homepage URL that WordPress will use to build URLs among other things (I set http://example.dev)

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

Authorization keys and salts that you can generate online

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:

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.

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.

Configuration and environment variables

As we saw earlier we have multiple configuration files under the `config` directory, and we also have the `.env` file where we put the username/password for the database.

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.

The solution is to add it to the .env file:

and read that value in the configuration file:

Remember that environment-specific files (`config/environment/
.php`) are required before the main (`config/application.php`). This means that you can't override settings. Say that you have the same configuration for development and staging but a different one for production. You can:

define it in every environment file (`development.php`, `staging.php`, `production.php`, etc).

put it in the `.env` file and define it in the main application (`application.php`) file.

Plugins

If your plugin is available in the official plugin registry, you can install using Composer and WordPress Packagist, which is a Composer repository that mirrors WordPress' official plugin and theme registry.

Bedrock already added wpackagist's repository, so installing a plugin is just a matter of running the following command to install the latest version:

Plugins are prefixed with wpackagist-plugin/ so the plugin Memberful WP becomes wpackagist-plugin/memberful-wp.

You can provide a constraint for the version (the previous command would use the ^ constraint):

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:

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:

The repository needs to contain a `composer.json` manifest file in the root, such as this:

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:

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 work the same way as plugins, with two differences:

Their directory is web/app/themes;

Composer's vendor name is wpackagist-theme.

If you use a theme that's available on the official WordPress registry you can install it with Composer:

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 (a site I developed with Bedrock), I don't include the minified assets in development:

Another difference is analytics. I only include Mint Analytics in production, using an approach similar to above. Google Analytics is available even in development or staging, but these two environments use a different site ID, so it's easier to perform tests.

Upgrading WordPress

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:

It will update the `composer.json` 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.

Upgrading plugins

Upgrading plugins is similar to upgrading WordPress: if you used Composer to install them, use Composer to upgrade them.

You can run composer require wpackagist-plugin/<name> <version> to upgrade a given plugin to a specific version or, even better, use composer update to update every package to their latest version that satisfies the constraints set in the `composer.json` file.

When you composer require <package> something (without specifying a version), Composer will by default use the ^ constraint: running composer update will update the package to the latest version that's not a new major version. Easy peasy.

Deploying your project

At this point you should have a working WordPress site on your machine. But that's almost useless, unless your machine is on the Internet.

Capistrano is the de-facto tool used for deploys, and you can use it to deploy WordPress sites.

What is Capistrano?

Capistrano is a powerful tool written in Ruby used mainly3 to perform deploys. Originally it was born to deploy Ruby on Rails web applications, but then it grew to deploy any kind of web application, with plugins to run custom tasks or connect to external services.

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 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 documentation page. I use SSH agent forwarding so Capistrano is able to pull from the git host using my local SSH key.

On the server, Capistrano works by keeping multiple directories in the directory you set as the target of deploys:

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 update the symlink to the version that was just deployed.

In your project you'll have a Capfile that requires the tasks to run (see the example from the flow) and a deploy.rb file that contains settings.

If you don't want to use Capistrano

Bedrock supports Capistrano, but it's not required. If Capistrano doesn't compel you, or you prefer to use a different tool, or prefer to use no tool at all, you're free to do so.

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.

Deploying with Capistrano

OK, I convinced you to try Capistrano, let's go on!

The Roots team (the team behind Bedrock) created a separate repository 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:

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.

Capistrano will be installed by Bundler and be treated as a dependency just like we did with WordPress and Composer earlier. This is useful because every project can use a different version of Capistrano and will not depend on a globally installed one. Also, if there are multiple developers on the team, a locally installed (and synchronized) version of Capistrano ensures that everything will be as smooth as possible4.

Commit both `Gemfile` and `Gemfile.lock` - just as with Composer:

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 repository. This is the equivalent of running bundle exec cap install and editing Capistrano's configuration files.

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:

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 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.

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.

Edit the `config/deploy/<stage>.rb` file to set per-stage settings.

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.

Run the following command to perform the check:

Capistrano will:

Log in on the server with the user you defined (so you're sure the SSH connection works)

List repository remote files (so you know Capistrano is able to connect and authenticate with the git host)

Create the directory structure

Create linked folders

Check for linked files

Here's an example of a Capistrano error we might see:

ERROR linked file /path/to/:deploy_to/shared/.env does not exist on <host>

Create that file (you can use scp .env <user>@<host>:<value of :deploy_to>/shared/.env to upload your development `.env` file to the server. At this time, we're interested in having the file, not having the correct file) and when you're done check again that everything works. Hooray it works!

Push your changes (otherwise Capistrano will clone an empty or old repository) and run the following to perform a real deploy:

This is the command that you'll need to remember and run every time you'll want to deploy your site.

Configuring the remote WordPress

You will have to update the `.env` file on the remote server to set the correct database information and, most importantly, the WP_ENV and WP_HOME settings.

If you deployed to production that must be WP_ENV=production (I don't know your WP_HOME value).

Your web server needs to serve contents from the current folder, so if locally you had this this setting for nginx:

on the server you'll have to update it in this way:

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:

To perform database migrations:

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, npm and many other community driven plugins.

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:

Now we need to do two things when we deploy:

Ensure the sass gem is installed

Run our own build script

Capistrano-bundler

The first step is easy to achieve: we'll require the capistrano-bundler gem by adding this line to the :deployment group:

After that you run bundle to install the gem and also edit the Capfile to require it:

Done. Every time we deploy, Capistrano will run bundle install on the server, after the deploy:updated task.

If you want, set the :bundle_without variable in the `deploy.rb` file:

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.

Running custom scripts

I don't know how your build script is structured. I'm a fan of make, but you can have anything you want: a shell script, npm scripts, Grunt/Gulp tasks, etc. No matter what, we have to run them, otherwise our site will be without assets.

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:

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.

Check out Capistrano's documentation for more. Hot tip: be sure to use the colon before any argument (it has to do with shell escaping).

Adding a new stage

Bedrock comes with three default environments:

Development (Bedrock only)

Staging (Bedrock and Capistrano)

Production (Bedrock and Capistrano)

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 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.

To add a new stage:

Create a new environment for Bedrock: `config/environment/backup.php` (you can copy an existing one and update what's needed)

Create the Capistrano stage configuration: `config/deploy/backup.rb` (again use an existing one as a base)

Setup the server (web server, database, etc)

Deploy the project (bundle exec cap backup deploy:check and bundle exec cap backup deploy)

Be sure the `.env` file contains WP_ENV=production

Migrating an existing project to Bedrock

In this tutorial we created a new project, but Bedrock can be used with existing projects.

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:

Install Bedrock

Configure WordPress

Restore your theme

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).

Gotchas and tips

Some tips:

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.

Don't store sensitive information in git.

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).

Use WP-CLI whenever possible: it's faster and can be executed by Capistrano. Examples include:

wp plugin activate <name>

bundle exec cap <stage> wpcli:run['plugin activate <name>']

wp core update-db

bundle exec cap <stage> wpcli:run['core update-db']

You can set Capistrano branch (config/deploy.rb) to set :branch, ENV['BRANCH'] || :master: doing so lets you easily deploy a branch by doing BRANCH=my-feature bundle exec cap <stage> deploy (remember to push, see tip #3); if you don't provide the BRANCH=<branch> environment variable, Capistrano will fall back to deploying master.

Using Slackistrano you can post to a Slack channel after every successful deploy.

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.

To recap, these are the advantages that Bedrock brings:

Self-contained repository

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]: 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.

[^2]: Unfortunately at this time it doesn't exist an official version of WordPress distributed via Composer. Bedrock uses this fork that syncs every 15 minutes.

[^3]: 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 sense to you.

[^4]: 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.

An Intro to Bedrock for WordPress is a post from CSS-Tricks

Show more