Services Fail After Accidental Deletion of Regional MySQL Databases

Problem

In certain cases, the MySQL database for a Self-hosted Private Cloud Director region may be accidentally deleted or corrupted. This can cause critical platform components to fail, resulting in an unusable environment and loss of region-specific data.

Environment

  • Self-Hosted Private Cloud Director Virtualization - v2025.10 and Higher

  • Component - MySQL

Cause

Accidental deletion or corruption of the MySQL database for a specific region(s).

Diagnostics

When a regional MySQL database is deleted or corrupted, symptoms may include:

  • Components failing to start or repeatedly crashing.

  • Errors related to missing tables or database connections in component/pod logs.

  • The region appearing as unreachable or inactive in the control plane UI.

To confirm database loss login to MySQL from Percona pod:

$ kubectl exec -it percona-db-pxc-db-pxc-0 -n <REGION_NAME> -c pxc -- bash bash-4.4$ mysql -u root -p<PASSWORD> mysql>show databases; [!DATABASE MISSING!]

If the region databases are missing from the list, restoration from the latest backup is required.

Resolution

As part of the Self-hosted Private Cloud Director backup process, MySQL data — including users and permissions — is backed up automatically for each region [Backup guide]. To restore the database without performing a full region restore, follow the steps below:

IMPORTANT NOTE

Before proceeding with the MySQL restore process, MySQL root password needs to be retrieved from the Kubernetes secret in its corresponding region namespace using

kubectl get secret -n <REGION_NAME> mysql -o jsonpath='{.data.pass}' | base64 -d

  1. Extract the Backup

Locate the backup archive obtained from the automated or manual backup process and extract it:

$ tar -xzvf backups/backup_20251113_181414.tar.gz backups/state_backup.yaml backups/kplane_values_backup.yaml backups/consul.snap backups/mysql_dump_Infra.sql backups/mysql_dump_LON.sql backups/ovn-north-backup-LON-ovn-ovsdb-nb-0.tar.gz backups/ovn-north-backup-LON-ovn-ovsdb-nb-1.tar.gz backups/ovn-north-backup-LON-ovn-ovsdb-nb-2.tar.gz backups/ovn-south-backup-LON-ovn-ovsdb-sb-0.tar.gz backups/ovn-south-backup-LON-ovn-ovsdb-sb-1.tar.gz backups/ovn-south-backup-LON-ovn-ovsdb-sb-2.tar.gz backups/rabbitmq-backup-Infra.tar.gz backups/rabbitmq-backup-LON.tar.gz

Within the extracted backup, locate the MySQL dump file for the affected region mysql_dump_<REGION_NAME>.sql

For example, for the Infra region backups/mysql_dump_Infra.sql

  1. Scale Down Percona Replicas

To safely restore the database, first scale down the Percona PXC replicas for the region:

$ kubectl scale sts percona-db-pxc-db-pxc -n <REGION_NAME> --replicas=0 statefulset.apps/percona-db-pxc-db-pxc scaled
  1. Delete Existing Percona PVCs

Delete the existing PersistentVolumeClaims (PVCs) associated with the Percona MySQL pods to remove the corrupted data:

$ kubectl get pvc -n <REGION_NAME> | grep percona-db-pxc-db-pxc datadir-percona-db-pxc-db-pxc-0 Bound [PVC_UUID1] 96Gi RWO hostpath-csi <unset> 3d6h datadir-percona-db-pxc-db-pxc-1 Bound [PVC_UUID2] 96Gi RWO hostpath-csi <unset> 3d6h datadir-percona-db-pxc-db-pxc-2 Bound [PVC_UUID3] 96Gi RWO hostpath-csi <unset> 3d6h $ kubectl delete pvc datadir-percona-db-pxc-db-pxc-0 datadir-percona-db-pxc-db-pxc-1 datadir-percona-db-pxc-db-pxc-2 -n <REGION_NAME> persistentvolumeclaim "datadir-percona-db-pxc-db-pxc-0" deleted persistentvolumeclaim "datadir-percona-db-pxc-db-pxc-1" deleted persistentvolumeclaim "datadir-percona-db-pxc-db-pxc-2" deleted
  1. Scale Up Percona Replicas

Scale the replicas back up to recreate the database with new, empty volumes:

$ kubectl scale sts percona-db-pxc-db-pxc -n <REGION_NAME> --replicas=3 statefulset.apps/percona-db-pxc-db-pxc scaled $ kubectl get pods -n <REGION_NAME> | grep percona-db-pxc-db-pxc percona-db-pxc-db-pxc-0 3/3 Running 0 3m26s percona-db-pxc-db-pxc-1 3/3 Running 0 2m25s percona-db-pxc-db-pxc-2 3/3 Running 0 83s
  1. Restore the Database from Backup

Once the Percona pods are up and running, copy the regional MySQL dump into percona-db-pxc-db-pxc-0 pod:

$ kubectl cp backups/mysql_dump_Infra.sql percona-db-pxc-db-pxc-0:/tmp -n <REGION_NAME> -c pxc $ kubectl exec -it percona-db-pxc-db-pxc-0 -n <REGION_NAME> -c pxc -- bash bash-4.4$ ls /tmp/mysql_dump_Infra.sql /tmp/mysql_dump_Infra.sql ## No platform9 service bash-4.4$ mysql -u root -p<PASSWORD> mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | +--------------------+ 4 rows in set (0.00 sec)

Then, restore the dump using the MySQL client:

bash-4.4$ mysql -u root -p<PASSWORD> < <PATH_TO_MYSQL_BACKUP.sql> && mysql -u root -p<PASSWORD> -e 'flush privileges' mysql: [Warning] Using a password on the command line interface can be insecure. mysql: [Warning] Using a password on the command line interface can be insecure.

Validation

  • Log in to the database from percona pod and confirm that the expected databases and tables are present:

mysql> show databases; +--------------------+ | Database | +--------------------+ | alertmanager | | hagrid | | information_schema | | keystone | | mysql | | performance_schema | | preference_store | | resmgr | | sys | +--------------------+ 9 rows in set (0.00 sec)
  • Verify that services are running and the region is healthy:

$ /opt/pf9/airctl/airctl status --region <REGION_NAME> --config /opt/pf9/airctl/conf/airctl-config.yaml ------------- deployment details --------------- fqdn: [FQDN] region: [REGION_NAME] deployment status: ready region health: ✅ Ready version: PCD 2025.10-3111 -------- region service status ---------- desired services: 30 ready services: 30
  • Access the management plane UI and confirm normal operations , API responses, and workflows should function as expected.