In this part, we move from a basic MicroK8s cluster to a real production stack with:
- PostgreSQL database
- Redis cache
- n8n workflow automation
- HTTPS using Letβs Encrypt
- Kubernetes Dashboard on its own domain
All deployed using proper namespaces and ingress rules.
β Prerequisites
From previous parts, you must already have:
- β MicroK8s installed and running
- β
kubectlalias working - β DNS records created:
| Domain | Points To |
|---|---|
n8n.domain.com |
Your server public IP |
kube.domain.com |
Your server public IP |
π Step 1 β Enable Required MicroK8s Add-ons
microk8s enable dns storage ingress cert-manager dashboard metrics-server
Wait for everything:
kubectl get pods -A
All pods should be Running.
π§± Step 2 β Project Structure
mkdir -p k8s-project/{03-postgres,04-redis,05-n8n,07-ingress}
cd k8s-project
π§© Step 3 β Create Production Namespace
kubectl create namespace prod
π Step 4 β Create Secrets
PostgreSQL Password
kubectl create secret generic postgres-secret \
--from-literal=password="$(openssl rand -base64 24)" \
-n prod
n8n Encryption Key
kubectl create secret generic n8n-secret \
--from-literal=encryption-key="$(openssl rand -base64 32)" \
-n prod
π Step 5 β Deploy PostgreSQL
π 03-postgres/postgres.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
namespace: prod
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
namespace: prod
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16
env:
- name: POSTGRES_DB
value: n8n
- name: POSTGRES_USER
value: n8n
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
ports:
- containerPort: 5432
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim:
claimName: postgres-pvc
---
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: prod
spec:
selector:
app: postgres
ports:
- port: 5432
Apply:
kubectl apply -f 03-postgres/postgres.yaml
π΄ Step 6 β Deploy Redis
π 04-redis/redis.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: prod
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7-alpine
ports:
- containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: prod
spec:
selector:
app: redis
ports:
- port: 6379
Apply:
kubectl apply -f 04-redis/redis.yaml
π Step 7 β Deploy n8n
π 05-n8n/n8n.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: n8n-pvc
namespace: prod
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: n8n
namespace: prod
spec:
replicas: 1
selector:
matchLabels:
app: n8n
template:
metadata:
labels:
app: n8n
spec:
containers:
- name: n8n
image: n8nio/n8n:latest
env:
- name: DB_TYPE
value: postgresdb
- name: DB_POSTGRESDB_HOST
value: postgres
- name: DB_POSTGRESDB_DATABASE
value: n8n
- name: DB_POSTGRESDB_USER
value: n8n
- name: DB_POSTGRESDB_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
- name: N8N_ENCRYPTION_KEY
valueFrom:
secretKeyRef:
name: n8n-secret
key: encryption-key
- name: N8N_HOST
value: n8n.domain.com
- name: N8N_PROTOCOL
value: https
- name: WEBHOOK_URL
value: https://n8n.domain.com/
ports:
- containerPort: 5678
volumeMounts:
- name: data
mountPath: /home/node/.n8n
volumes:
- name: data
persistentVolumeClaim:
claimName: n8n-pvc
---
apiVersion: v1
kind: Service
metadata:
name: n8n
namespace: prod
spec:
selector:
app: n8n
ports:
- port: 5678
targetPort: 5678
Apply:
kubectl apply -f 05-n8n/n8n.yaml
π Step 8 β Create Letβs Encrypt Issuer
π 07-ingress/issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
email: your-email@domain.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: public
Apply:
kubectl apply -f 07-ingress/issuer.yaml
π Step 9 β Ingress for n8n
π 07-ingress/n8n-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: n8n-ingress
namespace: prod
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: public
rules:
- host: n8n.domain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: n8n
port:
number: 5678
tls:
- hosts:
- n8n.domain.com
secretName: n8n-tls
Apply:
kubectl apply -f 07-ingress/n8n-ingress.yaml
π Step 10 β Ingress for Kubernetes Dashboard
π 07-ingress/dashboard-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dashboard-ingress
namespace: kube-system
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
ingressClassName: public
rules:
- host: kube.domain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kubernetes-dashboard
port:
number: 443
tls:
- hosts:
- kube.domain.com
secretName: kube-dashboard-tls
Apply:
kubectl apply -f 07-ingress/dashboard-ingress.yaml
β Step 11 β Verify Everything
Pods
kubectl get pods -A
Certificates
kubectl get certificate -A
Services
kubectl get svc -n prod
kubectl get svc -n kube-system | grep dashboard
π Result
| URL | What You Get |
|---|---|
| https://n8n.domain.com | n8n UI |
| https://kube.domain.com | Kubernetes Dashboard |
Top comments (0)