/ Docs / 06 — WordPress in K8s
Document 06 · WordPress in Kubernetes
Running WordPress as a Kubernetes workload

How the original WordPress site was containerised and deployed into the wordpress6 namespace — two pods, MySQL ClusterIP, MetalLB LoadBalancer, NFS content storage, and nginx path rewriting.

What runs in wordpress6
kubectl get all -n wordpress6

NAME                                READY   STATUS
pod/mysql-67bc7d8f75-v5gd4         1/1     Running
pod/wordpress-bfc789658-lt9gz      1/1     Running
pod/wordpress-bfc789658-pn27z      1/1     Running

NAME                TYPE           CLUSTER-IP      EXTERNAL-IP
service/mysql       ClusterIP      10.98.30.125    <none>
service/wordpress   LoadBalancer   10.97.215.231   10.99.101.205

NAME                        READY   UP-TO-DATE
deployment.apps/mysql       1/1     1
deployment.apps/wordpress   2/2     2
MySQL is ClusterIP: MySQL is intentionally not exposed outside the cluster. Only the WordPress pods can reach it via the internal mysql service hostname. This is the correct pattern — databases should never be publicly accessible.
WordPress Deployment

WordPress runs as a Deployment with 2 replicas — giving rolling update capability and basic redundancy. If one pod dies, the other continues serving traffic while Kubernetes restarts it.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  namespace: wordpress6
spec:
  replicas: 2
  selector:
    matchLabels:
      app: wordpress
  template:
    spec:
      containers:
        - name: wordpress
          image: wordpress:php8.3-apache
          env:
            - name: WORDPRESS_DB_HOST
              value: mysql          # resolves via CoreDNS
            - name: WORDPRESS_DB_NAME
              value: wordpress
            - name: WORDPRESS_DB_USER
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: username
            - name: WORDPRESS_DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: password
Database hostname: WordPress connects to MySQL using the service name mysql as the hostname. CoreDNS resolves this to the MySQL ClusterIP automatically. No hardcoded IPs needed.
Path rewriting — the /wp/ prefix

The WordPress site is served at mfa2fa.com/wp/ rather than the root. WordPress hard-codes its own URL in the database, so two things are needed:

1. nginx rewrite on OVH — strips the /wp prefix before proxying to the cluster, so WordPress receives requests at / not /wp/:

location /wp {
    rewrite ^/wp(/.*)$ $1 break;
    proxy_pass http://10.99.101.205;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
}

2. wp-cli URL update — tells WordPress its own URL has changed:

# Exec into the WordPress pod
kubectl exec -n wordpress6 -it <wordpress-pod> -- bash

# Update both URL options
wp option update siteurl 'https://mfa2fa.com/wp' --allow-root
wp option update home    'https://mfa2fa.com/wp' --allow-root
⚠ Without updating siteurl and home in the database, WordPress will redirect all requests back to the old URL — breaking the site completely. These two wp-cli commands are mandatory after moving WordPress to a subpath.
WordPress content on NFS

The wp-content directory (uploads, plugins, themes) is stored on NFS so both WordPress pods share the same files:

# NFS share for WordPress content
192.168.0.165:/srv/nfs/wp-content

# Mounted at /var/www/html/wp-content in both pods
# Owner: 33 (www-data) — the Apache user inside the container
✓ Both WordPress pods mount the same NFS share — uploads made through one pod are immediately visible to the other. Without NFS, uploads would only exist on the node where the uploading pod ran.
Operational commands
# Check pod status
kubectl get pods -n wordpress6 -o wide

# Tail WordPress logs
kubectl logs -n wordpress6 -l app=wordpress -f

# Exec into WordPress pod
kubectl exec -n wordpress6 -it \
  $(kubectl get pod -n wordpress6 -l app=wordpress \
    -o jsonpath='{.items[0].metadata.name}') -- bash

# Run wp-cli commands
kubectl exec -n wordpress6 -it <pod> -- \
  wp option list --allow-root

# Rolling restart (picks up new image or config)
kubectl rollout restart deployment/wordpress -n wordpress6

# Check rollout status
kubectl rollout status deployment/wordpress -n wordpress6

# Scale to 1 pod temporarily (maintenance)
kubectl scale deployment wordpress -n wordpress6 --replicas=1

# Scale back to 2
kubectl scale deployment wordpress -n wordpress6 --replicas=2