Post

Mealie Kubernetes Deployment with FreeIPA TLS

Mealie Kubernetes Deployment with FreeIPA TLS

Mealie Kubernetes Deployment with FreeIPA TLS

This unified guide walks through deploying Mealie into Kubernetes using:

  • FreeIPA-managed TLS certificates (with certmonger)
  • NGINX Ingress with MetalLB
  • Persistent storage via PVC

1. 📛 DNS Setup

Ensure DNS A record exists:

1
2
dig +short mealie.linux.matthewdavidson.us
# Should return your MetalLB IP (e.g. 192.168.0.23)

2. 🔐 TLS Certificate Management with certmonger

Use FreeIPA and certmonger to generate a cert with full chain and allow renewals.

1
2
3
4
5
6
7
8
9
10
# Stop old tracking (if any)
sudo ipa-getcert stop-tracking -f /etc/pki/tls/certs/mealie/mealie.crt

# Re-request with chain and tracking
sudo ipa-getcert request \
  -f /etc/pki/tls/certs/mealie/mealie.crt \
  -k /etc/pki/tls/certs/mealie/mealie.key \
  -D mealie.linux.matthewdavidson.us \
  -K HTTP/mealie.linux.matthewdavidson.us \
  -C "cat /etc/pki/tls/certs/mealie/mealie.crt /etc/ipa/ca.crt > /etc/pki/tls/certs/mealie/mealie-fullchain.crt"

Verify:

1
openssl x509 -in /etc/pki/tls/certs/mealie/mealie-fullchain.crt -noout -issuer -subject

3. 📦 Kubernetes Resources

A. Namespace

1
2
3
4
apiVersion: v1
kind: Namespace
metadata:
  name: mealie

B. PVC

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mealie-data
  namespace: mealie
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
  storageClassName: nfs-client

C. Deployment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mealie
  namespace: mealie
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mealie
  template:
    metadata:
      labels:
        app: mealie
    spec:
      containers:
        - name: mealie
          image: ghcr.io/mealie-recipes/mealie:latest
          ports:
            - containerPort: 80
          volumeMounts:
            - mountPath: /app/data
              name: mealie-data
      volumes:
        - name: mealie-data
          persistentVolumeClaim:
            claimName: mealie-data

D. Service

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Service
metadata:
  name: mealie
  namespace: mealie
spec:
  type: ClusterIP
  selector:
    app: mealie
  ports:
    - port: 80
      targetPort: 80

E. TLS Secret

1
2
3
4
kubectl create secret tls mealie-tls \
  --cert=/etc/pki/tls/certs/mealie/mealie-fullchain.crt \
  --key=/etc/pki/tls/certs/mealie/mealie.key \
  -n mealie

F. Ingress (Fix added to let access in from an external proxy.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# mealie-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: mealie
  namespace: mealie
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    # Add this annotation
    kubernetes.io/ingress.class: "nginx"
spec:
  # Add this field for newer controllers
  ingressClassName: nginx
  tls:
    - hosts:
        - mealie.linux.matthewdavidson.us
      secretName: mealie-tls
  rules:
    - host: mealie.linux.matthewdavidson.us
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: mealie
                port:
                  number: 80
    # Add rule for external domain
    - host: mealie.matthewdavidson.us
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: mealie
                port:
                  number: 80

4. 📡 MetalLB

You must have MetalLB configured and mealie.linux.matthewdavidson.us resolving to a LoadBalancer IP MetalLB manages (e.g., 192.168.0.23).

No special Service: LoadBalancer is needed for Mealie because ingress handles that.


🧠 Notes

  • The -C option in ipa-getcert is a post-command executed after successful cert issuance or renewal.
  • Make sure certmonger runs and FreeIPA is reachable from the host managing the cert.
  • Ingress class names must match the controller’s configured --ingress-class.
This post is licensed under CC BY 4.0 by the author.