Skip to content

WordPress

Deploy WordPress on Kubernetes with the official docker.io/library/wordpress:7.0.0-apache image. The chart keeps the default install useful for development while exposing explicit production controls for database selection, secrets, routing, storage, backup, network policy, metrics, WordPress cron, plugin installation, and Redis Object Cache.

Defaults are for fast development, not a full production baseline

A default install creates one WordPress pod, a HelmForge MySQL subchart, generated credentials, and a ReadWriteOnce PVC. Production deployments should opt in to explicit secrets, TLS routing, resources, backup, NetworkPolicy, and a storage/database strategy that matches the required availability target.

Key Features

  • Official image alignment - uses the upstream WordPress Apache image, with PHP configuration exposed through values.
  • Database choices - HelmForge MySQL subchart by default, or external MySQL/MariaDB through structured values.
  • Secrets paths - generated Kubernetes Secrets, existing Secrets, or optional External Secrets Operator resources.
  • Routing - classic Ingress and native Gateway API HTTPRoute.
  • Networking - dual-stack Service fields and optional NetworkPolicy ingress/egress rules.
  • Production controls - ServiceAccount token mount control, resources, HPA, PDB, deterministic WordPress cron, probes, and scheduling.
  • Backups - S3-compatible CronJob that exports both the database and wp-content.
  • Plugins - idempotent post-install/post-upgrade installer for official WordPress.org plugin slugs.
  • Redis Object Cache - optional redis-cache plugin and drop-in using HelmForge Redis or external Redis.
  • Extensibility - extra env, envFrom, init containers, sidecars, volumes, mounts, and raw extra manifests.

Architecture

Production

Traffic reaches WordPress through Ingress or Gateway API. Credentials can come from Kubernetes Secrets or External Secrets Operator. Backups export both database and content artifacts.

UsersHTTPSGateway APIor IngressServicedual-stack optionalWordPressofficial apache imageHPA / PDB optionalPVC/var/www/htmlMySQL / MariaDBsubchart or externalExternalSecretoptionalBackup CronJobDB + wp-contentS3 bucketcompatible storage

Redis Object Cache

Redis Object Cache is enabled by installing the official plugin, creating the object-cache.php drop-in, and pointing WordPress at a subchart or external Redis endpoint.

WordPress podwp-config.php constantsRedis Object Cacheplugin + drop-inRedissubchart or externalPlugin Jobpost-install/upgraderequires PVCShared PVCwp-content/pluginsobject-cache.phpSecretoptional passwordskipped when auth off

Installation

HTTPS repository:

helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install wordpress helmforge/wordpress -f values.yaml

OCI registry:

helm install wordpress oci://ghcr.io/helmforgedev/helm/wordpress -f values.yaml

For local access without Ingress or Gateway API:

kubectl port-forward svc/wordpress 8080:80

Development vs Production

Use the default install for local validation, demos, and development. It gives you a working WordPress instance quickly with a bundled MySQL database and persistent WordPress files.

For production, make the choices explicit:

  • Use existing Secrets or External Secrets Operator for admin, database, backup, and Redis credentials.
  • Set wordpress.siteUrl, wordpress.forceSSLAdmin, wordpress.disallowFileEdit, and wordpress.disableWpCron.
  • Use Gateway API or Ingress with TLS and route-aware NetworkPolicy ingress rules.
  • Set CPU and memory requests, limits, PDB, and HPA only after storage is safe for multiple pods.
  • Use ReadWriteMany storage or an immutable media/plugin strategy before running multiple replicas.
  • Enable the Kubernetes wpCron CronJob when DISABLE_WP_CRON is set.
  • Test backup and restore, not only backup creation.
Horizontal scaling requires a content strategy

WordPress writes uploads, plugins, themes, and drop-ins under /var/www/html. Keep one replica with the default ReadWriteOnce PVC. Use ReadWriteMany storage or a custom immutable image/object-storage media strategy before enabling HPA or setting replicaCount above 1.

Deployment Examples

# values.yaml - development install with bundled MySQL
wordpress:
  siteTitle: My Blog
  adminUser: admin
  adminPassword: change-me
  adminEmail: [email protected]

mysql:
  enabled: true
  auth:
    password: db-password

persistence:
  enabled: true
  size: 10Gi

php:
  ini: |
    upload_max_filesize = 64M
    post_max_size = 64M
    memory_limit = 256M
# values.yaml - production-oriented baseline
wordpress:
  siteUrl: https://blog.example.com
  siteTitle: My Blog
  adminEmail: [email protected]
  forceSSLAdmin: true
  disallowFileEdit: true
  disableWpCron: true
  memoryLimit: 256M
  maxMemoryLimit: 512M

admin:
  existingSecret: wordpress-admin

mysql:
  enabled: false

database:
  external:
    host: mysql.example.com
    port: 3306
    name: wordpress
    username: wordpress
    existingSecret: wordpress-db
    existingSecretPasswordKey: password

persistence:
  enabled: true
  accessMode: ReadWriteMany
  storageClass: nfs-rwx
  size: 20Gi

service:
  ipFamilyPolicy: PreferDualStack
  ipFamilies:
    - IPv4
    - IPv6

gatewayAPI:
  enabled: true
  parentRefs:
    - name: public
      namespace: gateway-system
      sectionName: https
  hostnames:
    - blog.example.com

networkPolicy:
  enabled: true
  ingress:
    extraFrom:
      - namespaceSelector:
          matchLabels:
            kubernetes.io/metadata.name: gateway-system
  egress:
    enabled: true
    allowDNS: true
    allowSameNamespaceDatabase: false
    allowHTTPS: true

wpCron:
  cronJob:
    enabled: true

autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 5

pdb:
  enabled: true
  minAvailable: 1

resources:
  requests:
    cpu: 250m
    memory: 512Mi
  limits:
    cpu: '1'
    memory: 1Gi
# values.yaml - external MySQL or MariaDB
mysql:
  enabled: false

database:
  mode: external
  external:
    host: mysql.database.svc.cluster.local
    port: 3306
    name: wordpress
    username: wordpress
    existingSecret: wordpress-db
    existingSecretPasswordKey: database-password

admin:
  existingSecret: wordpress-admin
  existingSecretPasswordKey: admin-password

backup:
  enabled: true
  s3:
    endpoint: https://s3.amazonaws.com
    bucket: wordpress-backups
    existingSecret: wordpress-s3
# values.yaml - Gateway API with External Secrets Operator
mysql:
  enabled: false

database:
  external:
    host: mysql.example.com
    name: wordpress
    username: wordpress
    existingSecretPasswordKey: database-password

externalSecrets:
  enabled: true
  secretStoreRef:
    name: vault
    kind: ClusterSecretStore
  admin:
    enabled: true
    passwordRemoteRef:
      key: prod/wordpress
      property: admin-password
  database:
    enabled: true
    passwordRemoteRef:
      key: prod/wordpress
      property: database-password

gatewayAPI:
  enabled: true
  parentRefs:
    - name: public
      namespace: gateway-system
  hostnames:
    - blog.example.com
# values.yaml - Redis Object Cache using HelmForge Redis subchart
plugins:
  enabled: true
  installer:
    enabled: true

objectCache:
  enabled: true
  redis:
    mode: subchart
    subchart:
      enabled: true

redis:
  architecture: standalone
  auth:
    existingSecret: wordpress-redis-auth
    existingSecretPasswordKey: redis-password
# values.yaml - Redis Object Cache using external Redis
plugins:
  enabled: true
  installer:
    enabled: true

objectCache:
  enabled: true
  redis:
    mode: external
    external:
      host: redis.example.com
    auth:
      existingSecret: wordpress-redis
      existingSecretPasswordKey: redis-password
# values.yaml - install official WordPress.org plugins
persistence:
  enabled: true

plugins:
  enabled: true
  installer:
    enabled: true
    activeDeadlineSeconds: 60
  items:
    - slug: classic-editor
      activate: true
      skipIfInstalled: true
    - slug: contact-form-7
      activate: true
      skipIfInstalled: true
The plugin installer requires persistent storage

The installer Job writes files into the WordPress volume. It fails validation when plugins.installer.enabled=true and persistence.enabled=false, because an isolated Job emptyDir would not be visible to the WordPress pod.

# values.yaml - S3-compatible backup of database and wp-content
backup:
  enabled: true
  schedule: '0 3 * * *'
  archivePrefix: wordpress
  s3:
    endpoint: https://s3.amazonaws.com
    bucket: wordpress-backups
    existingSecret: wordpress-s3
    existingSecretAccessKeyKey: access-key
    existingSecretSecretKeyKey: secret-key
  database:
    mysqldumpArgs: '--single-transaction --quick --skip-lock-tables --no-tablespaces'

With External Secrets Operator-managed backup credentials:

backup:
  enabled: true
  s3:
    endpoint: https://s3.amazonaws.com
    bucket: wordpress-backups

externalSecrets:
  enabled: true
  secretStoreRef:
    name: vault
    kind: ClusterSecretStore
  backup:
    enabled: true
    accessKeyRemoteRef:
      key: prod/wordpress-backup
      property: access-key
    secretKeyRemoteRef:
      key: prod/wordpress-backup
      property: secret-key

Operational Notes

Database Mode

When database.mode: auto, the chart resolves the database path in this order:

  1. database.external.host or database.external.existingSecret selects an external database.
  2. mysql.enabled: true selects the HelmForge MySQL subchart.
  3. Rendering fails because WordPress requires MySQL or MariaDB.

WordPress Cron

Set wordpress.disableWpCron: true and enable wpCron.cronJob.enabled when you want deterministic cron execution from Kubernetes instead of request-driven wp-cron.php.

Redis Object Cache

The chart installs the official redis-cache plugin, writes the object-cache.php drop-in, and sets WP_CACHE plus WP_REDIS_* constants. Redis is only functionally validated when WordPress can create cache keys in Redis; a running Redis pod alone is not enough.

If the Redis subchart is used and redis.auth.enabled=false, WordPress does not reference a Redis password Secret. If the subchart uses redis.auth.existingSecret, WordPress and the plugin installer read the same Secret and key.

NetworkPolicy

networkPolicy.enabled isolates application ingress. Add networkPolicy.ingress.extraFrom for an Ingress controller or Gateway controller that runs outside the WordPress namespace. Egress rules are opt-in; list DNS, database, HTTPS, SMTP, or custom destinations before enabling restrictive egress.

Backups and Restore

The backup CronJob writes two artifacts:

  • A compressed database dump from mysqldump.
  • A compressed archive of /var/www/html/wp-content.

Production users should test restore procedures, object storage retention, object lock, and database consistency. A successful backup upload is not a complete disaster recovery validation.

Configuration Reference

Core and Image

Parameter Type Default Description
nameOverride string "" Override chart name.
fullnameOverride string "" Override full release name.
commonLabels object {} Labels added to every rendered resource.
replicaCount integer 1 WordPress replicas when HPA is disabled.
image.repository string docker.io/library/wordpress WordPress image repository.
image.tag string 7.0.0-apache Image tag.
image.pullPolicy string IfNotPresent Image pull policy.
imagePullSecrets array [] Pull secrets for private registries.

WordPress

Parameter Type Default Description
wordpress.siteUrl string "" Full external site URL.
wordpress.siteTitle string WordPress Site title.
wordpress.adminUser string admin Initial admin user.
wordpress.adminPassword string "" Initial admin password, generated when empty.
wordpress.adminEmail string [email protected] Initial admin email.
wordpress.tablePrefix string wp_ Database table prefix.
wordpress.debug boolean false Enables WP_DEBUG.
wordpress.forceSSLAdmin boolean false Sets FORCE_SSL_ADMIN.
wordpress.disallowFileEdit boolean false Sets DISALLOW_FILE_EDIT.
wordpress.disableWpCron boolean false Sets DISABLE_WP_CRON.
wordpress.memoryLimit string "" Sets WP_MEMORY_LIMIT.
wordpress.maxMemoryLimit string "" Sets WP_MAX_MEMORY_LIMIT.
wordpress.configExtra string "" Extra PHP appended to wp-config.php.
wordpress.extraEnv array [] Extra container environment variables.
wordpress.extraEnvFrom array [] Extra envFrom sources.
php.ini string "" Custom PHP ini settings mounted into Apache PHP.

Credentials and External Secrets

Parameter Type Default Description
admin.existingSecret string "" Existing admin Secret.
admin.existingSecretPasswordKey string admin-password Admin password key.
externalSecrets.enabled boolean false Render ExternalSecret resources.
externalSecrets.apiVersion string external-secrets.io/v1 External Secrets Operator API version.
externalSecrets.secretStoreRef.name string "" SecretStore or ClusterSecretStore name.
externalSecrets.secretStoreRef.kind string SecretStore Store kind.
externalSecrets.admin.enabled boolean false Manage admin password with ESO.
externalSecrets.database.enabled boolean false Manage external database password with ESO.
externalSecrets.backup.enabled boolean false Manage backup S3 credentials with ESO.

Database and MySQL Subchart

Parameter Type Default Description
database.mode string auto auto, external, or mysql.
database.external.host string "" External database hostname.
database.external.port integer 3306 External database port.
database.external.name string wordpress Database name.
database.external.username string wordpress Database username.
database.external.password string "" Inline external database password.
database.external.existingSecret string "" Existing database password Secret.
database.external.existingSecretPasswordKey string database-password Password key in Secret.
mysql.enabled boolean true Deploy HelmForge MySQL subchart.
mysql.architecture string standalone MySQL architecture.
mysql.auth.database string wordpress Subchart database name.
mysql.auth.username string wordpress Subchart database user.
mysql.auth.password string "" Subchart user password.
mysql.auth.rootPassword string "" Subchart root password.
mysql.primary.persistence.enabled boolean true Enable MySQL PVC.
mysql.primary.persistence.size string 8Gi MySQL PVC size.

Storage, Service, and Routing

Parameter Type Default Description
persistence.enabled boolean true Create PVC for /var/www/html.
persistence.storageClass string "" StorageClass name.
persistence.accessMode string ReadWriteOnce PVC access mode.
persistence.size string 5Gi WordPress PVC size.
persistence.existingClaim string "" Use an existing PVC.
service.type string ClusterIP Service type.
service.port integer 80 HTTP Service port.
service.ipFamilyPolicy string "" SingleStack, PreferDualStack, or RequireDualStack.
service.ipFamilies array [] Ordered IP families, for example ["IPv4", "IPv6"].
ingress.enabled boolean false Create Ingress.
ingress.ingressClassName string "" Ingress class.
ingress.hosts array [] Ingress host rules.
ingress.tls array [] Ingress TLS entries.
gatewayAPI.enabled boolean false Create Gateway API HTTPRoute.
gatewayAPI.apiVersion string gateway.networking.k8s.io/v1 HTTPRoute API version.
gatewayAPI.parentRefs array [] Parent Gateway references.
gatewayAPI.hostnames array [] HTTPRoute hostnames.
gatewayAPI.matches array Path prefix / HTTPRoute matches.

Production Controls

Parameter Type Default Description
serviceAccount.create boolean false Create a dedicated ServiceAccount.
serviceAccount.automountServiceAccountToken boolean false Mount Kubernetes API token into pods.
autoscaling.enabled boolean false Create HPA.
autoscaling.minReplicas integer 2 HPA minimum replicas.
autoscaling.maxReplicas integer 5 HPA maximum replicas.
pdb.enabled boolean false Create PodDisruptionBudget.
pdb.minAvailable integer 1 Minimum available pods.
wpCron.cronJob.enabled boolean false Create Kubernetes CronJob for wp-cron.php.
wpCron.cronJob.schedule string */5 * * * * WordPress cron schedule.
resources object {} WordPress container requests and limits.
podSecurityContext object {} Pod security context.
securityContext object {} Container security context.

Plugins and Redis Object Cache

Parameter Type Default Description
plugins.enabled boolean false Enable plugin installer support.
plugins.installer.enabled boolean false Create post-install/post-upgrade installer Job.
plugins.installer.activeDeadlineSeconds integer 60 Installer timeout.
plugins.installer.backoffLimit integer 1 Installer retry limit.
plugins.installer.preferWordPressPodNode boolean true Prefer node with WordPress pod for RWO PVCs.
plugins.items array [] Official WordPress.org plugin slugs.
objectCache.enabled boolean false Enable object cache integration.
objectCache.provider string redis-cache Supported provider.
objectCache.installPlugin boolean true Install and activate redis-cache.
objectCache.enableDropIn boolean true Create wp-content/object-cache.php.
objectCache.redis.mode string subchart subchart or external.
objectCache.redis.subchart.enabled boolean false Deploy HelmForge Redis dependency.
objectCache.redis.external.host string "" External Redis hostname.
objectCache.redis.port integer 6379 Redis port.
objectCache.redis.database integer 0 Redis database number.
objectCache.redis.auth.enabled boolean true Use Redis password when effective Redis auth is enabled.
objectCache.redis.auth.existingSecret string "" Existing Redis password Secret.

NetworkPolicy, Metrics, and Backup

Parameter Type Default Description
networkPolicy.enabled boolean false Create NetworkPolicy.
networkPolicy.ingress.allowSameNamespace boolean true Allow same namespace ingress.
networkPolicy.ingress.extraFrom array [] Extra application ingress sources.
networkPolicy.metrics.enabled boolean false Add metrics ingress rule.
networkPolicy.egress.enabled boolean false Manage egress allow list.
networkPolicy.egress.allowDNS boolean true Allow DNS egress.
networkPolicy.egress.allowSameNamespaceDatabase boolean true Allow same-namespace DB egress.
networkPolicy.egress.allowHTTPS boolean false Allow HTTPS egress.
networkPolicy.egress.allowSMTP boolean false Allow SMTP egress.
metrics.enabled boolean false Enable Apache exporter sidecar.
metrics.serviceMonitor.enabled boolean false Create ServiceMonitor.
backup.enabled boolean false Create S3-compatible backup CronJob.
backup.schedule string 0 3 * * * Backup schedule.
backup.s3.endpoint string "" S3 endpoint URL.
backup.s3.bucket string "" Backup bucket.
backup.s3.existingSecret string "" Existing S3 credentials Secret.
backup.database.mysqldumpArgs string --single-transaction --quick --skip-lock-tables --no-tablespaces Extra mysqldump arguments.

Scheduling and Extensions

Parameter Type Default Description
nodeSelector object {} Node selector.
tolerations array [] Pod tolerations.
affinity object {} Pod affinity.
topologySpreadConstraints array [] Topology spread constraints.
priorityClassName string "" PriorityClass name.
terminationGracePeriodSeconds integer 30 Pod termination grace period.
podLabels object {} Extra pod labels.
podAnnotations object {} Extra pod annotations.
extraVolumeMounts array [] Extra WordPress volume mounts.
extraVolumes array [] Extra pod volumes.
extraInitContainers array [] Additional init containers.
extraContainers array [] Additional sidecars.
extraManifests array [] Extra Kubernetes manifests.

More Information