Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.frankenpress.com/llms.txt

Use this file to discover all available pages before exploring further.

site-template is a GitHub template repo for new WordPress sites running on the FrankenPress stack.

Layout

Bedrock-style:
.
├── composer.json              # site dependencies (no WC, no theme picks)
├── Dockerfile                 # multi-stage: composer build → runtime
├── docker-compose.yml         # local dev: site + mariadb + redis + minio
├── .env.example               # all platform env vars with sane defaults
├── config/
│   ├── application.php        # main config + lockdown constants + env wiring
│   └── environments/
│       ├── development.php    # WP_DEBUG=true, indexing disabled
│       ├── staging.php        # WP_DEBUG=true (log only), indexing disabled
│       └── production.php     # WP_DEBUG=false, all auto-updates disabled
└── web/                       # docroot
    ├── index.php              # WP front controller
    ├── wp-config.php          # thin loader → config/application.php
    ├── wp/                    # WP core (composer-installed, gitignored)
    └── app/                   # wp-content
        ├── mu-plugins/
        │   └── 00-stack.php   # loader (boots roots/bedrock-autoloader)
        ├── plugins/           # composer require wpackagist-plugin/...
        └── themes/            # composer require wpackagist-theme/...

What is Bedrock and why use it?

Bedrock is Roots’ opinionated shape for WordPress sites: WordPress is treated as a versioned dependency rather than something you commit, the filesystem is laid out for clean separation of code and runtime data, and configuration lives in one place per environment instead of being scattered across wp-config.php and the database. site-template is Bedrock-style — composer-managed, with the canonical web/wp + web/app + config/ split — because the whole FrankenPress proposition (immutable image, declarative deploy) breaks down without it. There needs to be a clear answer to “what’s code?” and “what’s data?” before you can confidently bake one into an image and leave the other in a database / S3 bucket.

What each Roots package does for you

The site composer-requires four small Roots packages. Each one does one thing:
PackageJob
roots/wordpressComposer-installable WordPress core — no bundled themes, no sample content. Lands at web/wp/ (gitignored). Lets you treat WP as a versioned dependency.
roots/wp-configThin loader (web/wp-config.php) that defers all config to config/application.php. Single source of truth for site config — plugins can’t mutate it behind your back.
roots/bedrock-autoloaderScans web/app/mu-plugins/<dir>/ and loads each one’s main file. Required because WordPress only auto-loads .php files at the root of mu-plugins/, not in subdirectories — and composer-installed mu-plugins always end up in subdirectories.
roots/bedrock-disallow-indexingWhen DISALLOW_INDEXING is true, forces blog_public = 0 (noindex meta + robots.txt Disallow: /) and adds an admin notice with the current WP_ENV. Fires automatically on staging / development, no-ops on production.
vlucas/phpdotenv and oscarotero/env sit alongside — they read .env into $_ENV for local dev and provide the env('FOO') helper used throughout application.php. In production the Helm chart sets env vars directly via the ConfigMap, so .env isn’t loaded at all.

Bedrock vs the other Roots projects

Roots also publishes Sage (modern build tooling for a custom theme) and Trellis (Ansible-based provisioning for Bedrock sites on VMs). Neither is used by FrankenPress:
  • Sage is theme-side — orthogonal to the site shape. You can use Sage on top of a FrankenPress site if you like; the runtime doesn’t care which theme you ship.
  • Trellis is the operational layer Bedrock sites traditionally paired with — VMs, Ansible, Nginx, PHP-FPM. FrankenPress replaces Trellis: containerised single-process runtime, declarative Helm deploy, immutable image. If you’re coming from a Trellis-shaped Bedrock site, FrankenPress is the migration target — keep Bedrock, drop Trellis.

Forking

1

Use this template

Click Use this template on the repo. Pick a name (e.g. mysite).
2

Bootstrap locally

git clone git@github.com:<your-org>/<your-site>.git
cd <your-site>
make setup    # composer install + .env from .env.example
make up       # docker compose up -d (site + db + redis + minio)
3

Install WordPress

make wp -- core install \
  --url=http://localhost:8080 \
  --title="My Site" \
  --admin_user=admin \
  --admin_email=admin@example.com \
  --admin_password=admin
Visit http://localhost:8080/wp/wp-admin/ and log in.
This is a one-time step for local Docker Compose dev. The charts Helm chart runs wp core install automatically as a post-install hook Job — that’s a Helm-only mechanism, so the local Compose stack still needs the manual command above.

Adding plugins and themes

Composer-managed (recommended):
composer require wpackagist-plugin/seo-by-rank-math
composer require wpackagist-theme/twentytwentyfive
The wpackagist repository is wired up in composer.json. Plugins land in web/app/plugins/, themes in web/app/themes/. Custom code (your own theme or plugin): drop a directory under the right web/app/* subtree and commit it. The .gitignore ignores composer-installed content but unhides anything you commit explicitly.
Composer-installed plugins do not auto-activate in WordPress. Use wp plugin activate <slug> once after install (or once per environment). The exception is humanmade/s3-uploads which mu-plugin loads explicitly.

Lockdown

The four constants in config/application.php are hard-coded — no env-var override:
Config::define( 'DISALLOW_FILE_EDIT', true );
Config::define( 'DISALLOW_FILE_MODS', true );
Plus in production.php:
Config::define( 'AUTOMATIC_UPDATER_DISABLED', true );
Config::define( 'WP_AUTO_UPDATE_CORE', false );
There is no env-var override by design. The container image is the source of truth — admin-side plugin installs would land on ephemeral disk and disappear on pod restart, replicating inconsistently across replicas. Hard-failing is the correct UX. If you genuinely need to relax this for a developer-only environment, fork the template and remove these lines. The default does not expose a knob.

CI / publishing

GitHub Actions:
  • lint.yml — PHPCS + composer audit on every push and PR
  • build.yml — on push to main and on v*.*.* tag, builds the site image and pushes to ghcr.io/<your-org>/<your-site>
To cut a versioned release:
git tag v1.0.0
git push origin v1.0.0
The image is then available at ghcr.io/<your-org>/<your-site>:v1.0.0. Deploy with:
helm upgrade mysite oci://ghcr.io/frankenpress/charts/site \
  --namespace mysite \
  --set image.repository=ghcr.io/<your-org>/<your-site> \
  --set image.tag=v1.0.0