(This article is the logical follow-up to the backup script described here: Nextcloud Backup Like a Pro – Automated with Bash. Both posts go hand in hand to complete the full migration and disaster recovery (workflow.)

After several years of running a trusty Nextcloud instance β€” complete with countless user files, calendars, contacts, and quirky shared folders β€” it was time for an upgrade. The old server was showing signs of age, and a shiny new machine had been provisioned with Ansible for a clean start.

Instead of messing around with manual exports, flaky web GUIs, or browser-based backup plugins, I opted for a lean and mean Bash script to handle the entire migration. It allowed me to relocate the entire Nextcloud stack (core files, user data, and database) to the new server β€” reproducibly and with minimal downtime.

🧠 What This Script Does

  1. Gracefully stops services on the target server to avoid file lock or conflict issues.
  2. Syncs Nextcloud application files (excluding the data/ directory).
  3. Transfers user data separately to its new destination path.
  4. Restores the database from a local dump using scp and mysql.
  5. Fixes ownership/permissions for web server access.
  6. Restarts PHP and web services after everything is in place.
  7. Confirms completion with a final status message.

πŸ“ Directory Layout Notes

As part of the migration, we also relocated the data/ directory to a dedicated location outside of the Apache web root. This is a cleaner and more secure approach than having user data stored inside the main Nextcloud installation directory (which was the case before).

This script takes that change into account by transferring the data/ folder separately and placing it into its new, external location on the target server.

βš™οΈ The Migration Script


#!/bin/bash

# === CONFIGURATION ===

REMOTE_USER="root"
REMOTE_HOST="your.remote.ip"
REMOTE_SSH_PORT=22
SSH_KEY="$HOME/.ssh/id_rsa"

BACKUP_DIR="/data/backup/nextcloud/cvjm"
REMOTE_NEXTCLOUD_DIR="/var/www/html/nextcloud"
REMOTE_DATA_DIR="/data"
DB_NAME="your.db.name"
DB_USER="your.user"
DB_PASS="********"  # πŸ” Password removed for security
WWW_USER="www-data"

SSH_CMD="ssh -i $SSH_KEY -p $REMOTE_SSH_PORT -o LogLevel=ERROR $REMOTE_USER@$REMOTE_HOST"

echo "πŸ›‘ [1/7] Stopping remote web and PHP services ..."
$SSH_CMD "systemctl stop apache2 2>/dev/null || systemctl stop httpd 2>/dev/null"
$SSH_CMD "systemctl stop php8.2-fpm 2>/dev/null || true"

echo "πŸ“€ [2/7] Transferring Nextcloud files (excluding data/) to remote server ..."
rsync -a --delete --exclude "data/" \
  -e "ssh -i $SSH_KEY -p $REMOTE_SSH_PORT -o LogLevel=ERROR" \
  "$BACKUP_DIR/nextcloud/" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_NEXTCLOUD_DIR/"

echo "πŸ“€ [3/7] Transferring user data (nextcloud/data/) to remote /data ..."
rsync -a --delete \
  -e "ssh -i $SSH_KEY -p $REMOTE_SSH_PORT -o LogLevel=ERROR" \
  "$BACKUP_DIR/nextcloud/data/" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_DATA_DIR/"

echo "πŸ›’οΈ  [4/7] Transferring and restoring database dump ..."
scp -i "$SSH_KEY" -P $REMOTE_SSH_PORT "$BACKUP_DIR/db.sql" "$REMOTE_USER@$REMOTE_HOST:/tmp/db.sql"
$SSH_CMD "mysql -u '$DB_USER' -p'********' '$DB_NAME' < /tmp/db.sql && rm /tmp/db.sql" echo "πŸ”’ [5/7] Setting remote file permissions ..." $SSH_CMD "chown -R '$WWW_USER:$WWW_USER' '$REMOTE_NEXTCLOUD_DIR'" $SSH_CMD "chown -R '$WWW_USER:$WWW_USER' '$REMOTE_DATA_DIR'" echo "πŸš€ [6/7] Starting remote services ..." $SSH_CMD "systemctl start php8.2-fpm 2>/dev/null || true"
$SSH_CMD "systemctl start apache2 2>/dev/null || systemctl start httpd 2>/dev/null"

echo "βœ… [7/7] Remote restore complete. Please verify your Nextcloud instance at: https://$REMOTE_HOST"

πŸŽ‰ Final Thoughts

Migrating a live Nextcloud instance that’s been around for years β€” with real data and real users β€” can be intimidating. But with a structured approach and proper automation, it doesn’t have to be. Thanks to this script and a clean Ansible-based target environment, the entire process took under 30 minutes and required no manual tweaking. Nerds rejoice.

If you’re planning your own Nextcloud migration, especially with large data volumes and the need for zero human error, give this approach a try β€” and tweak it to fit your infra style.

By raphael

Leave a Reply