Hosting WordPress on Kubernetes with Traefik and a Wildcard Certificate

After successfully setting up my wildcard certificate using cert-manager (as I covered in my previous guide), I decided to take the next step — deploying my personal WordPress site, thedougie.com, on my Kubernetes cluster.

By switching to Cloudflared and Traefik (see my recent post), I’ve eliminated the need for firewall holes to expose services directly, simplifying my design and improving security. Resource utilization has remained the same — and I haven’t noticed any difference in response times nor did I expect to.


Step 1: Using the Wildcard Certificate

Since I already had *.thedougie.com issued through Let’s Encrypt via cert-manager, I reused that certificate (wildcard-cert-prod-thedougie-tls) for this deployment. This allowed me to skip creating individual certs per app and focus directly on the deployment.


Step 2: Deploying WordPress via Helm (Ansible Playbook)

Below is my sanitized Ansible playbook for deploying WordPress (with MariaDB) using the Bitnami Helm chart.
Anything in {{ double curly braces }} should be defined in your Ansible vars file, ideally stored in an Ansible Vault — especially sensitive values like passwords or API keys.

# wordpress-install.yml
- name: Install WordPress and MySQL
  hosts: master
  gather_facts: false
  vars:
    desired_state: present
  tasks:
    - name: Deploy WordPress via Helm
      kubernetes.core.helm:
        kubeconfig: /etc/rancher/k3s/k3s.yaml
        validate_certs: false
        name: thedougie-wordpress
        chart_ref: oci://registry-1.docker.io/bitnamicharts/wordpress
        create_namespace: true
        release_namespace: thedougie-wordpress
        state: "{{ desired_state }}"
        timeout: 10m
        wait: true
        values:
          clusterDomain: cluster.local
          persistence:
            enabled: true
            size: 15Gi
            accessModes: [ReadWriteMany]
            annotations:
              helm.sh/resource-policy: keep
          mariadb:
            enabled: true
            architecture: standalone
            auth:
              username: bn_wordpress
              database: bitnami_wordpress
              password: "{{ wordpress_db_password }}"
              rootPassword: "{{ wordpress_db_root_password }}"
            primary:
              persistence:
                enabled: true
                size: 20Gi
                accessModes: [ReadWriteOnce]
                annotations:
                  helm.sh/resource-policy: keep
          resources:
            requests:
              cpu: 1500m
              memory: 1024Mi
            limits:
              cpu: 3000m
              memory: 2048Mi
          wordpressBlogName: "The Dougie Chronicles"
          wordpressEmail: "ad***@*******ie.com"
          wordpressFirstName: "Dougie"
          wordpressLastName: "Smash"
          wordpressPassword: "{{ wordpress_admin_password }}"
          wordpressScheme: http
          service:
            type: ClusterIP

Example vars file snippet (unencrypted):

# group_vars/all/vars.yml
wordpress_admin_password: changeme123
wordpress_db_password: mydbpassword
wordpress_db_root_password: rootpassword

💡 Tip: Once verified, encrypt this file using Ansible Vault:

ansible-vault encrypt group_vars/all/vars.yml

Step 3: Routing Through Traefik

With the WordPress service running inside the cluster, the next step is exposing it externally through Traefik, Kubernetes’ default ingress controller on k3s.
Here’s a simplified version of my IngressRoute manifest applied via Ansible:

- name: Add WordPress to Traefik
  hosts: master
  gather_facts: false
  vars:
    desired_state: present
  tasks:
    - name: Create Traefik IngressRoute for www.thedougie.com
      kubernetes.core.k8s:
        kubeconfig: /etc/rancher/k3s/k3s.yaml
        state: "{{ desired_state }}"
        definition:
          apiVersion: traefik.containo.us/v1alpha1
          kind: IngressRoute
          metadata:
            name: thedougie-wordpress
            namespace: thedougie-wordpress
          spec:
            entryPoints:
              - websecure
            routes:
              - kind: Rule
                match: Host(`www.thedougie.com`)
                services:
                  - name: thedougie-wordpress
                    port: 80
            tls:
              secretName: wildcard-cert-prod-thedougie-tls

This connects the wildcard certificate issued by cert-manager to your Traefik ingress route, enabling HTTPS for your site.


Step 4: Verifying Everything

Once deployed, verify each step with kubectl:

# Confirm namespace and pods are running
kubectl get ns
kubectl get pods -n thedougie-wordpress

# Check WordPress service
kubectl get svc -n thedougie-wordpress

# Verify Traefik IngressRoute
kubectl get ingressroute -n thedougie-wordpress

# Inspect TLS secret
kubectl get secret wildcard-cert-prod-thedougie-tls -n cert-manager

If all looks good, browsing to https://www.thedougie.com should take you straight to your fresh WordPress setup — secured by Cloudflare, fronted by Traefik, and running neatly on Kubernetes.

Leave a Comment