Skip to content

MariaDB PVCs not cleaned up after application deletion #2350

Closed as not planned

Description

@myasnikovdaniil

Description

When a MariaDB application instance is deleted, its PVCs remain in the namespace. The post-delete Helm hook that should clean them up fails silently due to a label mismatch between what the hook searches for and what labels the PVCs actually have.

Root cause

The cleanup hook in packages/apps/mariadb/templates/hooks/cleanup-pvc.yaml deletes PVCs matching:

kubectl delete pvc -n {{ .Release.Namespace }} -l app.kubernetes.io/instance={{ .Release.Name }}

However, the MariaDB CR definition in packages/apps/mariadb/templates/mariadb.yaml does not specify labels in the storage.volumeClaimTemplate:

storage:
  size: {{ .Values.size }}
  resizeInUseVolumes: true
  waitForVolumeResize: true
  storageClassName: {{ . }}

The mariadb-operator creates PVCs via StatefulSets without propagating the app.kubernetes.io/instance label, so the cleanup hook finds no matching PVCs and exits without deleting anything.

Expected behavior

PVCs created for MariaDB instances should be automatically cleaned up when the application is deleted.

Suggested fix

Add a volumeClaimTemplate with matching labels to the MariaDB CR storage spec:

storage:
  size: {{ .Values.size }}
  resizeInUseVolumes: true
  waitForVolumeResize: true
  volumeClaimTemplate:
    metadata:
      labels:
        app.kubernetes.io/instance: {{ .Release.Name }}

This ensures PVCs are created with the label that the post-delete hook uses to find and delete them.

Note

This issue is specific to MariaDB (and potentially other operator-managed apps that create StatefulSets). PostgreSQL (CNPG) handles PVC cleanup natively via its cluster deletion policy and does not need a post-delete hook.

Affected files

  • packages/apps/mariadb/templates/mariadb.yaml — missing volumeClaimTemplate labels
  • packages/apps/mariadb/templates/hooks/cleanup-pvc.yaml — cleanup hook (correct, but can't find PVCs)

Activity

  1. added
    kind/bugCategorizes issue or PR as related to a bug
    on Apr 6, 2026
  2. added
    area/databaseIssues or PRs related to managed databases (postgres, mariadb, redis, etcd, kafka, clickhouse)
    priority/important-soonMust be staffed and worked on either currently, or very soon, ideally in time for the next release
    triage/acceptedIndicates an issue is ready to be actively worked on
    on Apr 27, 2026
  3. scooby87 commented on Sep 9, 2026

    @scooby87
    Contributor

    I tried to reproduce this on a live cluster and it does not reproduce on the operator version we currently vendor. The cleanup hook finds and deletes the PVCs, because the label the hook selects on is already present — the operator stamps it itself.

    Root cause in the report is inaccurate

    The report assumes mariadb-operator creates the PVCs "without propagating the app.kubernetes.io/instance label". On mariadb-operator:25.10.2 (the version vendored in packages/system/mariadb-operator) that is not the case: the operator labels every data PVC with app.kubernetes.io/instance=<MariaDB name>, and in our chart the MariaDB CR is named {{ .Release.Name }} (packages/apps/mariadb/templates/mariadb.yaml), which is exactly what the hook selects on in cleanup-pvc.yaml. So the selector matches and the suggested volumeClaimTemplate.metadata.labels change is redundant — it re-adds a label the operator already sets.

    Live reproduction attempt (dev cluster, operator 25.10.2)

    Two instances created without any volumeClaimTemplate labels, to reflect exactly what the report describes. db-2 is a sibling whose name shares the db prefix, to check selector isolation.

    The PVCs come up already carrying app.kubernetes.io/instance, set by the operator:

    $ kubectl -n <ns> get pvc --show-labels
    NAME             STATUS   ...   LABELS
    storage-db-0     Bound    ...   app.kubernetes.io/instance=db,app.kubernetes.io/name=mariadb,pvc.k8s.mariadb.com/role=storage
    storage-db-2-0   Bound    ...   app.kubernetes.io/instance=db-2,app.kubernetes.io/name=mariadb,pvc.k8s.mariadb.com/role=storage

    Deleting the instance leaves the PVC orphaned, as expected — the operator does not reclaim StatefulSet PVCs, which is why the hook exists:

    $ kubectl -n <ns> delete mariadbs.k8s.mariadb.com db
    mariadb.k8s.mariadb.com "db" deleted
    $ kubectl -n <ns> get pvc
    NAME             STATUS   ...
    storage-db-0     Bound    ...
    storage-db-2-0   Bound    ...

    Running the real rendered cleanup-pvc.yaml hook for release=db deletes exactly the orphaned PVC and leaves the sibling alone:

    # rendered hook runs: kubectl delete pvc -n <ns> -l app.kubernetes.io/instance=db --ignore-not-found
    $ kubectl -n <ns> logs job/db-cleanup
    Deleting orphaned PVCs for db...
    persistentvolumeclaim "storage-db-0" deleted
    PVC cleanup complete.
    
    $ kubectl -n <ns> get pvc
    NAME             STATUS   ...
    storage-db-2-0   Bound    ...

    storage-db-0 is gone, storage-db-2-0 (the sibling release) is untouched.

    Suggestion

    Since the reported behaviour does not reproduce and the expected behaviour (PVCs reclaimed on delete) already holds on the vendored operator, I'd close this as not reproducible. If we want to stop depending on the operator continuing to set app.kubernetes.io/instance — i.e. harden the hook's selector against a future operator label-schema change — that's worth doing, but as a defensive hardening task rather than the bug described here.

    One unrelated note surfaced in the hook logs: cannot watch resource "persistentvolumeclaims". That is the missing-watch-verb issue already being fixed in #4135; the delete still lands, but without watch the hook does not wait for the PVC to finish terminating. Not part of this issue.

  4. added
    triage/not-reproducibleIndicates an issue can not be reproduced as described
    and removed
    priority/important-soonMust be staffed and worked on either currently, or very soon, ideally in time for the next release
    triage/acceptedIndicates an issue is ready to be actively worked on
    on Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area/databaseIssues or PRs related to managed databases (postgres, mariadb, redis, etcd, kafka, clickhouse)kind/bugCategorizes issue or PR as related to a bugtriage/not-reproducibleIndicates an issue can not be reproduced as described

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions