How I Safely Moved MariaDB Data Directory to Another Disk on Debian 13 (and Moved It Back Again) - How I Safely Moved MariaDB Data Directory to Another Disk on

How I Safely Moved MariaDB Data Directory to Another Disk on Debian 13 (and Moved It Back Again)

If you’re running a Debian VPS with Virtualmin and MariaDB, sooner or later you’ll run into the same problem I did: your root partition is almost full, but MariaDB needs even more space to rebuild a huge table.

In my case, I needed to add a generated column and a unique index to an 11 GB InnoDB table. Unfortunately, MariaDB rebuilds the table during this operation, and the remaining free space on my root partition simply wasn’t enough.

Instead of resizing partitions or deleting data, I temporarily moved the MariaDB data directory to a larger secondary volume, completed the database changes, and then moved everything back.

This guide documents the process.


My Server Setup

Here’s the environment I was working with.

  • Debian 13
  • MariaDB 11.8
  • Virtualmin
  • A Cloud VPS
  • Root filesystem (/dev/sda1) almost full
  • Existing 10 GB volume for Virtualmin backups
  • New 40 GB volume attached as /dev/sdc

The important part looked like this:

/dev/sda1    38 GB
Used:        30 GB
Free:         6 GB

/var/lib/mysql
≈15 GB

The largest table was:

bersih.ibd
11 GB

Why Moving the Data Directory Was the Safest Option

Adding a STORED generated column is not just a metadata change.

For large InnoDB tables, MariaDB rebuilds the table.

That means temporary disk usage can easily reach:

  • existing table
  • rebuilt table
  • temporary workspace
  • index creation

For an 11 GB table, MariaDB may temporarily need more than 20 GB of free space.

With only 6 GB available, failure was almost guaranteed.

Instead of risking a half-finished rebuild, I decided to temporarily move the entire MariaDB data directory onto the new 40 GB volume.


First, Clean Up Easy Disk Space

Before touching MariaDB, I reclaimed every bit of space I could.

One log file immediately stood out:

/var/log/proftpd/sftp.log
587 MB

Don’t delete log files that services are actively writing to.

Instead, truncate them:

truncate -s 0 /var/log/proftpd/sftp.log

Or:

: > /var/log/proftpd/sftp.log

I also cleaned old system journals:

journalctl --vacuum-time=14d

Then removed cached package downloads:

apt clean

Every extra megabyte helps when you’re working on a nearly full server.


Always Make Two Backups

I know backups are boring until they’re suddenly very exciting.

I created both a logical backup and a physical backup.

Logical backup

mkdir /root/mysql-backup

mysqldump 
--single-transaction 
--routines 
--events 
--triggers 
--all-databases 
> /root/mysql-backup/all.sql

Physical backup

Since the new volume was almost empty, I copied the existing data directory there as an extra safety net.

rsync -aHAX /var/lib/mysql/ 
/mnt/HC_Volume_555555555/mysql-backup/

Now I had two recovery options if something went wrong.


Moving MariaDB to the New Volume

After stopping MariaDB:

systemctl stop mariadb

I copied the entire data directory:

mkdir /mnt/HC_Volume_555555555/mysql

rsync -aHAX --info=progress2 
/var/lib/mysql/ 
/mnt/HC_Volume_555555555/mysql/

I never delete the original immediately.

Instead, I renamed it:

mv /var/lib/mysql /var/lib/mysql.old

Then created a new mount point:

mkdir /var/lib/mysql

Why I Used a Bind Mount

There are two common ways to move MariaDB’s data directory.

One is editing my.cnf and changing datadir.

The other is using a bind mount.

I chose the bind mount because MariaDB continues believing the data directory is still:

/var/lib/mysql

Nothing in the MariaDB configuration needs to change.

Mount it like this:

mount --bind 
/mnt/HC_Volume_555555555/mysql 
/var/lib/mysql

Then make it persistent by adding this line to /etc/fstab:

/mnt/HC_Volume_555555555/mysql 
/var/lib/mysql 
none bind 0 0

After that:

mount -a

Finally, verify permissions:

chown -R mysql:mysql 
/mnt/HC_Volume_555555555/mysql

Then start MariaDB again:

systemctl start mariadb

Everything should behave exactly as before.


Running the Table Modification

Originally I planned to run two separate statements.

ALTER TABLE bersih
ADD COLUMN article_url_hash ...

followed by

ALTER TABLE bersih
ADD UNIQUE KEY ...

A better approach is combining them into a single ALTER TABLE.

ALTER TABLE bersih
ADD COLUMN article_url_hash CHAR(32)
GENERATED ALWAYS AS (MD5(article_url)) STORED,
ADD UNIQUE KEY uniq_article_url_hash (article_url_hash);

This often allows MariaDB to rebuild the table only once, reducing both execution time and temporary disk usage.

For an 11 GB table, don’t expect this to finish in a few minutes.

Depending on your storage and CPU, it could easily take an hour or more.

The important part is simple:

Don’t interrupt it.


Moving Everything Back

After the database changes completed successfully, I reversed the process.

Stop MariaDB:

systemctl stop mariadb

Unmount the bind mount:

umount /var/lib/mysql

Remove the temporary bind entry from /etc/fstab.

Then copy everything back:

rsync -aHAX --info=progress2 
/mnt/HC_Volume_555555555/mysql/ 
/var/lib/mysql/

Start MariaDB again:

systemctl start mariadb

Only after verifying that everything worked normally did I remove the old backup directory.

rm -rf /var/lib/mysql.old

Never delete it until you’ve confirmed your databases are healthy.


Common Mistakes to Avoid

A few mistakes can turn a routine migration into a stressful recovery.

  • Don’t delete the original data directory before testing the new one.
  • Don’t forget to preserve file ownership and permissions.
  • Don’t modify MariaDB while it’s still running.
  • Don’t run huge ALTER TABLE operations without enough temporary disk space.
  • Don’t skip backups, even if the server has been stable for years.

Most migration problems come from rushing, not from the commands themselves.


Final Thoughts

This ended up being much simpler than resizing partitions or trying to free tens of gigabytes on the root filesystem.

Using a temporary bind mount kept the MariaDB configuration unchanged, made rollback easy, and let me safely complete the table rebuild on a much larger volume.

If you only need the extra space temporarily, this is a clean solution that’s easy to reverse once the database work is finished.

Sometimes the safest fix isn’t changing the application at all—it’s simply giving it enough room to work.

No Comments

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.