Skip to content

Latest commit

 

History

History

README.md

WordPress - Helm Chart for Kubernetes

Introduction

This Helm chart installs WordPress in a Kubernetes cluster with many advanced features. It is based on the official WordPress image and provides automation for installation, user management, plugin installation, and metrics for prometheus (WordPress and Apache).

TL;DR

You can find different sample YAML files (external database, integrated MariaDB and advanced configuration) in the GitHub repo in the subfolder "samples".

Note: No default site is set as "Home", so you will see no landing page. You have to log in to /wp-admin to install a theme.

Installation with integrated MariaDB chart

# ./samples/mariaDB.secrets.yaml
apiVersion: v1
kind: Secret
metadata:
  name: wordpress-test-secret
stringData:
  mariadb-root-password: S3cureDBP@ss
  mariadb-password: Sup3rS3cureP@ss
type: Opaque
# ./samples/mariaDB.values.yaml
wordpress:
  url: "https://example.com"
mariadb:
  auth:
    database: "wordpress_db"
    username: "wordpress_db_user"
    existingSecret: "wordpress-test-secret"

Install with Helm:

kubectl apply -f ./samples/mariaDB.secrets.yaml
helm install wordpress oci://ghcr.io/slybase/charts/wordpress --values ./samples/mariaDB.values.yaml

Features

Automatic WordPress Installation

  • Init Container: Automatic initial installation of WordPress with predefined admin credentials.
  • Configuration: Set admin username, password, email, first name, last name, and blog title.
  • Debug Mode: Enable debugging for the installation.
  • Permalinks: Configure permalink structures (e.g., post name, day and name).

User Management

  • Automatic User Generation: Create additional users with roles (Administrator, Editor, etc.).
  • Email Notification: Automatically send emails with generated passwords.

Language

  • Language: Set the WordPress language (e.g., de_DE for German).

Plugin Installation

  • Automatic Installation: Install plugins from WordPress.org, local ZIPs, URLs, or Composer packages.
  • Composer Support: Install plugins via Composer (e.g., humanmade/s3-uploads) that aren't available in WordPress.org.
  • Versioning: Specify plugin versions (WordPress.org and Composer syntax supported).
  • Activation and Auto-Updates: Activate plugins after installation and enable auto-updates.

Theme Installation

  • Automatic Installation: Install themes from WordPress.org, local ZIPs, URLs, or Composer packages.
  • Composer Support: Install themes via Composer (e.g., wpackagist-theme/astra).
  • Versioning: Specify theme versions (WordPress.org and Composer syntax supported).
  • Activation and Auto-Updates: Activate themes after installation and enable auto-updates.
  • Custom Themes: Support for custom theme ZIPs via direct URLs.

WordPress Multisite

  • Subdirectory and Subdomain modes: Configure multisite with either example.com/blog or blog.example.com style URLs.
  • Automatic Site Creation: Define sub-sites declaratively in values and they are created/updated on every init.
  • Network-wide Plugin/Theme Activation: Use networkActivate for plugins or networkEnable for themes to make them available across all sites.
  • Independent Main Site and Sub-Site Control: activate controls the main site, sites[] controls sub-sites — both can be combined.
  • Per-Site User Roles: Assign users to specific sub-sites with individual roles.
  • Site Pruning: Optionally archive sites not defined in the configuration.
  • URL Plugin/Theme Slug Override: Use the slug property for URL-based plugins/themes to enable full feature support (autoupdate, sites, network activation).

Database

  • External Database: Use an external MariaDB/MySQL database.
  • Embedded MariaDB: Enable the integrated MariaDB chart for local database.
  • Memcached: Enable Memcached for caching.
  • Redis: Enable Redis as alternative caching.
  • Valkey: Enable Valkey (Redis fork) as alternative caching.

Metrics and Monitoring

  • WordPress Metrics: Automatically install a WordPress Plugin for Prometheus metrics.
  • Apache Metrics: Sidecar container for Apache metrics.
  • Grafana Dashboard: Automatically deploy Grafana dashboard for WordPress metrics visualization.
    • Requires Grafana with dashboard sidecar (e.g., kube-prometheus-stack)
    • Automatically discovered via grafana_dashboard: "1" labe

Additional Configuration Files in values

  • Custom wp-config.php: Additional constants like WP_MEMORY_LIMIT.
  • .htaccess Configuration: Customize Apache URL rewriting and directives via wordpress.htaccess.
  • Apache Default Site Config: Modify /etc/apache2/sites-available/000-default.conf using apache.customDefaultSiteConfig.
  • Apache Ports Config: Adjust /etc/apache2/ports.conf with apache.customPortsConfig.
  • Apache PHP Config: Set PHP settings like upload limits via apache.customPhpConfig.

Custom commands in init container

  • Execute custom shell commands after init.sh via ConfigMap (wordpress.init.customInitConfigMap.name)
  • Perfect for custom setup tasks like updating plugins or creating pages
  • Configure with name and optional key (defaults to "commands.sh")
  • See samples/customInit.configmap.yaml and samples/customInit.values.yaml

MU-Plugins (Must-Use Plugins)

  • Deploy MU-Plugins via ConfigMaps - automatically activated PHP code that cannot be deactivated
  • Each ConfigMap data key becomes a PHP file in wp-content/mu-plugins/
  • Reference multiple ConfigMaps in wordpress.muPluginsConfigMaps
  • See samples/muPlugins.configmap.yaml and samples/muPlugins.values.yaml

Security by default

  • Pod Security Context: Configured by default for secure permissions.
  • Container Security Context: RunAsNonRoot and additional security measures enabled.

WordPress configuration

Mandatory parameters

  • wordpress.url: The WordPress site URL. Used to automatically set WP_HOME and WP_SITEURL — both as environment variables in the container and as PHP constants in wp-config.php (via WORDPRESS_CONFIG_EXTRA). If no protocol is provided, it is auto-prefixed with https:// (when Ingress TLS is configured) or http:// (otherwise).
  • storage: Set your storage settings for WordPress
  • By default mariadb.enabled is true. You have to set mariadb.auth and mariadb.persistence. Alternatively, it is also possible to use an external database.

Note: If you need full manual control over WP_HOME / WP_SITEURL (e.g. different values for each, or a reverse proxy setup), set wordpress.configExtraInject: false to disable the automatic injection into wp-config.php and define the constants yourself via wordpress.configExtra. The environment variables WP_HOME and WP_SITEURL in the container are always derived from wordpress.url.

Recommended parameters

  • wordpress.init: Admin credentials and blog setup
  • service.type: If you want to access WordPress internally set it to NodePort

Additional parameters

  • wordpress.plugins: List of plugins to install
  • wordpress.themes: List of themes to install
  • wordpress.users: Additional users
  • wordpress.language: Language (e.g., de_DE)
  • wordpress.permalinks.structure: Set the wordpress post structure
  • metrics.wordpress: Enable WordPress metrics
  • metrics.apache: Enable Apache metrics
  • memcached.enabled: Enable embedded memcached
  • redis.enabled: Enable embedded redis
  • valkey.enabled: Enable embedded valkey

Note: Cache backends are mutually exclusive. Enable at most one of memcached.enabled, redis.enabled, valkey.enabled (validated by values.schema.json and templates).

Caching backends (compact)

Enable exactly one backend via its enabled flag.

  • Memcached (memcached.enabled)
    • Subchart on port 11211
    • Injects: WP_CACHE, WP_CACHE_KEY_SALT, $memcached_servers
    • Additional init logic for PHP extensions memcache/memcached
  • Redis (redis.enabled)
    • Subchart on port 6379
    • Injects: WP_CACHE, WP_CACHE_KEY_SALT, WP_REDIS_HOST, WP_REDIS_PORT, optional WP_REDIS_PASSWORD
    • No extra PHP-extension init required (redis-cache uses Predis)
  • Valkey (valkey.enabled)
    • Same behavior as Redis (Valkey is Redis-compatible), also on port 6379
    • Same inject pattern as Redis

Common options for all backends:

  • createConfig: Enables/disables automatic config injection
  • cacheKeySalt: Explicit salt value
  • cacheKeySaltSecret.{name,key}: Salt from an existing Secret
  • Redis/Valkey auth password from auth.password or auth.existingSecret

Minimal example (Redis):

redis:
  enabled: true
  createConfig: true
  cacheKeySalt: ""
  cacheKeySaltSecret:
    name: ""
    key: "WP_CACHE_KEY_SALT"
  auth:
    password: ""
    existingSecret: ""
    existingSecretPasswordKey: "redis-password"

Quick check (same idea for Redis/Valkey):

kubectl -n default exec deploy/wp-wordpress -c wordpress -- ls -l /var/www/html/wp-content/object-cache.php
kubectl -n default exec deploy/wp-wordpress -c wordpress -- redis-cli -h wp-redis INFO stats | grep -E 'keyspace_hits|keyspace_misses'

Short stats checks:

# Memcached
kubectl -n default exec deploy/wp-wordpress -c wordpress -- php -r '$s=fsockopen("wp-memcached",11211,$e,$es,2); fwrite($s,"stats\r\n"); echo stream_get_contents($s); fclose($s);' | grep -E 'STAT cmd_get|STAT cmd_set|STAT get_hits|STAT get_misses'

# Redis / Valkey
kubectl -n default exec deploy/wp-wordpress -c wordpress -- redis-cli -h wp-redis INFO stats | grep -E 'keyspace_hits|keyspace_misses'

Installation samples

Basic Installation

See in TL;DR

With External Database

Find the externalDB.secrets.yaml and externalDB.values.yaml in the GitHub repo in the subfolder "samples".

kubectl apply -f ./samples/externalDB.secrets.yaml
helm install wordpress oci://ghcr.io/slybase/charts/wordpress --values ./samples/externalDB.values.yaml

Advanced installation

Find the advanced.secrets.yaml, advanced.configmap.yaml and advanced.values.yaml in the GitHub repo in the subfolder "samples".

This includes everything from basic installation plus:

  • Initial setup of WordPress
  • Plugin Installation
  • Theme Installation
  • Additional WordPress user
  • Additional configuration files
    • .htaccess
    • wp-config.php settings
    • apache custom.ini
  • Permanent Nodeport
  • Prometheus metrics
    • For WordPress
    • For Apache
  • Memcached pod
kubectl apply -f ./samples/advanced.secrets.yaml
kubectl apply -f ./samples/advanced.configmap.yaml
helm install wordpress oci://ghcr.io/slybase/charts/wordpress --values ./samples/advanced.values.yaml

Advanced installation with High Availability (2 Replicas)

Find the advanced2.values.yaml in the GitHub repo in the subfolder "samples".

This setup includes everything from the advanced installation plus:

  • 2 WordPress pod replicas for high availability and load balancing
  • ReadWriteMany (RWX) storage to allow multiple pods to share the same WordPress files
  • Distributed locking ensures only one pod runs init operations at a time (prevents race conditions)

Requirements:

  • Your cluster must support ReadWriteMany (RWX) storage class (e.g., NFS, Ceph, cloud provider shared storage)
  • LoadBalancer or Ingress for distributing traffic across replicas
kubectl apply -f ./samples/advanced.secrets.yaml
kubectl apply -f ./samples/advanced.configmap.yaml
helm install wordpress oci://ghcr.io/slybase/charts/wordpress --values ./samples/advanced2.values.yaml

Memcached cache setup

Use WordPress with Memcached object cache. See samples/memcached.values.yaml.

helm install wordpress oci://ghcr.io/slybase/charts/wordpress --values ./samples/memcached.values.yaml

Redis cache setup

Use WordPress with Redis object cache. See samples/redis.values.yaml.

helm install wordpress oci://ghcr.io/slybase/charts/wordpress --values ./samples/redis.values.yaml

Valkey cache setup

Use WordPress with Valkey object cache. See samples/valkey.values.yaml.

helm install wordpress oci://ghcr.io/slybase/charts/wordpress --values ./samples/valkey.values.yaml

Custom Init Commands

Execute custom shell commands after WordPress installation. See samples/customInit.configmap.yaml and samples/customInit.values.yaml.

kubectl apply -f ./samples/customInit.configmap.yaml
helm install wordpress oci://ghcr.io/slybase/charts/wordpress --values ./samples/customInit.values.yaml

MU-Plugins via ConfigMaps

Deploy Must-Use Plugins that are automatically activated. See samples/muPlugins.configmap.yaml and samples/muPlugins.values.yaml.

kubectl apply -f ./samples/muPlugins.configmap.yaml
helm install wordpress oci://ghcr.io/slybase/charts/wordpress --values ./samples/muPlugins.values.yaml

Composer Packages

Install plugins and themes via Composer that aren't available in WordPress.org. See samples/composer.values.yaml.

helm install wordpress oci://ghcr.io/slybase/charts/wordpress --values ./samples/composer.values.yaml

Examples:

  • Plugins: humanmade/s3-uploads, wpackagist-plugin/wordpress-seo
  • Themes: wpackagist-theme/astra
  • Library Packages: Composer packages listed under wordpress.plugins or wordpress.themes are explicitly mapped into wp-content/plugins or wp-content/themes, even when upstream declares them as type: library
  • Auto-Update: Works for packages without fixed version (always installs latest)
  • Pruning: Compatible with pluginsPrune and themesPrune

Custom Composer Repositories

By default, only wpackagist.org is configured, which mirrors all WordPress.org plugins and themes.

Add custom repositories for private/premium packages:

wordpress:
  composer:
    repositories:
      - type: "vcs"
        url: "https://github.com/mycompany/private-plugin"
      - type: "composer"
        url: "https://my-satis-server.com"
      - type: "package"
        package:
          name: "vendor/premium-plugin"
          version: "1.0.0"
          dist:
            url: "https://example.com/premium-plugin.zip"
            type: "zip"
  plugins:
    - name: "mycompany/private-plugin"
      activate: true

WordPress Multisite

Full multisite configuration with automatic site creation, network-wide plugin/theme management, and per-site user roles. See samples/multisite.values.yaml and samples/multisite.secrets.yaml.

kubectl apply -f ./samples/multisite.secrets.yaml
helm install wordpress oci://ghcr.io/slybase/charts/wordpress --values ./samples/multisite.values.yaml

Important: Do not add multisite constants (WP_ALLOW_MULTISITE, MULTISITE, SUBDOMAIN_INSTALL, DOMAIN_CURRENT_SITE, PATH_CURRENT_SITE, SITE_ID_CURRENT_SITE, BLOG_ID_CURRENT_SITE) to configExtra, configExtraConfigMap, or configExtraSecret. These constants are automatically written to wp-config.php by the init container during multisite setup and persist on the PVC. Defining them again would cause PHP "Constant already defined" warnings.

Plugin/Theme Activation in Multisite

In multisite mode, activate and sites work independently:

Property Effect
activate: true Activate on the main site
sites: [blog, shop] Activate on specific sub-sites
activate: true + sites: [blog] Activate on main + blog
networkActivate: true Activate across entire network (overrides activate and sites)

Example:

wordpress:
  plugins:
    # Network-wide: available on ALL sites
    - name: "wordpress-seo"
      activate: true
      networkActivate: true

    # Sub-sites only: NOT on main site
    - name: "contact-form-7"
      activate: false
      sites:
        - blog
        - shop

    # Main site + specific sub-sites
    - name: "woocommerce"
      activate: true
      sites:
        - shop

The same pattern applies to themes with networkEnable (make theme available to all sites) and activate/sites (control which site uses it as active theme).

URL Plugins/Themes with slug Property

When installing plugins or themes from a URL, the slug (directory name after installation) cannot always be determined from the URL. Use the slug property to explicitly specify it:

wordpress:
  plugins:
    - name: "https://example.com/downloads/my-premium-plugin-v2.3.zip"
      slug: "my-premium-plugin"   # Required: actual plugin directory name
      activate: true
      autoupdate: true
      sites:
        - blog
  themes:
    - name: "https://creativethemes.com/downloads/blocksy-child.zip"
      slug: "blocksy-child"       # Required: actual theme directory name
      activate: true
      autoupdate: true

Without slug, URL plugins/themes use basename of the URL as a best-effort guess, but this fails for URLs like download?id=123 or versioned filenames like plugin-v2.3.1.zip. Setting slug explicitly enables:

  • Skip-if-installed detection (no unnecessary reinstalls)
  • Auto-updates
  • Site-specific activation (sites[])
  • Network activation (networkActivate / networkEnable)

Notable changes

To 3.4.0

  • Complete Memcached object-cache support for WordPress, including runtime bootstrap of memcache/memcached PHP extensions (no custom image required).
  • WP_CACHE is now injected automatically (guarded) when Memcached config is enabled.
  • New WP_CACHE_KEY_SALT handling for stable cache isolation: explicit value, existing Secret reference, or auto-generated persisted value.
  • New memcached.serverGroups allows cache-group-specific Memcached backends, with fallback to the embedded Memcached service.
  • Improved init safety in drop-in mode (object-cache.php) to avoid plugin activation/redeclare conflicts and stale drop-in bootstrap failures.
  • Fixed blog title update in init (blogname) so title changes are now applied reliably.
  • Added Redis as alternative caching option using cloudpirates-redis chart and redis-cache plugin.
  • Added Valkey (Redis fork) as alternative caching option using cloudpirates-valkey chart and redis-cache plugin.
  • Automatically injects WP_REDIS_HOST, WP_REDIS_PORT, and optional WP_REDIS_PASSWORD into wp-config.php.
  • Uses Predis library (pure PHP) - no PHP extensions or init containers required.
  • Redis, Valkey, and Memcached can be used as mutually exclusive caching backends.

To 3.2.0

  • Introducing multisite for WordPress, including users, plugins, and themes

To 3.0.0

  • Split init pipeline into three containers: fix-permissions (runs as root), base, and init — enables correct ownership handling on RWX storage (e.g., Longhorn). ⚠️ fix-permissions requires root permissions.
  • Moved the init script library into a separate ConfigMap and added ConfigMap checksum annotations to trigger automatic rollouts on script/config changes. If the ConfigMap is managed outside the Helm release (e.g. applied via Kustomize), you may need to manually restart the deployment to run the init containers again: kubectl rollout restart deployment/wordpress-wordpress
  • Improved multi-pod init safety with heartbeat-based distributed locking, a bootstrap helm_locks table, and automatic stale-lock detection (60s without heartbeat).
  • Replaced wp-cli with direct database queries for much faster initialization (up to ~200% speed improvement in many scenarios).

To 2.0.0

  • WordPress version from 6.8.3 to 6.9.0
  • WordPress image tag from PHP version 8.1 to 8.3 (default)
  • mariadb from 12.0.2 to 12.1.2

To 1.0.0

This major release introduces new possibilities to use composer plugins and themes and muPlugins. Now it is possible to activate a prune mode for plugins and themes. This will uninstall all plugins/themes that are not listed in the values.

Also adds more flexible user customization with init scripts. The init script is now a huge set to pre-configure and set up WordPress. For more, see the changelog.