Scheduled MySQL Database Upgrade on May 2nd and Update guide for self-hosted instances

On May 2nd, we are upgrading our database to MySQL 8.4.

This upgrade is necessary as MySQL 8.0 is approaching its End-of-Life in April 2026, after which it will no longer receive security updates, bug fixes, or official support. Moving to a supported version ensures continued security compliance, stability, and access to the latest improvements.

These instructions cover the embedded UI Bakery MySQL database upgrade from mysql:8.0 to mysql:8.4 for:

  • Self-hosted customer-managed instances.

  • Docker Compose and Kubernetes deployments.

Important constraints:

  • UI Bakery must be version 3.177.2 or newer before the MySQL upgrade. If the instance runs an older UI Bakery version, upgrade UI Bakery first, validate the application, and only then upgrade MySQL.

  • Plan downtime. The embedded MySQL container/pod must be restarted, and the UI Bakery application should not write to the database while a storage-level backup is taken.

  • These steps are for the embedded UI Bakery MySQL database. Instances that use an external MySQL database must coordinate the upgrade with the database owner and should only update UI Bakery environment variables if host, port, database, username, or password changes.

  • Do not roll back by starting mysql:8.0 on a data directory already upgraded by MySQL 8.4. Restore the pre-upgrade VM/disk/PVC/volume backup instead.

  • MySQL 8.4 disables mysql_native_password by default. Docker Compose instances must replace --default-authentication-plugin=mysql_native_password with --mysql-native-password=ON to keep the current embedded DB user compatible.

References


Self-hosted instances

Docker Compose

Use this section for customer-managed installations based on uibakery/self-hosted Docker Compose.

Current expected embedded MySQL service:

  • Service: db

  • Container: db

  • Image before upgrade: mysql:8.0

  • Docker volume: my-db or <compose_project>_my-db

  • Data directory inside container: /var/lib/mysql

1. Preparing

  1. SSH to the server and go to the UI Bakery installation directory:
cd ~/ui-bakery-on-premise

If the instance was installed elsewhere, use the actual installation directory.

  1. Confirm the UI Bakery version is 3.177.2 or newer. If it is older, update UI Bakery first and validate the instance before continuing with MySQL.

Use the installed version shown in the UI, or inspect the configured/running images:

grep '^UI_BAKERY_VERSION=' .env || true
docker compose images

If UI_BAKERY_VERSION=latest, confirm the actually running UI Bakery version in the application UI or from the image tag/digest used during the latest update.

  1. Optional: schedule a maintenance notice if you want to show users an in-product maintenance notice before the downtime.

Add the variables to .env:

# Scheduled maintenance start time (ISO 8601 format, UTC/GMT)
UI_BAKERY_MAINTENANCE_TIME_GMT="2026-05-02T08:00:00Z"

# How many hours before maintenance to show the warning banner
UI_BAKERY_MAINTENANCE_NOTICE_PRIOR_HOURS="24"

Maintenance notifications appear only in the UI Bakery interface, not in shared applications. For Docker Compose, .env changes require recreating the frontend containers, not only restarting already-created containers:

docker compose up -d --force-recreate bakery-front workbench-front

This optional step is separate from the database maintenance window and can be done earlier.

  1. Confirm the running services and MySQL volume:
docker compose ps
docker volume ls | grep my-db

2. Back up configuration and data

  1. Create a timestamped backup directory:
BACKUP_DIR="backups/mysql-8.0-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
  1. Back up configuration files:
cp docker-compose.yml "$BACKUP_DIR"/
cp .env "$BACKUP_DIR"/ 2>/dev/null || true
  1. Create a logical MySQL dump:
docker compose exec -T db sh -c 'mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --single-transaction --routines --triggers --events --all-databases' > "$BACKUP_DIR/mysql-all-databases.sql"
  1. Stop UI Bakery application services so they no longer write to MySQL, but keep MySQL running for a clean shutdown command:
docker compose stop bakery-gateway bakery-back automation datasource python-runtime bakery-front workbench-front
  1. Request a slow InnoDB shutdown and stop MySQL:
docker compose exec db sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "SET GLOBAL innodb_fast_shutdown=0;"'
docker compose stop db
  1. Find the actual MySQL Docker volume name:
docker volume ls | grep my-db
  1. Create a tar backup of the MySQL volume. Replace <MYSQL_VOLUME_NAME> with the exact volume name from the previous command:
docker run --rm -v <MYSQL_VOLUME_NAME>:/volume -v "$PWD/$BACKUP_DIR":/backup alpine tar czf /backup/my-db-volume.tgz -C /volume .

Keep both the logical dump and the volume backup until the upgrade is verified.

3. Update docker-compose.yml

In the db service, change:

image: mysql:8.0
command: --default-authentication-plugin=mysql_native_password  --skip-log-bin

to:

image: mysql:8.4
command: --mysql-native-password=ON --skip-log-bin

Keep the existing my-db:/var/lib/mysql volume mount unchanged.

4. Start MySQL 8.4 and then the application

  1. If you added the optional maintenance notice and no longer need it, remove UI_BAKERY_MAINTENANCE_TIME_GMT and UI_BAKERY_MAINTENANCE_NOTICE_PRIOR_HOURS from .env.

  2. Pull only the MySQL image first, start MySQL, and watch the logs:

docker compose pull db
docker compose up -d db
docker compose logs -f db
  1. Wait until MySQL starts successfully and the db container becomes healthy.

  2. Start the full stack:

docker compose up -d

5. Validate the upgrade

  1. Confirm MySQL version and native password plugin status:
docker compose exec db sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "SELECT VERSION(); SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = '\''mysql_native_password'\'';"'

Expected result:

  • VERSION() starts with 8.4.

  • mysql_native_password exists and is ACTIVE.

  1. Confirm containers are running:
docker compose ps
  1. Open the UI Bakery instance and verify:
  • Login works.

  • Workspace loads.

  • An app opens.

  • A simple save/publish flow works.

6. Roll back if needed

Use rollback only if validation fails and the issue cannot be fixed with a forward-only correction. Do not start mysql:8.0 against a data directory that has already been opened by MySQL 8.4.

Preferred rollback: restore VM/disk snapshot

  1. Stop the stack if it is still running:
docker compose down
  1. Restore the pre-upgrade VM or disk snapshot using the hosting provider tooling.

  2. Start the restored instance and confirm it is back on MySQL 8.0:

cd ~/ui-bakery-on-premise
docker compose up -d
docker compose exec db sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "SELECT VERSION();"'
  1. Run the UI Bakery smoke check: login, workspace loading, app opening, and a simple save/publish flow.

Fallback rollback: restore Docker volume backup

Use this only if there is no VM/disk snapshot and the my-db-volume.tgz backup was created before the upgrade.

  1. Stop the stack:
docker compose down
  1. Restore the previous configuration:
cp "$BACKUP_DIR/docker-compose.yml" ./docker-compose.yml
cp "$BACKUP_DIR/.env" ./.env 2>/dev/null || true
  1. Confirm the exact Docker volume name. <MYSQL_VOLUME_NAME> must be the fully resolved name from docker volume ls, for example <compose_project>_my-db, not just the logical Compose name my-db:
docker volume ls | grep my-db
docker volume inspect <MYSQL_VOLUME_NAME>
  1. Remove the upgraded volume, let Docker Compose recreate the volume with the expected Compose metadata, and restore the backup into that volume:
docker volume rm <MYSQL_VOLUME_NAME>
docker compose create db
docker run --rm -v <MYSQL_VOLUME_NAME>:/volume -v "$PWD/$BACKUP_DIR":/backup alpine tar xzf /backup/my-db-volume.tgz -C /volume
  1. Start the stack:
docker compose up -d
  1. Validate rollback:
docker compose exec db sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "SELECT VERSION();"'
docker compose ps
  1. Open the UI Bakery instance and verify:
  • Login works.

  • Workspace loads.

  • An app opens.

  • A simple save/publish flow works.

Fallback rollback: restore logical dump into a new volume

Use this only if storage-level restore is unavailable. It can take longer and should be treated as the last option.

  1. Stop the stack:
docker compose down
  1. Restore the previous configuration with mysql:8.0:
cp "$BACKUP_DIR/docker-compose.yml" ./docker-compose.yml
cp "$BACKUP_DIR/.env" ./.env 2>/dev/null || true
  1. Confirm the exact Docker volume name, remove the upgraded volume, and let Compose recreate a fresh one:
docker volume ls | grep my-db
docker volume rm <MYSQL_VOLUME_NAME>
docker compose create db
  1. Start only MySQL and wait until it is ready:
docker compose up -d db
docker compose logs -f db
  1. Import the logical dump:
docker compose exec -T db sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD"' < "$BACKUP_DIR/mysql-all-databases.sql"
  1. Start the full stack and validate:
docker compose up -d
docker compose ps
Kubernetes

Use this section for customer-managed Kubernetes deployments based on uibakery/self-hosted/kubernetes.

Current expected embedded MySQL resources:

  • Namespace: ui-bakery
  • Deployment: mysql
  • Service: mysql
  • PVC: mysql-pv-claim
  • Image before upgrade: mysql:8.0

1. Preparing

  1. Confirm the UI Bakery version is 3.177.2 or newer. If it is older, update UI Bakery first and validate the instance before continuing with MySQL.

Inspect the configured/running images:

kubectl -n ui-bakery get deploy -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.template.spec.containers[*].image}{"\n"}{end}'

If images use latest, confirm the actually running UI Bakery version in the application UI or from the image tag/digest used during the latest rollout.

  1. Optional: schedule a maintenance notice if you want to show users an in-product maintenance notice before the downtime.

Set the variables in the UI Bakery ConfigMap or the cluster’s environment source:

UI_BAKERY_MAINTENANCE_TIME_GMT: "2026-05-02T08:00:00Z"
UI_BAKERY_MAINTENANCE_NOTICE_PRIOR_HOURS: "24"

After changing the ConfigMap/env source, recreate or restart the frontend pods/deployments that consume it. Maintenance notifications appear only in the UI Bakery interface, not in shared applications. This optional step is separate from the database maintenance window and can be done earlier.

  1. Confirm cluster access and current resources:
kubectl -n ui-bakery get pods,pvc
kubectl -n ui-bakery get deploy mysql -o wide

2. Back up MySQL

  1. Save current deployment replica counts so they can be restored later:
kubectl -n ui-bakery get deploy -o jsonpath='{range .items[*]}{.metadata.name}{" "}{.spec.replicas}{"\n"}{end}' > ui-bakery-replicas-before-mysql-upgrade.txt
  1. Stop all non-MySQL workloads in the namespace so they cannot write to MySQL. Review the list before running it if the namespace contains resources not owned by UI Bakery:
while read name replicas; do
  if [ "$name" != "mysql" ]; then
    kubectl -n ui-bakery scale deploy "$name" --replicas=0
  fi
done < ui-bakery-replicas-before-mysql-upgrade.txt
  1. Create a logical dump:
kubectl -n ui-bakery exec deploy/mysql -- sh -c 'mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --single-transaction --routines --triggers --events --all-databases' > mysql-all-databases.sql
  1. Prepare MySQL for a storage-level snapshot and stop the MySQL pod/deployment:
kubectl -n ui-bakery exec deploy/mysql -- sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "SET GLOBAL innodb_fast_shutdown=0;"'
kubectl -n ui-bakery scale deploy/mysql --replicas=0
  1. Create a storage-level backup of PVC mysql-pv-claim.

Preferred option: create a CSI VolumeSnapshot for mysql-pv-claim using the cluster’s configured VolumeSnapshotClass. The exact manifest depends on the Kubernetes storage provider, so use the snapshot class configured for the target cluster.

If CSI snapshots are unavailable, create a cloud disk snapshot for the persistent disk backing mysql-pv-claim.

3. Update ui-bakery-database.yaml

In the MySQL deployment, change:

image: mysql:8.0

to:

image: mysql:8.4

Keep the existing PVC mysql-pv-claim unchanged.

If the Kubernetes resources are managed by Helm, GitOps, or custom IaC, update the source of truth there and sync/apply it instead of editing a local manifest manually.

4. Apply and validate

  1. If you added the optional maintenance notice and no longer need it, remove UI_BAKERY_MAINTENANCE_TIME_GMT and UI_BAKERY_MAINTENANCE_NOTICE_PRIOR_HOURS from the ConfigMap/env source, then recreate or restart the frontend pods/deployments that consume them.
  2. Apply the updated MySQL manifest or sync the cluster source of truth and wait for rollout:
kubectl apply -f ui-bakery-database.yaml
kubectl -n ui-bakery scale deploy/mysql --replicas=1
kubectl -n ui-bakery rollout status deploy/mysql
  1. Confirm MySQL 8.4 is running:
kubectl -n ui-bakery exec deploy/mysql -- sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "SELECT VERSION();"'
kubectl -n ui-bakery get pods
  1. Restore UI Bakery application component replica counts:
while read name replicas; do
  if [ "$name" != "mysql" ]; then
    kubectl -n ui-bakery scale deploy "$name" --replicas="$replicas"
  fi
done < ui-bakery-replicas-before-mysql-upgrade.txt
  1. Validate the instance:
  • Login works.
  • Workspace loads.
  • An app opens.
  • A simple save/publish flow works.
  • Backend logs do not show database migration or connection errors.

5. Roll back if needed

Do not start mysql:8.0 on a PVC that has already been opened by MySQL 8.4. Do not reuse the upgraded PVC for rollback.

  1. Stop application writes again by scaling down non-MySQL workloads:
while read name replicas; do
  if [ "$name" != "mysql" ]; then
    kubectl -n ui-bakery scale deploy "$name" --replicas=0
  fi
done < ui-bakery-replicas-before-mysql-upgrade.txt
  1. Scale MySQL down if it is still running:
kubectl -n ui-bakery scale deploy/mysql --replicas=0
  1. Restore the pre-upgrade PVC snapshot or cloud disk snapshot into a PVC that MySQL will use for rollback. The restored PVC must replace or be bound in place of the upgraded mysql-pv-claim; do not leave the Deployment pointing at the upgraded PVC.
  2. Reapply the previous manifest or source-of-truth revision with:
image: mysql:8.0
  1. Wait for MySQL rollout:
kubectl -n ui-bakery scale deploy/mysql --replicas=1
kubectl -n ui-bakery rollout status deploy/mysql
kubectl -n ui-bakery exec deploy/mysql -- sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "SELECT VERSION();"'
  1. Restore UI Bakery application component replica counts:
while read name replicas; do
  if [ "$name" != "mysql" ]; then
    kubectl -n ui-bakery scale deploy "$name" --replicas="$replicas"
  fi
done < ui-bakery-replicas-before-mysql-upgrade.txt
  1. Run the UI Bakery smoke check.

For any questions, please contact us at support@uibakery.io.