Skip to content

WordPress

Deploy WordPress on Kubernetes with the official docker.io/library/wordpress:6.9.4-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.

Users HTTPS Gateway API or Ingress Service dual-stack optional WordPress official apache image HPA / PDB optional PVC /var/www/html MySQL / MariaDB subchart or external ExternalSecret optional Backup CronJob DB + wp-content S3 bucket compatible 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 pod wp-config.php constants Redis Object Cache plugin + drop-in Redis subchart or external Plugin Job post-install/upgrade requires PVC Shared PVC wp-content/plugins object-cache.php Secret optional password skipped 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: admin@example.com

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: admin@example.com
  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

ParameterTypeDefaultDescription
nameOverridestring""Override chart name.
fullnameOverridestring""Override full release name.
commonLabelsobject{}Labels added to every rendered resource.
replicaCountinteger1WordPress replicas when HPA is disabled.
image.repositorystringdocker.io/library/wordpressWordPress image repository.
image.tagstring6.9.4-apacheImage tag.
image.pullPolicystringIfNotPresentImage pull policy.
imagePullSecretsarray[]Pull secrets for private registries.

WordPress

ParameterTypeDefaultDescription
wordpress.siteUrlstring""Full external site URL.
wordpress.siteTitlestringWordPressSite title.
wordpress.adminUserstringadminInitial admin user.
wordpress.adminPasswordstring""Initial admin password, generated when empty.
wordpress.adminEmailstringadmin@example.comInitial admin email.
wordpress.tablePrefixstringwp_Database table prefix.
wordpress.debugbooleanfalseEnables WP_DEBUG.
wordpress.forceSSLAdminbooleanfalseSets FORCE_SSL_ADMIN.
wordpress.disallowFileEditbooleanfalseSets DISALLOW_FILE_EDIT.
wordpress.disableWpCronbooleanfalseSets DISABLE_WP_CRON.
wordpress.memoryLimitstring""Sets WP_MEMORY_LIMIT.
wordpress.maxMemoryLimitstring""Sets WP_MAX_MEMORY_LIMIT.
wordpress.configExtrastring""Extra PHP appended to wp-config.php.
wordpress.extraEnvarray[]Extra container environment variables.
wordpress.extraEnvFromarray[]Extra envFrom sources.
php.inistring""Custom PHP ini settings mounted into Apache PHP.

Credentials and External Secrets

ParameterTypeDefaultDescription
admin.existingSecretstring""Existing admin Secret.
admin.existingSecretPasswordKeystringadmin-passwordAdmin password key.
externalSecrets.enabledbooleanfalseRender ExternalSecret resources.
externalSecrets.apiVersionstringexternal-secrets.io/v1External Secrets Operator API version.
externalSecrets.secretStoreRef.namestring""SecretStore or ClusterSecretStore name.
externalSecrets.secretStoreRef.kindstringSecretStoreStore kind.
externalSecrets.admin.enabledbooleanfalseManage admin password with ESO.
externalSecrets.database.enabledbooleanfalseManage external database password with ESO.
externalSecrets.backup.enabledbooleanfalseManage backup S3 credentials with ESO.

Database and MySQL Subchart

ParameterTypeDefaultDescription
database.modestringautoauto, external, or mysql.
database.external.hoststring""External database hostname.
database.external.portinteger3306External database port.
database.external.namestringwordpressDatabase name.
database.external.usernamestringwordpressDatabase username.
database.external.passwordstring""Inline external database password.
database.external.existingSecretstring""Existing database password Secret.
database.external.existingSecretPasswordKeystringdatabase-passwordPassword key in Secret.
mysql.enabledbooleantrueDeploy HelmForge MySQL subchart.
mysql.architecturestringstandaloneMySQL architecture.
mysql.auth.databasestringwordpressSubchart database name.
mysql.auth.usernamestringwordpressSubchart database user.
mysql.auth.passwordstring""Subchart user password.
mysql.auth.rootPasswordstring""Subchart root password.
mysql.primary.persistence.enabledbooleantrueEnable MySQL PVC.
mysql.primary.persistence.sizestring8GiMySQL PVC size.

Storage, Service, and Routing

ParameterTypeDefaultDescription
persistence.enabledbooleantrueCreate PVC for /var/www/html.
persistence.storageClassstring""StorageClass name.
persistence.accessModestringReadWriteOncePVC access mode.
persistence.sizestring5GiWordPress PVC size.
persistence.existingClaimstring""Use an existing PVC.
service.typestringClusterIPService type.
service.portinteger80HTTP Service port.
service.ipFamilyPolicystring""SingleStack, PreferDualStack, or RequireDualStack.
service.ipFamiliesarray[]Ordered IP families, for example ["IPv4", "IPv6"].
ingress.enabledbooleanfalseCreate Ingress.
ingress.ingressClassNamestring""Ingress class.
ingress.hostsarray[]Ingress host rules.
ingress.tlsarray[]Ingress TLS entries.
gatewayAPI.enabledbooleanfalseCreate Gateway API HTTPRoute.
gatewayAPI.apiVersionstringgateway.networking.k8s.io/v1HTTPRoute API version.
gatewayAPI.parentRefsarray[]Parent Gateway references.
gatewayAPI.hostnamesarray[]HTTPRoute hostnames.
gatewayAPI.matchesarrayPath prefix /HTTPRoute matches.

Production Controls

ParameterTypeDefaultDescription
serviceAccount.createbooleanfalseCreate a dedicated ServiceAccount.
serviceAccount.automountServiceAccountTokenbooleanfalseMount Kubernetes API token into pods.
autoscaling.enabledbooleanfalseCreate HPA.
autoscaling.minReplicasinteger2HPA minimum replicas.
autoscaling.maxReplicasinteger5HPA maximum replicas.
pdb.enabledbooleanfalseCreate PodDisruptionBudget.
pdb.minAvailableinteger1Minimum available pods.
wpCron.cronJob.enabledbooleanfalseCreate Kubernetes CronJob for wp-cron.php.
wpCron.cronJob.schedulestring*/5 * * * *WordPress cron schedule.
resourcesobject{}WordPress container requests and limits.
podSecurityContextobject{}Pod security context.
securityContextobject{}Container security context.

Plugins and Redis Object Cache

ParameterTypeDefaultDescription
plugins.enabledbooleanfalseEnable plugin installer support.
plugins.installer.enabledbooleanfalseCreate post-install/post-upgrade installer Job.
plugins.installer.activeDeadlineSecondsinteger60Installer timeout.
plugins.installer.backoffLimitinteger1Installer retry limit.
plugins.installer.preferWordPressPodNodebooleantruePrefer node with WordPress pod for RWO PVCs.
plugins.itemsarray[]Official WordPress.org plugin slugs.
objectCache.enabledbooleanfalseEnable object cache integration.
objectCache.providerstringredis-cacheSupported provider.
objectCache.installPluginbooleantrueInstall and activate redis-cache.
objectCache.enableDropInbooleantrueCreate wp-content/object-cache.php.
objectCache.redis.modestringsubchartsubchart or external.
objectCache.redis.subchart.enabledbooleanfalseDeploy HelmForge Redis dependency.
objectCache.redis.external.hoststring""External Redis hostname.
objectCache.redis.portinteger6379Redis port.
objectCache.redis.databaseinteger0Redis database number.
objectCache.redis.auth.enabledbooleantrueUse Redis password when effective Redis auth is enabled.
objectCache.redis.auth.existingSecretstring""Existing Redis password Secret.

NetworkPolicy, Metrics, and Backup

ParameterTypeDefaultDescription
networkPolicy.enabledbooleanfalseCreate NetworkPolicy.
networkPolicy.ingress.allowSameNamespacebooleantrueAllow same namespace ingress.
networkPolicy.ingress.extraFromarray[]Extra application ingress sources.
networkPolicy.metrics.enabledbooleanfalseAdd metrics ingress rule.
networkPolicy.egress.enabledbooleanfalseManage egress allow list.
networkPolicy.egress.allowDNSbooleantrueAllow DNS egress.
networkPolicy.egress.allowSameNamespaceDatabasebooleantrueAllow same-namespace DB egress.
networkPolicy.egress.allowHTTPSbooleanfalseAllow HTTPS egress.
networkPolicy.egress.allowSMTPbooleanfalseAllow SMTP egress.
metrics.enabledbooleanfalseEnable Apache exporter sidecar.
metrics.serviceMonitor.enabledbooleanfalseCreate ServiceMonitor.
backup.enabledbooleanfalseCreate S3-compatible backup CronJob.
backup.schedulestring0 3 * * *Backup schedule.
backup.s3.endpointstring""S3 endpoint URL.
backup.s3.bucketstring""Backup bucket.
backup.s3.existingSecretstring""Existing S3 credentials Secret.
backup.database.mysqldumpArgsstring--single-transaction --quick --skip-lock-tables --no-tablespacesExtra mysqldump arguments.

Scheduling and Extensions

ParameterTypeDefaultDescription
nodeSelectorobject{}Node selector.
tolerationsarray[]Pod tolerations.
affinityobject{}Pod affinity.
topologySpreadConstraintsarray[]Topology spread constraints.
priorityClassNamestring""PriorityClass name.
terminationGracePeriodSecondsinteger30Pod termination grace period.
podLabelsobject{}Extra pod labels.
podAnnotationsobject{}Extra pod annotations.
extraVolumeMountsarray[]Extra WordPress volume mounts.
extraVolumesarray[]Extra pod volumes.
extraInitContainersarray[]Additional init containers.
extraContainersarray[]Additional sidecars.
extraManifestsarray[]Extra Kubernetes manifests.

More Information