Tuesday, October 27, 2026

Moodle migration to freshly installed server - apt install and rsync of files

Claude's steps for installing the required software on the new Ubuntu 26.04 server, and migrating the old server's Moodle directories over -

Install list (Ubuntu 26.04, PHP 8.5, Apache)
 
sudo apt update && sudo apt upgrade -y
sudo apt install -y unzip git curl cron rsync rclone htop

sudo apt install -y apache2 libapache2-mod-php8.5

sudo apt install -y php8.5-cli php8.5-common php8.5-curl php8.5-gd php8.5-intl \
  php8.5-mbstring php8.5-xml php8.5-zip php8.5-soap php8.5-ldap \
  php8.5-mysql php8.5-pgsql

sudo apt install -y mysql-server postgresql

sudo apt install -y ghostscript poppler-utils graphviz aspell python3

Verify against the old server
 
# on the old server
php -m | sort > php-modules.txt
# on the new server (after copying the file over)
php -m | sort > php-modules-new.txt
diff php-modules.txt php-modules-new.txt

Next steps
  • Copy over the old php.ini settings (upload_max_filesize, post_max_size, max_input_vars ≥ 5000, memory_limit) and the Apache vhosts.
  • Check the MySQL and PostgreSQL major versions match the old server, or are newer, before restoring the dumps. Ubuntu 26.04 ships newer ones than 24.04, which is fine for restoring but not for rolling back. (which was fine for us.)
Claude suggested that I not shift the Moodle instances to postgres during the move - 
Tune MySQL and PostgreSQL for the 8 GB box: innodb_buffer_pool_size of about 1 GB, PostgreSQL shared_buffers of about 512 MB, and a sensible pm.max_children or Apache prefork limit. Whether memory is actually tight on your server depends on the real numbers, so check free -m on the Azure VM at peak load first.

Then, I asked for rclone commands for copying over everything. Claude suggested rsync instead, 

rclone doesn’t preserve file ownership or most permissions, and it can’t copy symlinks to an SFTP destination as real symlinks. 

For checking out the directory structure, Claude suggested some commands, which I supplied. Claude again dissuaded me from changing the architecture to use S3 storage instead of the mapped hard drive - so, as prompted, I created and attached a volume.

In the EC2 console, go to Elastic Block Store → Volumes → Create volume. Choose gp3 and the size. Set the Availability Zone to the same one as your new instance, or it won’t attach.

Then, to mount it,

lsblk -f
sudo mkfs.ext4 -L sssvv_data /dev/nvme1n1

sudo mkdir -p /var/www/ourpath
sudo mount /dev/nvme1n1 /var/www/ourpath

df -h /var/www/ourpath

sudo blkid /dev/nvme1n1

(for finding the UUID=value, for entering below) 

echo 'UUID=PASTE-UUID-HERE /var/www/ourpath ext4 defaults,nofail 0 2' | sudo tee -a /etc/fstab

Test with

sudo umount /var/www/ourpath
sudo mount -a
df -h /var/www/ourpath

sudo chown www-data:www-data /var/www/ourpath

Then, for copying over, Claude preferred tmux over my preference of screen. 

260 GB at a realistic 20-50 MB/s takes roughly 1.5 to 4 hours. Azure charges for outbound data, with some free monthly allowance, so expect a one-time charge of perhaps $10-20 for this copy (check your Azure pricing).

sudo apt install -y rsync tmux

On new server,

ssh-keygen -t ed25519 -f ~/.ssh/migrate_key -N ""
cat ~/.ssh/migrate_key.pub 


Copy-paste the key from the above to the old server,

echo 'PASTE-THE-PUBLIC-KEY-LINE-HERE' >> ~/.ssh/authorized_keyschmod 600 ~/.ssh/authorized_keys

Test on new server with

sudo ssh -i /home/ubuntu/.ssh/migrate_key -o StrictHostKeyChecking=accept-new azureuser@OLD-SERVER-IP 'sudo -n true && echo CONNECTED'

Dry run of copy - 

sudo rsync -aH --numeric-ids --dry-run --stats \
  -e "ssh -p 2222 -i /home/ubuntu/.ssh/migrate_key" \
  --rsync-path="sudo rsync" \
  --exclude='/our_data_disk/' \
  --exclude='/our_php_disk/' \
  azureuser@OLD-SERVER-IP:/var/www/ /var/www/ | tail -25

Then the actual copy inside a tmux instance, without --dry-run and --stats but adding progress -

tmux new -s migrate

sudo rsync -aH --numeric-ids --info=progress2 \
  -e "ssh -p 2222 -i /home/ubuntu/.ssh/migrate_key" \
  --rsync-path="sudo rsync" \
  --exclude='/our_data_disk/' \
  --exclude='/our_php_disk/' \
  azureuser@OLD-SERVER-IP:/var/www/ /var/www/

To reconnect to the tmux shell named migrate,

tmux attach -t migrate

To detach, Ctrl-b and then d

The /var/www rsync completed in just a couple of minutes.

Data copy dry run, excluding some directories which will be regenerated - 

sudo rsync -aH --numeric-ids --dry-run --stats \
  -e "ssh -p 2222 -i /home/ubuntu/.ssh/migrate_key" \
  --rsync-path="sudo rsync" \
  --exclude='/lost+found/' \
  --exclude='/our_*_data/cache/*' \
  --exclude='/our_*_data/localcache/*' \
  --exclude='/our_*_data/sessions/*' \
  --exclude='/our_*_data/temp/*' \
  azureuser@OLD-SERVER-IP:/var/www/our_data_disk/ /var/www/our_data_disk/ | tail -25

and similarly, replacing --dry-run --stats with --info=progress2 --partial inside tmux for the actual run. 

This rsync completed in just over 2 hours,
166,623,277,225 100%   21.67MB/s    2:02:12 (xfr#125429, to-chk=0/254935)




 

No comments:

Post a Comment