Core WP is a WordPress starter framework for building custom block themes using modern tooling. It uses Full Site Editing (FSE), current build tooling, and a Docker-first local development workflow.
These steps are for initial project setup only. After setup, docker compose up -d is all you need.
git clone https://github.com/Blake-C/core-wp.git your-project-name
cd your-project-name
rm -rf .git
git init && git add -A
git commit -m "Initial Commit"
git config core.hooksPath .githooks
docker compose up -dThen enter the CLI container:
docker compose exec cli_tools zshWordPress core is provided by the Docker image. Plugins are managed with Composer from wp-content/:
cd wp-content
composer installAdd plugins from wpackagist.org by adding them to wp-content/composer.json and running composer update.
Run the interactive setup script from inside the CLI container:
wp-initThis will:
- Run
pnpm installandpnpm run buildin the theme - Configure WordPress via WP-CLI with your supplied credentials (site title, admin user, timezone)
- Set a homepage and pretty URLs
- Assign the
core-wptheme and create a main navigation menu - Remove default themes (
twentytwentyfive,twentytwentyfour,twentytwentythree) - Remove default plugins (
hello,akismet)
From inside the CLI container:
wp-theme-unit-dataThis imports the WordPress theme unit test data for testing the theme against standard content types.
| Resource | URL |
|---|---|
| Site | http://localhost |
| phpMyAdmin | http://localhost:8000 |
| XDebug port | 9003 |
phpMyAdmin credentials: server: db, user and password from your .env file.
Core WP uses the WordPress block theme structure (Full Site Editing):
wp-content/themes/core-wp/
├── assets/ # Compiled output (git-ignored)
├── parts/ # Block template parts
│ ├── header.html
│ └── footer.html
├── patterns/ # Registered block patterns
│ ├── content-post.php
│ ├── content-page.php
│ ├── content-search.php
│ └── content-none.php
├── templates/ # Block templates (replaces PHP template hierarchy)
│ ├── index.html
│ ├── single.html
│ ├── page.html
│ ├── archive.html
│ ├── search.html
│ ├── 404.html
│ ├── front-page.html
│ └── blank.html
├── theme_components/ # Source files
│ ├── sass/
│ ├── js/
│ ├── images/
│ ├── fonts/
│ └── icons/
├── theme.json # Global styles: color palette, layout, typography
├── functions.php
└── package.json
Global styles (color palette, layout sizes, font scale) are defined in theme.json rather than functions.php.
All pnpm commands run inside the cli_tools Docker container:
docker compose exec cli_tools zsh
cd wp-content/themes/core-wp| Command | Description |
|---|---|
pnpm run build |
Full build — styles, scripts, images, static assets |
pnpm run styles |
Compile SCSS → CSS (lint → Sass → PostCSS → rename) |
pnpm run scripts |
Bundle JS (Prettier → Webpack) |
pnpm run images |
Copy and optimize images via ImageMagick |
pnpm run static:assets |
Copy fonts and icons |
pnpm run watch |
Watch mode for styles, scripts, images, and static assets |
pnpm run serve |
Watch + Browser-Sync livereload (CSS injected without reload) |
pnpm run serve:all |
Same as serve plus PHP (PHPCS/PHPCBF) and HTML file watching |
Browser-Sync is environment-aware via bs-config.cjs:
| Environment | URL | Notes |
|---|---|---|
| Docker (inside container) | http://localhost:3000 |
Proxies the wordpress service |
| Local (outside container) | http://localhost:3010 |
Proxies http://localhost |
CSS changes are injected directly into the browser without a full page reload. All other changes (JS, PHP, HTML, images) trigger a full reload.
theme # cd into the theme root
theme_components # cd into theme_components/XDebug 3 is installed in the wordpress container via docker/wordpress/Dockerfile. To debug:
- Install the PHP Debug extension in VSCode
- Start the "Listen for XDebug" configuration (
F5) - Load any page at
http://localhost
XDebug connects to your host on port 9003. Path mappings in .vscode/launch.json are pre-configured.
git clone your-project.git
cd your-project
docker compose up -d
docker compose exec cli_tools zshThen:
- Copy your
plugins/anduploads/intowp-content/ - Import the database via phpMyAdmin at
http://localhost:8000 - Run a search-replace for the production URL:
wp search-replace https://production.com http://localhost --precise --all-tables- Activate the theme and run the build:
wp theme activate core-wp
cd wp-content/themes/core-wp
pnpm install && pnpm run build- If
wp search-replacefails, temporarily rename theplugins/directory and retry - Disable caching, security, and mailing plugins while working locally (WP Rocket, Wordfence, etc.)
- If phpMyAdmin won't accept your database file, increase
UPLOAD_LIMITincompose.yml(default:1500M)
docker compose downProduction uses compose.prod.yml with a multi-stage Docker build. WordPress core comes from the official image; theme assets are compiled and plugins installed inside the build — no bind mounts, no dev tools in the final image.
Requirements: a server with Docker and Docker Compose installed, and Git access to the repo.
git clone your-project.git
cd your-project
cp .env-example .env
nano .env # fill in production credentials
chmod 600 .env # restrict to owner only
./deploy.shgit pull
./deploy.shdeploy.sh always pulls the latest base images (--pull), rebuilds the WordPress image with the latest code, and restarts the services with zero manual steps.
Named Docker volumes survive rebuilds and container replacements:
| Volume | Contents |
|---|---|
uploads |
wp-content/uploads/ — user-uploaded media |
db_data |
MariaDB data directory |
All other content (theme, plugins, PHP code) is baked into the image and replaced on every deploy.
compose.prod.yml serves on port 80 only. For HTTPS, place a reverse proxy (nginx + Certbot, or Traefik) in front of the WordPress container to handle SSL termination. The WordPress container does not need to change.
To roll back to a previous build, re-tag or re-run the previous image:
docker compose -f compose.prod.yml down
# restore the previous image tag, then:
docker compose -f compose.prod.yml up -dPort already in use (80, 3306, or 8000)
Another process on your machine is using the port. Stop it, or change the host-side port in compose.yml (e.g. 8080:80).
WordPress container keeps restarting
The database health check hasn't passed yet. Wait ~30 seconds, or check logs:
docker compose logs dbChanged .env credentials and the database won't connect
MariaDB initializes its root password and database on first run. If you change credentials after the first docker compose up, delete the persisted data and recreate:
docker compose down
rm -rf data/mysql
docker compose up -dpnpm: command not found
Build commands must run inside the cli_tools container, not on your host machine:
docker compose exec cli_tools zsh
cd wp-content/themes/core-wp
pnpm run buildBuild fails with missing packages
Run pnpm install from the theme directory first.
Hooks not running on commit
The hooks path must be configured once per clone:
git config core.hooksPath .githooksHook fails: node: command not found
The hook auto-detects nvm. If you use a non-standard Node install, ensure node is on your PATH before committing.
Hook fails with PHPCS errors
Run composer install from wp-content/ first to install PHPCS and the coding standards.
Breakpoints not being hit
- Confirm
XDEBUG_MODE: debugis set (not commented out) incompose.yml - Install the PHP Debug VSCode extension
- Start the "Listen for XDebug" configuration (
F5) before loading the page
Check the XDebug log
docker compose exec wordpress cat /tmp/xdebug.logwp commands not found
WP-CLI runs inside the cli_tools container:
docker compose exec cli_tools zsh
wp option get siteurlSite not loading after database import
The imported database still has the production URL. Run a search-replace:
wp search-replace https://production.com http://localhost --precise --all-tables| Service | Image | Port | Notes |
|---|---|---|---|
| WordPress (PHP 8.3) | Custom — docker/wordpress/Dockerfile |
80 |
XDebug 3 installed |
| Database | mariadb:11.8 |
3306 |
— |
| phpMyAdmin | phpmyadmin |
8000 |
— |
| CLI Tools | digitalblake/light-cli:5.0.0 |
— | pnpm, WP-CLI, Composer, ImageMagick |
XDebug 3 is baked into the WordPress service image (docker/wordpress/Dockerfile) and listens on port 9003. See the PHP Debugging section for VSCode setup.
| Package | Version |
|---|---|
| PHP | 8.3 |
| Node | 24 |
| pnpm | 10.32.1 |
| WP-CLI | latest |
| Composer | latest |
| ImageMagick | 7.1.2 (with JPEG support) |
| zsh | 5.9 |
| Tool | Purpose |
|---|---|
| Webpack 5 + Babel | JS bundling, ES2020 transpilation |
| Dart Sass 1.98 | SCSS compilation |
| PostCSS + cssnano | CSS autoprefixing and minification |
| Prettier | JS + SCSS formatting |
| ESLint 10 | JS linting (flat config) |
| Stylelint 16 | SCSS linting |
| Browser-Sync 3 | Live reload proxy with CSS injection |
| ImageMagick | Image optimization via mogrify |
See CHANGELOG.md for details on recent updates.