Deploy a 3 tier app using Kubernetes

Raphael Ndonga | Oct 7, 2025 min read

I have published two public docker images on DockerHub:

  • raphaelndonga/wazijobs-frontend
  • raphaelndonga/wazijobs-backend

…and a postgresql database hosted on Railway

Approach

  1. Deploy the backend on a pod and expose it through a service.
  2. Deploy the frontend on a pod and expose it through a service.
  3. Connect the frontend and the backend through the established services.

1. Backend Implementation

Create the pod for the backend at wazijobs-backend-pod.yaml:

apiVersion: v1
kind: Pod
metadata:
    creationTimestamp: null
    labels:
    run: wazijobs-backend
    name: wazijobs-backend
spec:
    containers:
        - image: raphaelndonga/wazijobs-backend:latest
        name: wazijobs-backend
        ports:
        - containerPort: 8000
    imagePullSecrets:
        - name: my-docker-secret
    dnsPolicy: ClusterFirst
    restartPolicy: Always
status: {}

Then run:

kubectl apply -f wazijobs-backend-pod.yaml

NOTE: The containerPort MUST match the port exposed by the Dockerfile:

FROM --platform=linux/amd64 golang:1.21-alpine
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o main .
EXPOSE 8000
CMD [ "./main" ]

Create a service by exposing the pod:

kubectl expose pod wazijobs-backend --name wazijobs-backend-svc --port 80 --target-port=8000 --dry-run=client -o yaml > wazijobs-backend-svc.yaml

Thus creating the below manifest at wazijobs-backend-svc.yaml:

apiVersion: v1
kind: Service
metadata:
  creationTimestamp: null
  labels:
    run: wazijobs-backend
  name: wazijobs-backend-svc
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8000
  type: ClusterIP
  selector:
    run: wazijobs-backend
status:
  loadBalancer: {}

Then run:

    kubectl apply -f wazijobs-backend-svc.yaml

2. Frontend Implementation

Create a pod using the below manifest at wazijobs-frontend-pod.yaml:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: wazijobs-frontend
  name: wazijobs-frontend
spec:
  containers:
  - image: raphaelndonga/wazijobs-frontend:latest
    name: wazijobs-frontend
    ports:
    - containerPort: 3000
    env:
    - name: NEXT_PUBLIC_API_URL_PROD
      value: "http://wazijobs-backend-svc"
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

Note.The ENV Variable uses the service name set in the backend implementation, made possible through Kubernetes’ CoreDNS.

Create a service by exposing the pod:

kubectl expose pod wazijobs-frontend --name wazijobs-frontend-svc --port 30 --target-port=3000 --dry-run=client -o yaml > wazijobs-frontend-svc.yaml

Thus creating the below service at wazijobs-frontend-svc.yaml:

apiVersion: v1
kind: Service
metadata:
  creationTimestamp: null
  labels:
    run: wazijobs-frontend
  name: wazijobs-frontend-svc
spec:
  ports:
  - port: 30
    protocol: TCP
    targetPort: 3000
  type: ClusterIP
  selector:
    run: wazijobs-frontend
status:
  loadBalancer: {}

Then run:

kubectl apply -f wazijobs-frontend-svc.yaml

3. Exposing the application

To expose the application, run:

kubectl port-forward svc/wazijobs-frontend-svc 30:3000

30 being the service port and 3000 being the container port for the service wazijobs-frontend-svc.

Conclusion

There you have it! A 3 tier application in Kubernetes