-
Notifications
You must be signed in to change notification settings - Fork 0
WordPress Customization
This guide covers how to customize WordPress deployments with Displace, including adding custom themes, plugins, and using the hot-reload development workflow.
flowchart TB
subgraph local["Local Development"]
themes_local["themes/"]
plugins_local["plugins/"]
compose["docker-compose"]
browser["Browser"]
end
subgraph production["Production Build"]
dockerfile["Dockerfile"]
image["Container Image"]
k8s["Kubernetes Pod"]
end
themes_local -->|"Hot-reload<br/>(volume mount)"| compose
plugins_local -->|"Hot-reload<br/>(volume mount)"| compose
compose --> browser
themes_local -->|"COPY into image"| dockerfile
plugins_local -->|"COPY into image"| dockerfile
dockerfile --> image
image --> k8s
Two Development Modes:
- Hot-reload (Development): Edit files locally, see changes instantly
- Baked-in (Production): Themes and plugins are built into the container image
After running displace project init wordpress, you'll have:
my-wordpress-site/
├── themes/ # Custom WordPress themes
│ └── .gitkeep
├── plugins/ # Custom WordPress plugins
│ └── .gitkeep
├── mu-plugins/ # Must-use plugins (auto-activated)
│ └── .gitkeep
├── uploads/ # Persistent uploads (PVC in production)
│ └── .gitkeep
├── docker/ # Container configuration
│ ├── nginx.conf
│ ├── supervisord.conf
│ └── wp-config.php
├── manifests/ # Kubernetes manifests
├── Dockerfile # Multi-stage build
├── docker-compose.yaml # Local development
├── Makefile # Build and deploy commands
├── .credentials # Generated passwords (git-ignored)
└── README.md
# Option A: Create a new theme from scratch
mkdir -p themes/my-theme
touch themes/my-theme/style.css
touch themes/my-theme/index.php
touch themes/my-theme/functions.php
# Option B: Copy an existing theme
cp -r /path/to/my-theme themes/
# Option C: Download a theme
cd themes
curl -O https://example.com/theme.zip
unzip theme.zip && rm theme.zipAt minimum, a WordPress theme needs:
themes/my-theme/
├── style.css # Required: Theme metadata
├── index.php # Required: Main template
├── functions.php # Optional: Theme functions
├── header.php # Optional: Header template
├── footer.php # Optional: Footer template
├── screenshot.png # Optional: Theme preview image
└── ...
style.css header (required):
/*
Theme Name: My Custom Theme
Theme URI: https://example.com/my-theme
Author: Your Name
Author URI: https://example.com
Description: A custom WordPress theme
Version: 1.0.0
Requires at least: 6.0
Tested up to: 6.7
Requires PHP: 8.0
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: my-theme
*/# Option A: Create a new plugin
mkdir -p plugins/my-plugin
touch plugins/my-plugin/my-plugin.php
# Option B: Copy an existing plugin
cp -r /path/to/my-plugin plugins/
# Option C: Download from WordPress.org
cd plugins
curl -O https://downloads.wordpress.org/plugin/plugin-name.zip
unzip plugin-name.zip && rm plugin-name.zipA minimal plugin:
plugins/my-plugin/
├── my-plugin.php # Main plugin file with header
├── includes/ # Optional: PHP includes
├── assets/ # Optional: CSS/JS/images
└── readme.txt # Optional: Plugin readme
Plugin header (required in main file):
<?php
/**
* Plugin Name: My Custom Plugin
* Plugin URI: https://example.com/my-plugin
* Description: A custom WordPress plugin
* Version: 1.0.0
* Requires at least: 6.0
* Requires PHP: 8.0
* Author: Your Name
* Author URI: https://example.com
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
* Text Domain: my-plugin
*/
// Your plugin code hereMust-use plugins are automatically activated and cannot be deactivated via the admin:
# Add a must-use plugin
cp my-critical-plugin.php mu-plugins/Use mu-plugins for:
- Critical functionality that shouldn't be disabled
- Performance optimizations
- Security hardening
- Custom site configurations
This is the primary development workflow. Edit files locally, see changes instantly in your browser.
# Create project
mkdir my-wordpress-site && cd my-wordpress-site
displace project init wordpress
# Deploy to local cluster
make deployExpected output:
Building WordPress Docker image...
PHP Version: 8.3
WordPress Version: 6.9.4
Image: my-wordpress-site:latest
[+] Building 45.2s (12/12) FINISHED
✓ Build completed!
Deploying WordPress to Kubernetes...
✓ namespace/my-wordpress-site created
✓ secret/database-secret created
...
Deployment completed!
# Create custom theme
mkdir -p themes/developer-theme
# Create style.css
cat > themes/developer-theme/style.css << 'EOF'
/*
Theme Name: Developer Theme
Description: A theme for development
Version: 1.0.0
*/
body {
font-family: system-ui, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 2rem;
}
EOF
# Create index.php
cat > themes/developer-theme/index.php << 'EOF'
<?php get_header(); ?>
<main>
<?php if (have_posts()): while (have_posts()): the_post(); ?>
<article>
<h2><?php the_title(); ?></h2>
<?php the_content(); ?>
</article>
<?php endwhile; endif; ?>
</main>
<?php get_footer(); ?>
EOF
# Create header.php
cat > themes/developer-theme/header.php << 'EOF'
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?php wp_head(); ?>
</head>
<body <?php body_class(); ?>>
<header>
<h1><?php bloginfo('name'); ?></h1>
</header>
EOF
# Create footer.php
cat > themes/developer-theme/footer.php << 'EOF'
<footer>
<p>© <?php echo date('Y'); ?> <?php bloginfo('name'); ?></p>
</footer>
<?php wp_footer(); ?>
</body>
</html>
EOFmake devExpected output:
Starting development environment...
Themes and plugins are mounted for hot-reload
Development environment started!
Access:
WordPress: http://localhost:8080
Admin: http://localhost:8080/wp-admin
Hot-reload directories:
themes/ - Edit themes, changes appear immediately
plugins/ - Edit plugins, changes appear immediately
mu-plugins/ - Edit must-use plugins
Commands:
make dev-logs - View container logs
make dev-down - Stop development environment
sequenceDiagram
participant Editor as Code Editor
participant FS as File System
participant Docker as Docker Container
participant Browser as Browser
Editor->>FS: Save file
FS->>Docker: Volume mount updates
Browser->>Docker: Refresh page
Docker->>Browser: Serve updated content
Note over Editor,Browser: Changes visible immediately!
- Open your browser: http://localhost:8080/wp-admin
-
Log in with credentials from
.credentials - Activate your theme: Appearance → Themes → Developer Theme
- Edit theme files in your editor
- Refresh browser to see changes instantly
When you edit a file:
# Edit the theme
vim themes/developer-theme/style.cssChanges appear immediately in your browser - no rebuild needed!
# In another terminal
make dev-logsExpected output:
wordpress-1 | [php-fpm] GET /wp-admin/ 200
wordpress-1 | [php-fpm] GET /wp-content/themes/developer-theme/style.css 200
mariadb-1 | Version: '11.5.2-MariaDB' socket: '/run/mysqld/mysqld.sock'
make dev-downWhen your theme is ready, deploy to production with the theme baked into the image.
flowchart LR
themes["themes/"]
plugins["plugins/"]
build["make build"]
image["Docker Image"]
push["displace registry push"]
deploy["make deploy"]
cluster["Production Cluster"]
themes --> build
plugins --> build
build --> image
image --> push
push --> deploy
deploy --> cluster
make buildExpected output:
Building WordPress Docker image...
PHP Version: 8.3
WordPress Version: 6.9.4
Image: my-wordpress-site:latest
[+] Building 52.3s (15/15) FINISHED
=> [wordpress-source] Downloading WordPress 6.9.4...
=> [production] Installing PHP extensions...
=> [production] COPY themes/ /var/www/html/wp-content/themes/
=> [production] COPY plugins/ /var/www/html/wp-content/plugins/
=> [production] COPY mu-plugins/ /var/www/html/wp-content/mu-plugins/
✓ Build completed!
Your themes and plugins are now baked into the image.
# Push to cluster registry
displace registry push my-wordpress-site:latestExpected output:
Pushing my-wordpress-site to registry: k3d-displace-registry
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Tagging image for localhost:5000...
✓ Successfully pushed my-wordpress-site
Image available at:
From host: localhost:5000/my-wordpress-site:latest
From cluster: k3d-displace-registry:5000/my-wordpress-site:latest
# Deploy to local cluster
make deploy
# Or deploy to production
displace project deploy --cluster productionExpected output:
Deploying WordPress to Kubernetes...
✓ namespace/my-wordpress-site created
✓ secret/database-secret created
✓ persistentvolumeclaim/mariadb-pvc created
✓ deployment/mariadb created
✓ service/mariadb-service created
✓ deployment/wordpress created
✓ service/wordpress-service created
✓ ingress/wordpress-ingress created
Deployment completed!
Access your site:
Local access: Run 'make port-forward' then visit http://localhost:8080
Admin panel: http://localhost:8080/wp-admin
Edit .credentials or set environment variables:
# In .credentials
PHP_VERSION=8.2
# Or as environment variable
PHP_VERSION=8.2 make buildSupported PHP versions:
-
8.3(default, recommended) 8.28.1
# In .credentials
WORDPRESS_VERSION=6.6.2
# Or as environment variable
WORDPRESS_VERSION=6.6.2 make buildCheck available versions:
curl -s https://api.wordpress.org/core/version-check/1.7/ | jq '.offers[].version'| WordPress Version | Minimum PHP | Recommended PHP |
|---|---|---|
| 6.7.x | 7.4 | 8.2+ |
| 6.6.x | 7.4 | 8.2+ |
| 6.5.x | 7.4 | 8.1+ |
| 6.4.x | 7.4 | 8.1+ |
Check your plugin requirements before selecting PHP version:
// In your plugin header
* Requires PHP: 8.0Here's a complete workflow from start to finish:
mkdir my-blog && cd my-blog
displace project init wordpressmake deploymkdir -p themes/minimalist
cat > themes/minimalist/style.css << 'EOF'
/*
Theme Name: Minimalist
Description: A clean, minimalist theme
Version: 1.0.0
*/
* { box-sizing: border-box; }
body {
font-family: Georgia, serif;
line-height: 1.8;
max-width: 700px;
margin: 0 auto;
padding: 2rem;
color: #333;
}
h1, h2, h3 { font-family: system-ui, sans-serif; }
a { color: #0066cc; }
article { margin-bottom: 3rem; }
EOF
cat > themes/minimalist/index.php << 'EOF'
<?php get_header(); ?>
<main>
<?php if (have_posts()): while (have_posts()): the_post(); ?>
<article>
<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
<time><?php the_date(); ?></time>
<?php the_excerpt(); ?>
</article>
<?php endwhile; endif; ?>
</main>
<?php get_footer(); ?>
EOF
cat > themes/minimalist/header.php << 'EOF'
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?php wp_head(); ?>
</head>
<body>
<header>
<h1><a href="<?php echo home_url(); ?>"><?php bloginfo('name'); ?></a></h1>
<p><?php bloginfo('description'); ?></p>
</header>
EOF
cat > themes/minimalist/footer.php << 'EOF'
<footer>
<p>Powered by WordPress</p>
</footer>
<?php wp_footer(); ?>
</body>
</html>
EOFmake dev- Open http://localhost:8080/wp-admin
- Log in (credentials in
.credentials) - Go to Appearance → Themes
- Activate "Minimalist" theme
- Open http://localhost:8080 in another tab
- Edit
themes/minimalist/style.css - Refresh browser - changes appear immediately!
When ready:
# Stop dev mode
make dev-down
# Build production image with theme baked in
make build
# Deploy to cluster
make deploy# Check status
make status
# View in browser
make port-forward
# Visit http://localhost:8080Check theme directory structure:
ls -la themes/my-theme/
# Must have style.css and index.phpVerify style.css has valid header:
head -20 themes/my-theme/style.css
# Must have "Theme Name:" lineVerify volumes are mounted:
docker compose ps
# Should show "Up" status
docker compose exec wordpress ls -la /var/www/html/wp-content/themes/
# Should show your themeClear WordPress cache:
# In wp-admin: Settings → Permalinks → Save (to flush rewrite rules)
# Or use WP-CLI if installed:
docker compose exec wordpress wp cache flushCheck plugin header:
head -20 plugins/my-plugin/my-plugin.php
# Must have "Plugin Name:" lineCheck PHP errors:
make dev-logs | grep -i errorCheck Dockerfile syntax:
docker build --no-cache -t test .Verify file permissions:
ls -la themes/
ls -la plugins/
# Files should be readable# Add to .gitignore (already done by template)
.credentials
uploads/
# Commit your themes and plugins
git add themes/ plugins/
git commit -m "Add custom theme and plugins"-
Always start with
make devfor development - Test locally before building production image
-
Use
make buildonly when ready to deploy - Tag releases with version numbers
- Never commit
.credentialsfile - Use strong passwords (auto-generated by template)
- Keep WordPress and plugins updated
- Review third-party plugins before using
- WordPress Template - Full template reference
- Dev Mode - Live synchronization for Kubernetes
- Container Registry - Managing container images
- Getting Started - Initial setup guide