Skip to content

Post 9: Deploying WordPress on k3s


Goal: Deploy a self-hosted WordPress site on your k3s cluster using:

  • Longhorn for persistent config storage
  • MetalLB for a static IP
  • Cloudflare Tunnel for external access

This is a standard procedure for publishing most apps going forward.


Step 1: Prerequisites

Make sure you:

  • Have a working k3s cluster
  • Installed Longhorn and set up a default StorageClass
  • Have a running NFS server (e.g., TrueNAS at 10.0.0.3)
  • Have MetalLB configured (we’ll use IP 10.0.0.35)
  • Have kubectl configured from your jumpbox

📁 Step 2: Create Namespace

kubectl create namespace web

💾 Step 3: Create Persistent Volume Claims

Save the following as wordpress-pvc.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wordpress-config
  namespace: web
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: longhorn
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wordpress-media
  namespace: web
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 500Gi
  nfs:
    path: /mnt/tank/data/wordpress
    server: 10.0.0.3

Apply it:

kubectl apply -f wordpress-pvc.yaml

🐬 Step 4: Deploy MariaDB

Save this as mariadb.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mariadb-data
  namespace: web
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: longhorn
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mariadb
  namespace: web
spec:
  selector:
    matchLabels:
      app: mariadb
  replicas: 1
  template:
    metadata:
      labels:
        app: mariadb
    spec:
      containers:
        - name: mariadb
          image: mariadb:10.6
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: my-secret-pw
            - name: MYSQL_DATABASE
              value: wordpress
            - name: MYSQL_USER
              value: wordpress
            - name: MYSQL_PASSWORD
              value: wordpresspass
          volumeMounts:
            - mountPath: /var/lib/mysql
              name: mariadb-storage
      volumes:
        - name: mariadb-storage
          persistentVolumeClaim:
            claimName: mariadb-data
---
apiVersion: v1
kind: Service
metadata:
  name: mariadb
  namespace: web
spec:
  ports:
    - port: 3306
  selector:
    app: mariadb

Apply it:

kubectl apply -f mariadb.yaml

🌐 Step 5: Deploy WordPress

Save as wordpress.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  namespace: web
spec:
  selector:
    matchLabels:
      app: wordpress
  replicas: 1
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
        - name: wordpress
          image: wordpress:latest
          env:
            - name: WORDPRESS_DB_HOST
              value: mariadb.web.svc.cluster.local
            - name: WORDPRESS_DB_NAME
              value: wordpress
            - name: WORDPRESS_DB_USER
              value: wordpress
            - name: WORDPRESS_DB_PASSWORD
              value: wordpresspass
          volumeMounts:
            - mountPath: /var/www/html
              name: wordpress-storage
            - mountPath: /var/www/html/wp-content/uploads
              name: media-storage
      volumes:
        - name: wordpress-storage
          persistentVolumeClaim:
            claimName: wordpress-config
        - name: media-storage
          persistentVolumeClaim:
            claimName: wordpress-media
---
apiVersion: v1
kind: Service
metadata:
  name: wordpress
  namespace: web
spec:
  selector:
    app: wordpress
  ports:
    - port: 80
      targetPort: 80
  type: LoadBalancer
  loadBalancerIP: 10.0.0.35

Apply it:

kubectl apply -f wordpress.yaml

✅ Step 6: Accessing WordPress

Once deployed:

  • You can access WordPress at http://10.0.0.35 if on your LAN
  • If you have a Cloudflare Tunnel set up, your domain name should now work, otherwise set up cloudflared.

Tip: you can ask Chat-GPT to generate the yaml files for you as long as you provide lots of information as context.

Published inKubernetes