How to run a WordPress on Kubernetes on port 80

Cluster information:

Kubernetes version: v1.32.3
Cloud being used: bare metal
Installation method: kubeadm
Host OS: Ubuntu 20
CNI and version: Calico v3.25.0
CRI and version: Not sure, but containerd --version shows 1.7.24

It’s been around a month or more that I’m struggling this issue and it’s really driving me crazy:(

What I’m looking for

Deploying a simple WordPress application on K8S cluster on port 80 (not any other NodePort ports) and only on specific domain (like wp.saeed.com).

These are steps I did so far (and as I googled and used AIs also):

  1. Installed K8S cluster using apt install -y kubelet kubectl kubeadm on one worker and one master (that’s for testing only, but I can increase server numbers if needed). Both can see each other and kubectl get nodes shows both Ready.

  2. Run these commands on master:

kubeadm init --control-plane-endpoint="$(hostname)" --upload-certs --pod-network-cidr=192.168.0.0/16
mkdir -p $HOME/.kube
ln -sf /etc/kubernetes/admin.conf $HOME/.kube/config

kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/calico.yaml

ssh -n worker1 "mkdir -pv $HOME/.kube"
rsync /etc/kubernetes/admin.conf worker1:$HOME/.kube/config
token=$(kubeadm token create --print-join-command)
ssh -n worker1 "$token"
sleep 15

kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.12/config/manifests/metallb-native.yaml
kubectl wait --namespace metallb-system \
    --for=condition=ready pod \
    --selector=app=metallb \
    --timeout=90s

sleep 30
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/cloud/deploy.yaml
sleep 30

cat <<EOF > /root/metallb.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: default-pool
  namespace: metallb-system
spec:
  addresses:
    - 192.168.100.10/32
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: default-l2
  namespace: metallb-system
spec:
  ipAddressPools:
    - default-pool
  interfaces:
    - $(ip a | grep $(hostname -I | awk -F ' ' '{print $1}') | awk -F ' ' '{print $NF}')
EOF
kubectl apply -f /root/metallb.yaml
rm -vf /root/metallb.yaml

That’s how I run a cluster, deploying ingress-controller, calico and metallb.

I use metallb because I have a dedicated server with some IPs (let’s suppose my public available IPs are 192.168.100.10-192.168.100.20).

I specified 192.168.100.11 to the master as fixed IP in netplan file, and 192.168.100.12 to the worker as Floating IP (I mean this IP is not shown in the ip a output command).

Both worker and master are in a private network 10.10.73.0/24 that can see and ping each other.

Master has two IPs: private and public, while worker has one private only.

  1. After setting everything up, I do these:
  • create namespace
  • create role_based_authorization (rbac) to ensure every namespace can only see its things:
"rules": [
    {
        "apiGroups": [""],
        "resources": ["pods", "services", "deployments"],
        "verbs": ["get", "list", "watch", "create", "update", "patch", "delete"]
    }
]
  • create RoleBinding
  • create deployment on that namespace, and application like wordpress, and containerPort: 80
  • create service like this:
if self.create_service:
    service_manifest = {
        "apiVersion": "v1",
        "kind": "Service",
        "metadata": {
            "name": self.name,
            "namespace": self.namespace.name
        },
        "spec": {
            "type": "ClusterIP",
            "selector": {
                "app": self.name
            },
            "ports": [
                {
                    "protocol": "TCP",
                    "port": self.application.port,
                    "targetPort": self.application.port
                }
            ]
        }
    }
  • create ingress on that namespace, with things like:
ingress_manifest = {
    "apiVersion": "networking.k8s.io/v1",
    "kind": "Ingress",
    "metadata": {
        "name": f"{self.name}-ingress",
        "namespace": self.namespace.name,
        "annotations": {
            "nginx.ingress.kubernetes.io/rewrite-target": "/"
        }
    },
    "spec": {
        'ingressClassName': 'nginx',
        "rules": [
            {
                "host": self.domain,
                "http": {
                    "paths": [
                        {
                            "path": "/",
                            "pathType": "Prefix",
                            "backend": {
                                "service": {
                                    "name": self.name,
                                    "port": {
                                        "number": self.application.port
                                    }
                                }
                            }
                        }
                    ]
                }
            }
        ]
    }
}

I also edit /etc/hosts to point domain to both worker or master IP, but wp.saeed.com shows Connection Refused.

Did I write enough information regarding the issue?

How can I reach what I’m looking for?

1 Like

Hi,

I am Sanjay from India. I have 10 years of experience in this industry. Right now I am working as a project manager. Still, I do love participating in several forums.
I am trying to help you here with my knowledge.

See if you want to run WordPress on Kubernetes and make it available on your domain like wp.saeed.com (and have it work on port 80), here’s how you can do it step by step:

1. Set Up WordPress and MySQL

First, you need to install WordPress and a MySQL database in your Kubernetes cluster. Use Kubernetes YAML files to set them up, and make sure you use volumes so that your data is saved even if the pod restarts.

You can follow this helpful Kubernetes guide to get started.


2. Install an Ingress Controller

To make WordPress available on port 80, you’ll need something called an Ingress Controller. It handles incoming web traffic.

Run this command to install NGINX Ingress Controller:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.2.1/deploy/static/provider/cloud/deploy.yaml

3. Create an Ingress Rule

Now tell Kubernetes how to route traffic to your WordPress site. Here’s a sample rule:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: wordpress-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: wp.saeed.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: wordpress
            port:
              number: 80

Save this in a file called wordpress-ingress.yaml, then apply it with:

kubectl apply -f wordpress-ingress.yaml

4. Update Your Domain’s DNS

Get the external IP address of your Ingress controller using this:

kubectl get services -o wide -w -n ingress-nginx

Then go to your domain provider (like GoDaddy or Namecheap) and point wp.saeed.com to that IP address by adding an A record.


5. (Optional) Add HTTPS

If you want your site to be secure (use HTTPS), you can install Cert-Manager. It helps get free SSL certificates automatically.

Install it using:

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

Then follow a guide like this one to finish the setup.

That’s it! Now your WordPress site should work on wp.saeed.com over port 80, and optionally HTTPS if you added that part.

If you have still any issue please feel free to connect. I will try to help you out.