Roots Bedrock: The WordPress Boilerplate That Treats Core Like a Dependency

Composer-managed core, environment-specific config, and a folder structure that makes WordPress behave like a modern application instead of a pile of mutable files.

8 to 10 min read • View on GitHub • More from roots

A cutaway of a WordPress project shown like a small building with a public front entrance, hidden interior rooms, and a separated utility wing. It explains that Bedrock moves the web root inward and treats core, config, and dependencies as distinct layers instead of one exposed folder tree.
Bedrock redraws the boundary between public files and application code, then puts Composer in charge of what belongs inside it.
Key Takeaways

Bedrock is the rare WordPress project that changes the question. It is not asking how to make WordPress prettier. It is asking how to make WordPress behave like software a team can ship, lock, test, and run with fewer surprises.

That shift starts with a simple but radical idea: WordPress core is not the center of the project. It is a dependency. Once you accept that, the rest of Bedrock’s structure begins to make sense.

The move that changes everything

A standard WordPress install puts too much trust in mutable folders. Bedrock moves the public document root to web/, places WordPress core in web/wp/, and relocates content into web/app/. The result is a project that separates what the browser can touch from what the team should control.

That is not just tidier. It changes the security posture, the deployment model, and the mental model. In Bedrock, the site is no longer a loose pile of files. It is an application with boundaries.

A close-up editorial scene of packages moving along a conveyor into three destinations, with a gate for environment selection and a side bridge for must-use plugins. It explains how Bedrock routes dependencies and config into runtime behavior without manual copying.
Bedrock turns installation into a deterministic flow from declared dependency to runtime location.

WordPress, but managed like PHP software

Composer is the engine room. Bedrock uses it to install WordPress core, plugins, and themes as explicit packages, then maps them into the folder structure with installer paths. That gives teams version locking, repeatability, and a clean upgrade path instead of hand-managed downloads.

This matters because the usual WordPress workflow is still optimized for manual administration. Bedrock replaces that with a software delivery workflow. A site becomes something you can provision the same way on every machine, every branch, and every environment.

The important thing in Bedrock is not any single file. It is the sequence that turns environment data, dependency declarations, and autoloaded plugins into one predictable app.

Config becomes code, but less fragile

Bedrock replaces the usual sprawl of wp-config.php hacks with config/application.php, .env, and environment-specific files in config/environments/. That gives configuration a proper hierarchy. Base settings live in one place, environment overrides in another, and secrets stay out of version control.

The small detail that matters most is type handling. Bedrock leans on environment parsing that can convert booleans correctly, which avoids the classic bug where the string "false" behaves like truthy PHP text. Tiny safeguards like that are what make configuration systems trustworthy.

Bedrock now requires PHP 8.3+. PHP 8.1 reached end of life in December 2025, and setting 8.2 as the floor would have given less than a year before another bump.

The point is not just modern syntax. The point is a configuration layer that behaves predictably when real teams swap local, staging, and production settings without rewriting the app.

The mu-plugin escape hatch

Must-use plugins are one of WordPress’s awkward corners. They are powerful, but they do not fit neatly into standard plugin management. Bedrock’s autoloader solves that by making critical plugins composable while keeping them always on.

That is a quiet but important engineering move. It lets teams treat essential integrations like code, not admin clicks. If a logging plugin, security layer, or storage integration matters to the runtime, it should not be one dashboard toggle away from disappearing.

<?php

// Bedrock's mu-plugin pattern keeps critical code always loaded.
// Composer installs the package, and the autoloader bridges it into WordPress.

require_once __DIR__ . '/../../../../vendor/autoload.php';

// The actual plugin file is resolved through the Composer-managed stack.
// This keeps must-use functionality in version control and out of manual admin drift.

Why the folder structure is the security story

Bedrock’s file layout is also a hardening layer. Moving application files away from the web root narrows what can be exposed if someone probes the server. Renaming the standard content path to app/ also breaks a lot of canned attack scripts that assume the default WordPress layout.

That should be read as hardening, not magic. Bedrock does not make a site invulnerable. It does reduce the amount of sensitive surface area sitting in the obvious place, which is exactly what a sane baseline should do.

ConcernClassic WordPressBedrock
Core locationUsually downloaded into the site rootInstalled as a dependency under web/wp
ConfigurationSingle wp-config.php with ad hoc editsconfig/application.php plus .env and environment overrides
Dependency managementManual updates or dashboard installsComposer-managed packages and locked versions
Web root exposureMore files exposed directly to the public rootPublic surface isolated under web/
Plugin managementOften mixed between admin and filesystemComposer installs plus autoloaded mu-plugins
Environment parityEasy to drift between local and prodSame codebase, environment-specific config
CI/test friendlinessHarder to standardizeBuilt for automation and repeatability
Security postureRelies heavily on conventionsReduces exposure with structure and defaults

The contrast is blunt. Classic WordPress is built around manual administration. Bedrock is built around teams, automation, and reproducibility.

Bedrock will be getting a major version update 💥 We'll be making changes to the directory structure and configuration system to better align with modern PHP practices.

retlehs, Member / Assignee · Bedrock v2 · Issue #764 · roots/bedrock

What changed Bedrock from boilerplate to platform convention

The repo’s recent maintenance tells you a lot. Bedrock now targets PHP 8.3, uses Pest for testing, ships with modern code style tooling, and supports devcontainer-based local development. That is not the behavior of a frozen starter kit. It is a living platform choice.

This is where Bedrock stops looking like an old workaround and starts looking like an institutional norm for serious WordPress teams. The project has absorbed the expectations of modern PHP without losing its original purpose.

Bedrock versus classic WordPress

The practical difference is easy to state and hard to ignore. Classic WordPress assumes the dashboard is where everything happens. Bedrock assumes the repository is where the system is defined.

DimensionClassic WordPressBedrock
Install processDownload and unpackComposer install
Core updatesManual or dashboard-drivenDependency-managed
Secrets handlingOften mixed into config filesStored in .env and loaded at runtime
Deployment disciplineAd hocRepeatable and CI-friendly
Default structureStandard wp-content layoutweb/, web/wp/, web/app/
Team workflowAdmin-centricDeveloper-centric
Testing setupUsually added laterExpected from the start
Operational modelMutable filesDeclarative dependencies

If you run a solo site and like the dashboard to be the center of gravity, Bedrock is probably more structure than you need. If you run WordPress like software, it is the right kind of structure.

That is the whole argument. Bedrock does not reinvent WordPress. It disciplines it.