DEV Community

kamlesh merugu
kamlesh merugu

Posted on • Edited on

πŸš€ Part 3 β€” Deploy Production Stack on MicroK8s

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
  • βœ… kubectl alias 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
Enter fullscreen mode Exit fullscreen mode

Wait for everything:

kubectl get pods -A
Enter fullscreen mode Exit fullscreen mode

All pods should be Running.


🧱 Step 2 β€” Project Structure

mkdir -p k8s-project/{03-postgres,04-redis,05-n8n,07-ingress}
cd k8s-project
Enter fullscreen mode Exit fullscreen mode

🧩 Step 3 β€” Create Production Namespace

kubectl create namespace prod
Enter fullscreen mode Exit fullscreen mode

πŸ” Step 4 β€” Create Secrets

PostgreSQL Password

kubectl create secret generic postgres-secret \
  --from-literal=password="$(openssl rand -base64 24)" \
  -n prod
Enter fullscreen mode Exit fullscreen mode

n8n Encryption Key

kubectl create secret generic n8n-secret \
  --from-literal=encryption-key="$(openssl rand -base64 32)" \
  -n prod
Enter fullscreen mode Exit fullscreen mode

🐘 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
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f 03-postgres/postgres.yaml
Enter fullscreen mode Exit fullscreen mode

πŸ”΄ 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
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f 04-redis/redis.yaml
Enter fullscreen mode Exit fullscreen mode

πŸ” 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
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f 05-n8n/n8n.yaml
Enter fullscreen mode Exit fullscreen mode

πŸ” 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
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f 07-ingress/issuer.yaml
Enter fullscreen mode Exit fullscreen mode

🌍 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
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f 07-ingress/n8n-ingress.yaml
Enter fullscreen mode Exit fullscreen mode

πŸ“Š 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
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f 07-ingress/dashboard-ingress.yaml
Enter fullscreen mode Exit fullscreen mode

βœ… Step 11 β€” Verify Everything

Pods

kubectl get pods -A
Enter fullscreen mode Exit fullscreen mode

Certificates

kubectl get certificate -A
Enter fullscreen mode Exit fullscreen mode

Services

kubectl get svc -n prod
kubectl get svc -n kube-system | grep dashboard
Enter fullscreen mode Exit fullscreen mode

πŸŽ‰ Result

URL What You Get
https://n8n.domain.com n8n UI
https://kube.domain.com Kubernetes Dashboard

πŸ”œ Coming Next

πŸ‘‰ Part 4 β€” Centralized Secrets with Doppler + External Secrets Operator

πŸ‘‰ Part 5 β€” Observability (Prometheus, Grafana, Loki)

πŸ‘‰ Part 6 β€” Automated Backups & Disaster Recovery (Velero + DB dumps)

Top comments (0)