> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Setting up the MySQL Database

> Deploying a MySQL database for the Block Buster app using Flux and Kustomize with GitOps, selective deployment, secrets handling, and verification steps

In this lesson we deploy a MySQL database for the Block Buster app using Flux (GitRepository source) and the Kustomize controller. Starting with application version 7.7.0, `highscore.php` persists the high score into MySQL — the app connects to the database at `mysql.database.svc.cluster.local`.

Example snippet from `highscore.php` showing the connection details:

```php theme={null}
<?php
$servername = "mysql.database.svc.cluster.local";
$username = "root";
$password = "mysql-password-0123456789";
$dbname = "bricks";

$conn = new mysqli($servername, $username, $password, $dbname); // Create connection
if ($conn->connect_error) { // Check connection
    die("Connection failed: " . $conn->connect_error);
}
```

We deploy the database infrastructure first so the application can connect as soon as the database becomes available.

Why use Flux + Kustomize for the database?

* GitOps: manifests are tracked in Git and applied declaratively.
* Selective deploy: point Kustomize to only `./database` so only DB manifests are applied from the infra branch.
* Reconciliation and drift detection are handled automatically by Flux.

Relevant docs and references

* Flux GitRepository / Kustomization: [https://fluxcd.io](https://fluxcd.io)
* Kustomize: [https://kubectl.docs.kubernetes.io/references/kustomize/](https://kubectl.docs.kubernetes.io/references/kustomize/)
* Sealed Secrets (example encryption): [https://github.com/bitnami-labs/sealed-secrets](https://github.com/bitnami-labs/sealed-secrets)
* Kubernetes concepts: [https://kubernetes.io/docs/concepts/](https://kubernetes.io/docs/concepts/)

Repository branches and infra layout

* Application code: `7-demo` branch.
* Infrastructure manifests (cluster-level): `infrastructure` branch (Sealed Secrets, cert-manager, database, ingress-nginx, etc.)

Listing of the repository root on the `infrastructure` branch:

```bash theme={null}
root ~/bb-app-source  infrastructure [?]
➜ ll
total 32
drwxr-xr-x  7 batman root 4096 Apr  6 18:08 ./
drwxr-xr-x 10 batman batman 4096 Apr  6 17:47 ../
drwxr-xr-x  8 batman root 4096 Apr  6 18:08 .git/
drwxr-xr-x  2 root root 4096 Apr  6 18:08 bitnami-sealed-secrets/
-rw-r--r--  1 root root 1569 Apr  6 17:56 block-buster-helm-app-7.7.1.tgz
drwxr-xr-x  2 root root 4096 Apr  6 18:08 cert-manager/
drwxr-xr-x  2 root root 4096 Apr  6 18:08 database/
drwxr-xr-x  2 root root 4096 Apr  6 18:08 ingress-nginx/
```

Database manifests overview
Inside the `database/` directory you should find manifests for:

| Resource | Purpose |
| - | - |
| Namespace | Isolates database resources |
| ConfigMap (`mysql-initdb-config`) | Initial SQL or initialization scripts |
| Deployment (MySQL) | Runs the MySQL container |
| Service (ClusterIP) | Internal access at `mysql.database.svc.cluster.local:3306` |
| PersistentVolume / PersistentVolumeClaim | Durable storage for MySQL data |
| Secret | Root password and other credentials (should be sealed/encrypted) |

Example plain-text Secret currently in the repo (for demonstration only):

```yaml theme={null}
apiVersion: v1
kind: Secret
metadata:
  name: secret-mysql
  namespace: database
stringData:
  password: mysql-password-0123456789
```

Secrets in plain text in a Git repository are a security risk. We will later encrypt these (for example, with Sealed Secrets — see [Introduction to Sealed Secrets in Kubernetes](https://learn.kodekloud.com/user/courses/introduction-to-sealed-secrets-in-kubernetes)) before storing them in Git.

Using Flux to deploy only the database manifests
We create a Flux GitRepository source that points to the `infrastructure` branch and a Kustomization that references only the `./database` path. This ensures only the database resources are applied, avoiding cluster-wide changes from other infra manifests.

Example GitRepository manifest (save this as a Flux Source in the `flux-system` namespace):

```yaml theme={null}
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
  name: infra-source-git
  namespace: flux-system
spec:
  interval: 1m0s
  ref:
    branch: infrastructure
  url: https://github.com/sidd-harth-2/bb-app-source
```

Create this source with the `flux` CLI. Example:

```bash theme={null}
flux create source git infra-source-git \
  --url https://github.com/sidd-harth-2/bb-app-source \
  --branch infrastructure \
  --timeout 10s \
  --export > infra-source-git.yml
```

Kustomization that targets only the `database` subdirectory:

```yaml theme={null}
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
  name: infra-database-kustomize-git-mysql
  namespace: flux-system
spec:
  interval: 1m40s
  path: ./database
  prune: true
  sourceRef:
    kind: GitRepository
    name: infra-source-git
```

The `path: ./database` ensures Flux/Kustomize applies manifests only from the `database` directory of the `infrastructure` branch.

Create, reconcile, and verify
After placing `infra-source-git.yml` and `infra-database-kustomize-git-mysql.yml` in your Flux cluster manifests repo (for example, `block-buster/flux-clusters/dev-cluster`) commit and push them. You can then reconcile manually to apply immediately.

Example terminal sequence (switching to the Flux cluster repo and exporting the Git source):

```bash theme={null}
root ~/bb-app-source  infrastructure [?]
➤➤ cd ../block-buster/flux-clusters/dev-cluster/

root ~/block-buster/flux-clusters/dev-cluster  ● main
➤➤ flux create source git infra-source-git \
  --url https://github.com/sidd-harth-2/bb-app-source \
  --branch infrastructure \
  --timeout 10s \
  --export > infra-source-git.yml
```

Once pushed and reconciled, a new `database` namespace should be created by Flux:

```bash theme={null}
root ~/block-buster/flux-clusters/dev-cluster  ● main
➤➤ kubectl get ns
NAME                  STATUS   AGE
1-demo                Active   16h
2-demo                Active   16h
3-demo                Active   16h
4-demo                Active   131m
5-demo                Active   104m
6-demo                Active   49m
database              Active   19s
default               Active   17h
flux-system           Active   17h
kube-node-lease       Active   17h
kube-public           Active   17h
kube-system           Active   17h
minio-dev             Active   139m
```

Confirm Flux source and Kustomization status:

```bash theme={null}
root ~/block-buster/flux-clusters/dev-cluster  ● main
➤➤ flux get sources git infra-source-git

NAME                 REVISION                                   SUSPENDED READY MESSAGE
infra-source-git     infrastructure@sha1:c94a687d              False     True   stored artifact for revision 'infrastructure@sha1:c94a687d'

root ~/block-buster/flux-clusters/dev-cluster  ● main
➤➤ flux get kustomizations infra-database-kustomize-git-mysql

NAME                                   REVISION                                   SUSPENDED READY MESSAGE
infra-database-kustomize-git-mysql     infrastructure@sha1:c94a687d              False     True   Applied revision: infrastructure@sha1:c94a687d
```

Check the database namespace resources
When first applied the MySQL pod may be `Pending` while PVs are provisioned. Example:

```bash theme={null}
root ~/block-buster/flux-clusters/dev-cluster  main
➤➤ kubectl -n database get all
NAME                                    READY   STATUS    RESTARTS   AGE
pod/mysql-7d89476576-m7z12              0/1     Pending   0          58s

NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
service/mysql            ClusterIP   10.96.154.12    <none>        3306/TCP    58s

NAME                                READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/mysql                0/1     1            0           58s

NAME                                        DESIRED   CURRENT   READY   AGE
replicaset.apps/mysql-7d89476576             1         1         0       58s
```

After volumes are bound and the container starts up, resources become Running / Available:

```bash theme={null}
root ~/block-buster/flux-clusters/dev-cluster  main
➤➤ kubectl -n database get all,cm,secrets
NAME                            READY   STATUS    RESTARTS   AGE
pod/mysql-7d89476576-m7z12      1/1     Running   0          72s

NAME               TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)      AGE
service/mysql      ClusterIP   10.96.154.12   <none>        3306/TCP     72s

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/mysql     1/1     1            1           72s

NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/mysql-7d89476576      1         1         1       72s

NAME                              DATA   AGE
configmap/kube-root-ca.crt         1      73s
configmap/mysql-initdb-config      1      73s

NAME                    TYPE     DATA   AGE
secret/secret-mysql     Opaque   1      72s
```

At this point you should verify:

* Deployment/Pod status: `kubectl -n database get pods,deploy`
* Service: `kubectl -n database get svc mysql` (ClusterIP, port 3306)
* PVC / PV is `Bound` if using dynamic storage: `kubectl -n database get pvc`
* ConfigMap initialization scripts applied and Secret exists (but remember it's currently plain text)

Helpful checks and troubleshooting

* View pod logs: `kubectl -n database logs deploy/mysql`
* Check PVC status: `kubectl -n database get pvc`
* Describe pod for events and volume errors: `kubectl -n database describe pod <pod-name>`

Tip: If the MySQL pod stays Pending, check your storage class and PV provisioning. Use `kubectl describe pod` and `kubectl get pvc -o wide` to inspect volume binding and related events.

Next steps

* Seal or encrypt Secrets (e.g., Sealed Secrets) and store the sealed manifest in Git rather than plain text.
* Add application Flux sources and Kustomizations that deploy the Block Buster app images (from your OCI registry) against this cluster namespace so the app can connect to `mysql.database.svc.cluster.local`.
* Monitor Flux reconciliation and application health:
  * `flux get kustomizations`
  * `kubectl -n <app-namespace> get pods,svc`

Quick checklist before deploying the app

1. Database namespace exists and is Active.
2. MySQL pod is Running and Deployment is Available.
3. Service `mysql` is a ClusterIP on port 3306.
4. PVC is Bound and data directory is writable.
5. Secrets are sealed/encrypted before committing to Git.

References

* Flux: [https://fluxcd.io](https://fluxcd.io)
* Kustomize: [https://kubectl.docs.kubernetes.io/references/kustomize/](https://kubectl.docs.kubernetes.io/references/kustomize/)
* Sealed Secrets: [https://github.com/bitnami-labs/sealed-secrets](https://github.com/bitnami-labs/sealed-secrets)
* Kubernetes docs: [https://kubernetes.io/docs/](https://kubernetes.io/docs/)
* MySQL Docker image: [https://hub.docker.com/\_/mysql](https://hub.docker.com/_/mysql)

> **[Watch Video](https://learn.kodekloud.com/user/courses/gitops-with-fluxcd/module/205ec7c7-4cb6-4ecb-9bb5-fa50419f1e68/lesson/938cb579-768a-48b1-8184-3fe116c36db3)**


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.