If you’re running MariaDB (or MySQL compatible) on a Debian server, by default your database files live under:

/var/lib/mysql

That’s fine until your root disk starts filling up, or you want your databases on a larger or faster volume mounted under /home.

In this guide, we’ll:

  • Move MariaDB’s data directory from /var/lib/mysql to /home/mysql
  • Use a POSIX sh shell script that:
    • Creates backups automatically
    • Handles permissions and systemd hardening
    • Adjusts AppArmor if needed
    • Rolls back if something goes wrong

You can use this on Debian 12 (and similar) with mariadb.service running under systemd.


Why Move MariaDB to /home/mysql?

Common reasons:

  • Your / (root) filesystem is small or almost full
  • You’ve attached a bigger disk and mounted it at /home
  • You want a clean separation of OS files and data files
  • You’re planning for better performance and easier backup strategies

Instead of manually fighting with permissions, systemd, and AppArmor every time, we’ll let a script do the heavy lifting.


Prerequisites

Before you start, make sure:

  • You are on a system using systemd
  • MariaDB is installed and managed as mariadb.service
  • You can run commands as root (or via sudo)
  • rsync is installed:
apt install rsync -y

Important: Always back up your databases before any migration. The script itself creates backups of the data directory, but having your own mariadb-dump is wise.

Example full backup:

mariadb-dump --add-drop-table --all-databases | gzip > /home/alldatabases.sql.gz

What the Script Does

The script:

  1. Verifies you are root and that mariadb.service exists
  2. Stops MariaDB
  3. Backs up the existing data directory /var/lib/mysql
  4. Creates /home/mysql (and backs up its contents if it’s non-empty)
  5. Uses rsync to copy all data from /var/lib/mysql to /home/mysql
  6. Sets correct ownership and permissions
  7. Moves the original /var/lib/mysql aside and replaces it with a symlink to /home/mysql
  8. Creates a systemd override for mariadb.service:
    • ProtectHome=read-only
    • ReadWritePaths=/home/mysql
  9. Updates AppArmor (if a mysqld profile exists) to allow /home/mysql
  10. Starts MariaDB again
  11. If starting fails, it rolls back:
    • Restores the original directory
    • Removes the override
    • Tries to start MariaDB from the original location

This gives you:

  • A safer migration
  • Automatic rollback in case something breaks
  • Backups kept with timestamps

Step-by-Step: Using the Script

1. Create the Script File

Log in as root (or use sudo -i) and create a new file:

nano /root/move-mariadb-to-home.sh

Paste the full script below (in the next section) into that file, then save and exit.

2. Make the Script Executable

chmod +x /root/move-mariadb-to-home.sh

3. Run the Script

Run it as root:

/root/move-mariadb-to-home.sh

You’ll see logs like:

[*] Stopping MariaDB...
[*] Backing up old data directory /var/lib/mysql to /var/lib/mysql.backup-20251129-171500 ...
[*] Syncing data from /var/lib/mysql to /home/mysql ...
[*] Replacing /var/lib/mysql with symlink to /home/mysql ...
[*] Configuring systemd override at /etc/systemd/system/mariadb.service.d/override.conf ...
[*] Starting MariaDB with new data location...
[*] MariaDB started successfully.

If something fails, the script will attempt a rollback and tell you where the backups are.


Verifying the New Setup

After running the script, verify:

Check MariaDB status

systemctl status mariadb

You should see active (running).

Check directories and symlink

ls -ld /var/lib/mysql
ls -ld /home/mysql

Expected:

  • /var/lib/mysql is a symlink → /home/mysql
  • /home/mysql is owned by mysql:mysql

Check datadir value

MariaDB will still report /var/lib/mysql as the data directory:

mysql -e "SHOW VARIABLES LIKE 'datadir';"

Even though it says /var/lib/mysql/, the actual files now live in /home/mysql thanks to the symlink.


Full Script: move-mariadb-to-home.sh

Paste this into /root/move-mariadb-to-home.sh:

#!/bin/sh
#
# Move MariaDB data directory from /var/lib/mysql to /home/mysql on Debian 12
# with backups, fault tolerance, and basic auto-correction.
#
# Must be run as root.

set -eu

OLD_DATADIR="/var/lib/mysql"
NEW_DATADIR="/home/mysql"
BACKUP_SUFFIX="$(date +%Y%m%d-%H%M%S)"

log() {
    echo "[*] $*"
}

err() {
    echo "[!] $*" >&2
}

# --- Safety checks ---------------------------------------------------------

if [ "$(id -u)" != "0" ]; then
    err "This script must be run as root."
    exit 1
fi

if ! command -v systemctl >/dev/null 2>&1; then
    err "systemctl not found. This script is intended for systemd-based systems."
    exit 1
fi

if ! systemctl list-unit-files | grep -q '^mariadb.service'; then
    err "mariadb.service not found. Are you sure MariaDB is installed?"
    exit 1
fi

if [ -L "$OLD_DATADIR" ]; then
    log "$OLD_DATADIR is already a symlink. Aborting to avoid nested moves."
    ls -ld "$OLD_DATADIR"
    exit 1
fi

if [ ! -d "$OLD_DATADIR" ]; then
    err "Old data directory $OLD_DATADIR does not exist or is not a directory."
    exit 1
fi

# --- Stop MariaDB ---------------------------------------------------------

log "Stopping MariaDB..."
if ! systemctl stop mariadb; then
    err "Failed to stop mariadb.service. Aborting."
    exit 1
fi

# --- Prepare target directory ---------------------------------------------

log "Creating new data directory at $NEW_DATADIR if needed..."
mkdir -p "$NEW_DATADIR"

# Backup existing NEW_DATADIR if it is non-empty
if [ "$(find "$NEW_DATADIR" -mindepth 1 -maxdepth 1 2>/dev/null | wc -l)" -gt 0 ]; then
    NEW_BACKUP="${NEW_DATADIR}.preexist-backup-${BACKUP_SUFFIX}"
    log "$NEW_DATADIR is not empty. Moving its contents to $NEW_BACKUP"
    mkdir -p "$NEW_BACKUP"
    mv "$NEW_DATADIR"/* "$NEW_BACKUP"/ 2>/dev/null || true
fi

# --- Backup old data dir --------------------------------------------------

OLD_BACKUP="${OLD_DATADIR}.backup-${BACKUP_SUFFIX}"
log "Backing up old data directory $OLD_DATADIR to $OLD_BACKUP ..."
cp -a "$OLD_DATADIR" "$OLD_BACKUP"

# --- Move data to new location (rsync for safety) ------------------------

log "Syncing data from $OLD_DATADIR to $NEW_DATADIR ..."
if ! command -v rsync >/dev/null 2>&1; then
    err "rsync not found. Install rsync and run again."
    # Restore from backup (best effort)
    log "Restoring from backup..."
    rm -rf "$NEW_DATADIR"/*
    cp -a "$OLD_BACKUP"/* "$OLD_DATADIR"/ 2>/dev/null || true
    exit 1
fi

rsync -aHAX --delete "$OLD_DATADIR"/ "$NEW_DATADIR"/

# --- Fix ownership & perms ------------------------------------------------

log "Setting ownership and permissions on $NEW_DATADIR ..."
chown -R mysql:mysql "$NEW_DATADIR"
chmod 755 /home
find "$NEW_DATADIR" -type d -exec chmod 750 {} \; 2>/dev/null || true
find "$NEW_DATADIR" -type f -exec chmod 640 {} \; 2>/dev/null || true

# --- Replace OLD_DATADIR with symlink ------------------------------------

log "Replacing $OLD_DATADIR with symlink to $NEW_DATADIR ..."

# Move original out of the way (we already backed it up above)
OLD_ORIG="${OLD_DATADIR}.orig-${BACKUP_SUFFIX}"
mv "$OLD_DATADIR" "$OLD_ORIG"

# Create symlink
ln -s "$NEW_DATADIR" "$OLD_DATADIR"

# --- systemd override: ProtectHome & ReadWritePaths ----------------------

OVERRIDE_DIR="/etc/systemd/system/mariadb.service.d"
OVERRIDE_CONF="$OVERRIDE_DIR/override.conf"

log "Configuring systemd override at $OVERRIDE_CONF ..."
mkdir -p "$OVERRIDE_DIR"

cat > "$OVERRIDE_CONF" <<EOF
[Service]
ProtectHome=read-only
ReadWritePaths=$NEW_DATADIR
EOF

log "Reloading systemd daemon..."
systemctl daemon-reload

# --- AppArmor tweak (if profile exists) ----------------------------------

if command -v aa-status >/dev/null 2>&1; then
    if aa-status 2>/dev/null | grep -q "usr.sbin.mysqld"; then
        log "AppArmor profile for mysqld detected. Updating paths..."
        APPARMOR_PROFILE="/etc/apparmor.d/usr.sbin.mysqld"
        if [ -f "$APPARMOR_PROFILE" ]; then
            # Only append if not already present
            if ! grep -q "$NEW_DATADIR/" "$APPARMOR_PROFILE" 2>/dev/null; then
                cat >> "$APPARMOR_PROFILE" <<EOF

# Added by move-mariadb-to-home.sh
$NEW_DATADIR/ r,
$NEW_DATADIR/** rwk,
EOF
                log "Reloading AppArmor..."
                systemctl reload apparmor || true
            else
                log "AppArmor profile already contains $NEW_DATADIR rules."
            fi
        else
            log "AppArmor profile file $APPARMOR_PROFILE not found; skipping."
        fi
    else
        log "No enforcing AppArmor profile for mysqld reported; skipping AppArmor changes."
    fi
else
    log "AppArmor tools not installed; skipping AppArmor changes."
fi

# --- Start MariaDB and verify --------------------------------------------

log "Starting MariaDB with new data location..."
if ! systemctl start mariadb; then
    err "MariaDB failed to start. Attempting rollback..."

    # Stop again just in case
    systemctl stop mariadb || true

    # Remove symlink and restore original dir
    rm -f "$OLD_DATADIR"
    mv "$OLD_ORIG" "$OLD_DATADIR"

    # Restore original override (best effort: just move file away)
    if [ -f "$OVERRIDE_CONF" ]; then
        mv "$OVERRIDE_CONF" "${OVERRIDE_CONF}.failed-${BACKUP_SUFFIX}" || true
        systemctl daemon-reload || true
    fi

    # Try starting MariaDB from original location
    systemctl start mariadb || true

    err "Rollback attempted. MariaDB may be running from the original location."
    err "Check 'systemctl status mariadb' and logs with 'journalctl -u mariadb.service'."
    err "Backup of original data: $OLD_BACKUP"
    exit 1
fi

# --- Final checks ---------------------------------------------------------

log "MariaDB started successfully."

# Test that mysql user can write to new dir
if sudo -u mysql touch "$NEW_DATADIR/.write_test" 2>/dev/null; then
    rm -f "$NEW_DATADIR/.write_test"
    log "Verified mysql user can write to $NEW_DATADIR."
else
    err "Warning: mysql user could not write test file in $NEW_DATADIR."
fi

log "Old original datadir preserved at: $OLD_ORIG"
log "Backup copy of original data at:  $OLD_BACKUP"
log "New data directory in use via symlink: $OLD_DATADIR -> $NEW_DATADIR"

echo "[+] Migration complete."
exit 0

Conclusion

With this script and guide, you now have a repeatable, fault-tolerant way to move MariaDB’s data directory from /var/lib/mysql to /home/mysql on Debian:

  • Safer than manual copy/paste operations
  • Automatically handles systemd hardening and AppArmor
  • Easy to run again on future servers

Feel free to share or adapt this script in your own infrastructure tooling or DevOps playbooks.

Categorized in:

Miscellaneous,

Last Update: December 4, 2025

Tagged in:

, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,