[TIP] Mass-Migrate Websites Between ISPConfig Servers with a simple rsync Script

Discussion in 'Tips/Tricks/Mods' started by ustoopia, Jun 12, 2025.

  1. ustoopia

    ustoopia Member

    First off, I hope I’m not stepping on any toes of the developers behind the Migration Toolkit. I realize there’s a paid, official solution for migration, and if you need to migrate, or you want to support the project, definitely check that out. My use-case was a bit urgent and quite desperate, so I figured I’d share my approach here, in case it helps anyone else...

    Recently, I found myself needing to move numerous websites from one ISPConfig server to another after a hardware scare. I had no proper backups or snapshots, but thankfully I was able to mount the disk from the old machine onto a fresh ISPConfig server. I never used the Migration Toolkit, and the thought of using it hadn't even crossed my mind yet. So I decided to do it all mysel. After having manually added the websites on the new server in ISPconfig, I started to realize that I probably had a lot more work on my hands copying the web folders to their new location, one by one. So, I started thinking about how I could save myself some time, since I wasn't keen on a ton of manual file copying, checking and double-checking. So I asked ChatGPT for assistance, since my programming skills are about as good as those from a dying plant. CHatGPT somewhat surprised me when I found out it knows how ISPconfig works when it listed all the common folders inside a web folder. It wrote a simple script for me that worked perfectly.

    The major catch in my case: the “web” folder numbers (e.g., /var/www/web37) on the new server rarely matched those from the old server. To keep it organized, I built a simple “mapping list” in a text file, where each line looks like this:

    <destination_web_number> <optional_domain_name> <source_web_number>
    Example:
    Code:
    15    domain.nl        191
    16    sub.domain.nl        192
    17    site2.eu        17
    The domain names in the middle are more for my information. The list could in fact be as simple as this:

    Code:
    15    191
    16    192
    17    17
    The script only cares about the web numbers—the domain name is just for human reference.

    With this list ready, I reached out to ChatGPT for help automating the whole process, and it delivered a Bash script that worked perfectly. It uses rsync, so all file ownership, permissions, and symlinks are kept intact. Existing files in the destination are overwritten, but files unique to the destination folder are left untouched—no data loss.
    How it works:
    • Reads your mapping list line by line
    • For each line, copies /var/www-import/web<source_number>/ to /var/www/web<destination_number>/
    • Skips lines without proper numbers
    • Preserves all users, groups, permissions

    Here’s the script (REPLACE THE MAPPING FILE PATH AS NEEDED):

    Code:
    #!/bin/bash
    
    MAPPING_FILE="/root/web_migration_list.txt"
    SRC_BASE="/mnt/olddisk/var/www"
    DST_BASE="/var/www"
    
    while IFS=$'\t' read -r DEST DOMAIN SRC REST; do
        [[ -z "$DEST" || -z "$SRC" ]] && continue
        [[ ! "$DEST" =~ ^[0-9]+$ ]] && continue
        [[ ! "$SRC" =~ ^[0-9]+$ ]] && continue
    
        SRC_DIR="$SRC_BASE/web$SRC"
        DST_DIR="$DST_BASE/web$DEST"
    
        if [[ ! -d "$SRC_DIR" ]]; then
            echo "WARNING: Source directory $SRC_DIR does not exist. Skipping."
            continue
        fi
    
        if [[ ! -d "$DST_DIR" ]]; then
            echo "WARNING: Destination directory $DST_DIR does not exist. Skipping."
            continue
        fi
    
        echo "Copying from $SRC_DIR/ to $DST_DIR/ ..."
        rsync -a --progress --stats "$SRC_DIR/." "$DST_DIR/"
        if [[ $? -ne 0 ]]; then
            echo "ERROR: rsync failed for $SRC_DIR -> $DST_DIR"
        fi
    
    done < "$MAPPING_FILE"
    
    echo "Migration script completed."
    
    Final note: For databases, I just made sure to create new databases with the same names as before on the target server. Then I exported directly from source DB to destination DB via the export function in HeidiSQL.
    And again—double-check your mapping list! If you mess it up, your files will end up in the wrong place.

    Perhaps the script help someone with similar needs!?
     

Share This Page