How to Set Up MariaDB in Kubernetes using Helm and Kustomize

Step-by-step guide on how to deploy MariaDB in Kubernetes using Kustomize and (optionally) Helmfile, with secrets managed via External Secrets.

MariaDB, a popular open-source relational database, runs well in Kubernetes — especially when paired with tools like Helm and Kustomize for clean, repeatable infrastructure management.

In this guide, you’ll learn how to deploy MariaDB into a Kubernetes cluster using Kustomize (plain YAML + overlays) and optionally Helmfile (Helm releases as code).

Using Helm and Kustomize is beneficial in a couple of ways:

  • Helm simplifies complex deployments using charts — reusable application templates.
  • Kustomize allows you to declaratively patch and manage multiple environments (dev, staging, prod) without duplicating YAML.

In this guide, you’ll learn how to:

  • Deploy MariaDB in Kubernetes
  • Manage configuration using Helm and Kustomize
  • Secure secrets using External Secrets
  • Use Helmfile for simplified release management

Prerequisites

  • Kubernetes cluster (local via kind/minikube, or cloud via GKE/EKS/AKS)
  • kubectl, helm, and kustomize installed
  • Optional: kubens and kubectx for context/namespace management

Create a Namespace

Isolate the MariaDB resources in a dedicated namespace. Save this under namespace.yaml:

1
2
3
4
5
6
# namespace.yaml
---
apiVersion: v1
kind: Namespace
metadata:
  name: mariadb

Namespacing keeps resources logically grouped and avoids name collisions.

Define persistent storage

MariaDB stores data on disk, so you want persistent storage. For a StatefulSet, the most common pattern is to use volumeClaimTemplates so each replica gets its own PVC automatically.

Manage Secrets with External Secrets

We need a Kubernetes Secret containing database credentials. A common pattern is to store them in a secret manager and sync them into Kubernetes using External Secrets.

Use External Secrets Operator to securely inject credentials from a secret store (e.g. Vault, AWS Secrets Manager).

Save this under external-secret.yaml.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# external-secret.yaml
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: mariadb-credentials
  namespace: mariadb
spec:
  dataFrom:
    - extract:
        key: infra/mariadb-credentials
  secretStoreRef:
    kind: ClusterSecretStore
    name: vault-backend
  target:
    name: mariadb-credentials
    creationPolicy: Owner

This keeps credentials out of source control and lets you rotate secrets outside the cluster.

Expected keys in the generated Secret (example):

  • root-password
  • (recommended) username
  • (recommended) password
  • (optional) database

Deploy MariaDB with Kustomize (StatefulSet)

Databases should usually run as a StatefulSet (stable identity + safer semantics with storage). Save this under statefulset.yaml:

 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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mariadb
  namespace: mariadb
spec:
  serviceName: mariadb
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: mariadb
  template:
    metadata:
      labels:
        app.kubernetes.io/name: mariadb
    spec:
      securityContext:
        fsGroup: 999
      terminationGracePeriodSeconds: 30
      containers:
        - name: mariadb
          image: mariadb:11.7
          ports:
            - containerPort: 3306
              name: mariadb
          env:
            - name: MARIADB_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mariadb-credentials
                  key: root-password
            # Recommended: create an application DB/user (avoid using root for apps)
            - name: MARIADB_DATABASE
              valueFrom:
                secretKeyRef:
                  name: mariadb-credentials
                  key: database
                  optional: true
            - name: MARIADB_USER
              valueFrom:
                secretKeyRef:
                  name: mariadb-credentials
                  key: username
                  optional: true
            - name: MARIADB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mariadb-credentials
                  key: password
                  optional: true
          resources:
            requests:
              cpu: 50m
              memory: 256Mi
            limits:
              cpu: "1"
              memory: 1Gi
          livenessProbe:
            exec:
              command:
                [
                  "bash",
                  "-ec",
                  'mariadb-admin ping -uroot -p"${MARIADB_ROOT_PASSWORD}"',
                ]
            initialDelaySeconds: 30
            periodSeconds: 10
            timeoutSeconds: 5
          readinessProbe:
            exec:
              command:
                [
                  "bash",
                  "-ec",
                  'mariadb-admin ping -uroot -p"${MARIADB_ROOT_PASSWORD}"',
                ]
            initialDelaySeconds: 10
            periodSeconds: 10
            timeoutSeconds: 5
          volumeMounts:
            - name: data
              mountPath: /var/lib/mysql
  volumeClaimTemplates:
    - metadata:
        name: data
        labels:
          app.kubernetes.io/name: mariadb
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 10Gi
        # Optional: set a storageClassName depending on your cluster (EBS, Ceph, Longhorn, etc.)
        # storageClassName: standard

This will create a PVC automatically (named like data-mariadb-0) and mount it at /var/lib/mysql.

Expose MariaDB via Service

We need a way to expose the database inside the cluster. Save this under service.yaml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# service.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: mariadb
  namespace: mariadb
spec:
  clusterIP: None
  ports:
    - port: 3306
      targetPort: mariadb
  selector:
    app.kubernetes.io/name: mariadb

This creates a headless service for the StatefulSet, and other pods can connect using mariadb.mariadb.svc.cluster.local:3306.

Configure with Kustomize

We use Kustomize to manage all the YAML files. Bring it all together using a kustomization.yaml file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# kustomization.yaml
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: mariadb

resources:
  - ./namespace.yaml
  - ./external-secret.yaml
  - ./service.yaml
  - ./statefulset.yaml

Apply the Configuration

Apply everything using Kustomize:

1
kustomize build . | kubectl apply -f -

Verify:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$ kubectl get all -n mariadb

NAME                              READY   STATUS    RESTARTS   AGE
pod/mariadb-app-7c9f6d4d8-gv67k   1/1     Running   0          21h

NAME                  TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
service/mariadb-app   ClusterIP   10.43.3.189   <none>        3306/TCP   22h

NAME                          READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/mariadb-app   1/1     1            1           22h

NAME                                     DESIRED   CURRENT   READY   AGE
replicaset.apps/mariadb-app-7c9f6d4d8    1         1         1       22h

You should see a running pod, service, and associated deployment.

Deploy MariaDB using Helm + Helmfile (optional)

If you prefer Helm releases managed as code, you can use Helmfile. I have a public Helm charts repo for common things here that we can use.

Create a helmfile.yaml file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# helmfile.yaml
---
helmDefaults:
  createNamespace: true
  wait: true

repositories:
  - name: mycharts
    url: https://etowett.github.io/helm-charts

releases:
  - name: mariadb
    namespace: mariadb
    chart: mycharts/app
    version: "1.1.1"
    values:
      - ./values.yaml

Then create a values file - values.yaml:

 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
40
41
42
43
44
45
46
47
48
replicaCount: 1

image:
  repository: mariadb
  pullPolicy: IfNotPresent
  tag: 11.7

serviceAccount:
  create: true

service:
  type: ClusterIP
  name: mariadb
  port: 3306

resources:
  limits:
    cpu: 1000m
    memory: 1024Mi
  requests:
    cpu: 50m
    memory: 64Mi

pvc:
  create: true
  claimName: mariadb-data-vol
  size: 10Gi

volumeMounts:
  - name: mariadb-data-vol
    mountPath: /var/lib/mysql

volumes:
  - name: mariadb-data-vol
    persistentVolumeClaim:
      claimName: mariadb-data-vol

externalSecrets:
  - name: mariadb-credentials
    refreshInterval: 5m
    secretStoreRefName: vault-backend
    targetName: mariadb-credentials
    dataKey: infra/mariadb-credentials

secretEnv:
  MARIADB_ROOT_PASSWORD:
    name: mariadb-credentials
    key: root-password

Then apply

1
helmfile sync

Validate with:

1
2
3
4
$ helm ls -n mariadb

NAME    NAMESPACE REVISION UPDATED                              STATUS   CHART     APP VERSION
mariadb mariadb   3        2025-05-05 11:27:54.825897 +0300 EAT deployed app-1.1.1 1.0.0

Final Thoughts

Deploying MariaDB in Kubernetes using Helm and Kustomize provides:

  • Declarative configuration
  • Secure secret management
  • Reusable environments

Whether you’re deploying for development or production, this setup ensures consistency, flexibility, and security.

comments powered by Disqus
Citizix Ltd
Built with Hugo
Theme Stack designed by Jimmy