← All posts

Production WordPress on K3s

January 19, 2026 kubernetesk3shelmwordpress
Production web architecture on a compact Kubernetes cluster with SSL and storage components

A production-ready WordPress setup on K3s, with a Helm chart for fully automated SSL and a scalable architecture. How, and why?

Awesome Helm Kubernetes License: MIT WordPress

Why K3s, and why this solution?

When it comes to deploying modern web applications, Kubernetes has become the industry standard. But setting up and running a full Kubernetes cluster can be complex and resource-heavy, especially for small and mid-sized projects. That’s exactly where K3s comes in.

This post is a deep dive into the production-ready Helm chart I built for running WordPress on K3s. You can find the GitHub repo here.

What is K3s, and why use it?

Advantages of K3s

K3s is a lightweight, production-ready Kubernetes distribution from Rancher Labs. It delivers the full Kubernetes experience at a fraction of the resource cost.

1. Minimal resource usage

2. Easy install and management

curl -sfL https://get.k3s.io | sh -

3. Built-in components

K3s ships by default with components you’d otherwise have to install separately on standard Kubernetes:

4. Production-ready

Disadvantages of K3s

Despite all these advantages, K3s isn’t the right fit for every scenario:

1. Limited plugin ecosystem

2. Swapping out default components

3. Community and documentation

4. High-availability complexity

Why I built this Helm chart

WordPress still powers over 43% of the internet. But setting up a modern, container-based WordPress deployment kept running into a few recurring problems:

Problems I kept hitting

  1. Manual SSL certificate management: renewing Let’s Encrypt certificates by hand is time-consuming
  2. Scattered configuration: separate YAML files for WordPress, MySQL, and Redis
  3. Security concerns: passwords and secrets stored in plain text
  4. Persistence issues: data loss after pod restarts
  5. Lack of scalability: traffic spikes require manual intervention

My solution

A single Helm chart that solves all of these problems, designed to be production-ready:

helm install myblog wordpress-helm/ -f my-values.yaml

Quick install

To get an SSL-secured WordPress site running on a fresh Ubuntu/Debian server in under 5 minutes:

One-shot script

DOMAIN="${1:-example.com}"
EMAIL="${2:-admin@example.com}"

echo "Starting K3s WordPress setup..."
echo "Domain: $DOMAIN"
echo "Email: $EMAIL"
echo ""

echo "Installing K3s..."
curl -sfL https://get.k3s.io | sh -
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
sleep 10

echo "Installing cert-manager..."
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml
kubectl wait --for=condition=ready pod -l app=cert-manager -n cert-manager --timeout=300s

echo "Installing Helm..."
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

echo "Fetching the Helm chart..."
cd /tmp
git clone https://github.com/cagatayuresin/awesome-k3s-wordpress-helm.git
cd awesome-k3s-wordpress-helm

echo "Generating secure passwords..."
MYSQL_ROOT_PASS=$(openssl rand -base64 32)
MYSQL_WP_PASS=$(openssl rand -base64 32)
REDIS_PASS=$(openssl rand -base64 32)

echo "Preparing configuration..."
cat > quick-values.yaml <<EOF
domain: $DOMAIN
enableWwwRedirect: true

letsencrypt:
  email: $EMAIL

mysql:
  rootPassword: "$MYSQL_ROOT_PASS"
  password: "$MYSQL_WP_PASS"

redis:
  enabled: true
  password: "$REDIS_PASS"

wordpress:
  replicas: 1
  resources:
    requests:
      memory: "256Mi"
      cpu: "250m"
    limits:
      memory: "512Mi"
      cpu: "500m"
EOF

echo "Deploying WordPress..."
helm install myblog wordpress-helm/ -f quick-values.yaml

echo "Waiting for pods to be ready..."
kubectl wait --for=condition=ready pod -l app=wordpress -n wordpress --timeout=300s

echo ""
echo "Setup complete!"
echo ""
echo "Important info:"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Site URL: https://$DOMAIN"
echo "MySQL root password: $MYSQL_ROOT_PASS"
echo "WordPress DB password: $MYSQL_WP_PASS"
echo "Redis password: $REDIS_PASS"
echo ""
echo "Save these passwords somewhere safe!"
echo ""
echo "Status check:"
echo "  kubectl get pods -n wordpress"
echo "  kubectl get certificate -n wordpress"
echo ""
echo "Visit https://$DOMAIN to finish the WordPress setup"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

Manual quick install (step by step)

If you’d rather go step by step instead of using the script:

curl -sfL https://get.k3s.io | sh -
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml
kubectl wait --for=condition=ready pod -l app=cert-manager -n cert-manager --timeout=300s

curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

git clone https://github.com/cagatayuresin/awesome-k3s-wordpress-helm.git
cd awesome-k3s-wordpress-helm

cat > my-values.yaml <<EOF
domain: yourdomain.com
letsencrypt:
  email: your-email@yourdomain.com
mysql:
  rootPassword: "$(openssl rand -base64 32)"
  password: "$(openssl rand -base64 32)"
EOF

helm install myblog wordpress-helm/ -f my-values.yaml

kubectl get pods -n wordpress
kubectl get certificate -n wordpress

One-liner (if K3s is already installed)

If K3s and Helm are already in place:

git clone https://github.com/cagatayuresin/awesome-k3s-wordpress-helm.git && \
cd awesome-k3s-wordpress-helm && \
cat > my-values.yaml <<EOF
domain: yourdomain.com
letsencrypt:
  email: your@email.com
mysql:
  rootPassword: "change-me-$(date +%s)"
  password: "change-me-$(date +%s)"
EOF
helm install myblog wordpress-helm/ -f my-values.yaml

Post-install checks

kubectl get pods -n wordpress

kubectl get certificate -n wordpress

kubectl get ingress -n wordpress

kubectl logs -f deployment/myblog-wordpress -n wordpress

Expected timing

StepDuration
K3s install~30 seconds
cert-manager install~2 minutes
Helm install~30 seconds
WordPress deployment~1 minute
SSL certificate issuance~30-60 seconds
TOTAL~5 minutes

Important notes

  1. DNS: your domain’s A record needs to point at your server’s IP
  2. Firewall: ports 80 and 443 must be open
  3. Root access: commands must be run as root or with sudo
  4. Server requirements: minimum 2GB RAM, 2 vCPU

Architecture and components

System architecture

awesome-k3s-wordpress-helm diagram

Main components

1. WordPress deployment

wordpress:
  image: wordpress:6.9.0-php8.2-apache
  replicas: 1
  resources:
    requests: { memory: "256Mi", cpu: "250m" }
    limits: { memory: "512Mi", cpu: "500m" }

Features:

2. MySQL 8.0 database

3. Redis object cache (optional)

4. Traefik ingress controller

5. cert-manager + Let’s Encrypt

letsencrypt:
  email: admin@domain.com
  server: https://acme-v02.api.letsencrypt.org/directory

6. Persistent storage

Strengths of the Helm chart

Pros

1. One-command deploy

helm install myblog wordpress-helm/ -f my-values.yaml

An SSL-secured, hardened WordPress site within minutes.

2. Simple configuration

Every setting lives in a single values.yaml file:

domain: cagatayuresin.com
letsencrypt:
  email: admin@cagatayuresin.com
mysql:
  rootPassword: "strong-password-123"
redis:
  enabled: true

3. Production-ready defaults

4. Security

5. Easy upgrades

helm upgrade myblog wordpress-helm/ \
  --set wordpress.image.tag=6.9.1-php8.2-apache

6. Observability

kubectl logs -f deployment/myblog-wordpress -n wordpress

kubectl get pods -n wordpress

7. Backup and restore

Thanks to PersistentVolumes:

kubectl cp wordpress/myblog-wordpress-0:/var/www/html ./backup

kubectl cp ./backup wordpress/myblog-wordpress-0:/var/www/html

Cons and limitations

1. Single point of failure (by default)

Fix:

wordpress:
  replicas: 3

2. Persistent volume management

Fix:

persistence:
  storageClass: nfs-client

3. Manual Redis integration

4. No database migration support

5. No multi-tenancy

6. No built-in monitoring/alerting

Fix: as a separate stack:

helm install prometheus prometheus-community/kube-prometheus-stack

Scalability

Horizontal scaling

WordPress replicas

wordpress:
  replicas: 5

Things to watch out for:

MySQL replication

The default install doesn’t ship with master-slave support, but it can be added:

mysql:
  replication:
    enabled: true
    replicas: 2

Vertical scaling

Bumping resource limits:

wordpress:
  resources:
    requests:
      memory: "1Gi"
      cpu: "1000m"
    limits:
      memory: "2Gi"
      cpu: "2000m"

Auto-scaling (HPA)

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

kubectl autoscale deployment myblog-wordpress \
  --cpu-percent=70 \
  --min=2 \
  --max=10 \
  -n wordpress

WordPress pods scale automatically based on CPU usage.

Storage scaling

persistence:
  wordpress:
    size: 50Gi
  mysql:
    size: 100Gi

Caching strategies

Redis object cache

redis:
  enabled: true
  maxMemory: "512mb"
  replicas: 3

CDN integration

Via WordPress plugins:

Load testing results

Test scenario: 100 concurrent users, 10,000 requests

ConfigurationResponse timeThroughput
1 WordPress pod, no Redis450ms150 req/s
1 WordPress pod, with Redis180ms380 req/s
3 WordPress pods, with Redis95ms850 req/s
5 WordPress pods, with Redis, CDN35ms1500 req/s

Security best practices

1. Strong passwords

openssl rand -base64 32

2. Network policies

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: wordpress-netpol
spec:
  podSelector:
    matchLabels:
      app: wordpress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: traefik

3. Security scanning

trivy image wordpress:6.9.0-php8.2-apache

4. RBAC

apiVersion: v1
kind: ServiceAccount
metadata:
  name: wordpress-sa

5. Secret encryption at rest

Enable it in K3s:

secrets-encryption: true

Step-by-step install

1. Install K3s

curl -sfL https://get.k3s.io | sh -

export KUBECONFIG=/etc/rancher/k3s/k3s.yaml

kubectl get nodes

2. Install cert-manager

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml

kubectl wait --for=condition=ready pod -l app=cert-manager -n cert-manager --timeout=300s

3. Install Helm

curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

4. Configure DNS

At your domain provider, add an A record:

Type: A
Name: @ (or subdomain)
Value: <your-server-ip>
TTL: 300

And for the www subdomain:

Type: A
Name: www
Value: <your-server-ip>
TTL: 300

5. Deploy the Helm chart

git clone https://github.com/cagatayuresin/awesome-k3s-wordpress-helm.git
cd awesome-k3s-wordpress-helm

cat > my-values.yaml <<EOF
domain: myblog.com
enableWwwRedirect: true

letsencrypt:
  email: admin@myblog.com

mysql:
  rootPassword: "$(openssl rand -base64 32)"
  password: "$(openssl rand -base64 32)"

redis:
  enabled: true
  password: "$(openssl rand -base64 32)"

wordpress:
  replicas: 2
  resources:
    requests:
      memory: "512Mi"
      cpu: "500m"
    limits:
      memory: "1Gi"
      cpu: "1000m"
EOF

helm install myblog wordpress-helm/ -f my-values.yaml

6. Check the deployment

kubectl get pods -n wordpress

kubectl get certificate -n wordpress

kubectl logs -f deployment/myblog-wordpress -n wordpress

7. Finish the WordPress setup

Open https://yourdomain.com in your browser and complete the WordPress setup wizard.

Redis integration (optional)

  1. Log in to the WordPress admin panel
  2. Plugins → Add New → search for “Redis Object Cache”
  3. Install and activate the plugin
  4. Settings → Redis → click “Enable Object Cache”

Maintenance and upgrades

Upgrading WordPress

helm upgrade myblog wordpress-helm/ \
  -f my-values.yaml \
  --set wordpress.image.tag=6.9.1-php8.2-apache

Upgrading MySQL (carefully!)

kubectl exec -n wordpress myblog-mysql-0 -- \
  mysqldump -u root -p"$ROOT_PASSWORD" --all-databases > backup.sql

helm upgrade myblog wordpress-helm/ \
  -f my-values.yaml \
  --set mysql.image.tag=8.0.35

Upgrading the Helm chart

git pull origin main

helm upgrade myblog wordpress-helm/ -f my-values.yaml

Troubleshooting

SSL certificate not issuing

Check:

kubectl describe certificate -n wordpress

kubectl logs -n cert-manager deploy/cert-manager

Likely causes:

  1. DNS hasn’t fully propagated yet (wait up to 24 hours)
  2. Ports 80/443 aren’t open in the firewall
  3. Let’s Encrypt rate limit hit (use the staging server)

WordPress “Error establishing database connection”

Check:

kubectl get pods -n wordpress

kubectl logs -n wordpress myblog-mysql-0

kubectl exec -n wordpress myblog-wordpress-0 -- \
  nc -zv myblog-mysql 3306

Slow performance

Optimization checklist:

  1. Enable Redis
  2. Increase the WordPress replica count
  3. Use a CDN
  4. Raise resource limits
  5. Check whether OPcache is enabled

Pods keep restarting

kubectl describe pod -n wordpress <pod-name>

kubectl top pod -n wordpress

Alternatives and comparison

1. Bitnami WordPress Helm chart

Pros:

Cons:

2. Docker Compose

Pros:

Cons:

3. Managed WordPress (WP Engine, Kinsta)

Pros:

Cons:

This solution (K3s + Helm chart)

Best fit when:

Conclusion

Running WordPress on K3s strikes an excellent balance between performance, cost, and flexibility. With this Helm chart you get:

Who it’s for

Who it’s not for

Resources