[Moodle Migration][Phase 4] Restore Moodle 4.0.9 to staging
Context / intent
- Staging hostname: moodle.newroadgi.com → VPS IP
158.69.206.122. - Restore backups (code + moodledata + DB), validate on HTTP, add TLS, then keep email off and cron on.
Process
4.0 Base (pre-existing from earlier steps)
- UFW:
OpenSSH,Nginx Fullallowed. - MariaDB 11.4 LTS installed via upstream repo; local-only bind; utf8mb4 defaults.
- DB + user created:
moodle/moodleuser@localhost(strong pw). - Created a small “ops bundle” tar with Nginx/MariaDB/PHP configs and package list (permissions fixed using
sudo bash -cfor redirection).
4.1 Upload backups to server
/tmp/learning.newroadgi.com.zip
/tmp/moodledata.zip
/tmp/wwwnewroadgi_678.sql
Result: files present in /tmp.
4.2 Restore code → /var/www/moodle
sudo apt -y install unzip
sudo rm -rf /var/www/moodle/*
sudo unzip -q /tmp/learning.newroadgi.com.zip -d /var/www/moodle
# Flatten nested folder if present:
sudo cp -a /var/www/moodle/learning.newroadgi.com/. /var/www/moodle/ 2>/dev/null || true
sudo rm -rf /var/www/moodle/learning.newroadgi.com 2>/dev/null || true
sudo chown -R www-data:www-data /var/www/moodle
Result: Moodle code + plugins in place at /var/www/moodle.
4.3 Restore moodledata → /var/www/moodledata
sudo rm -rf /var/www/moodledata/*
sudo unzip -q /tmp/moodledata.zip -d /var/www/moodledata
# It extracted as moodledata/moodledata; flatten:
sudo cp -a /var/www/moodledata/moodledata/. /var/www/moodledata/
sudo rm -rf /var/www/moodledata/moodledata
sudo chown -R www-data:www-data /var/www/moodledata
sudo chmod 750 /var/www/moodledata
# sanity: list expected dirs
sudo ls -1 /var/www/moodledata | egrep -i 'filedir|cache|localcache|temp|sessions'
Result: cache filedir localcache sessions temp present.
4.4 Import DB
sudo mariadb moodle < /tmp/wwwnewroadgi_678.sql
sudo mariadb -N -e \
"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='moodle';"
# -> 476
Result: 476 tables in moodle.
4.5 Create / edit config.php
sudo tee /var/www/moodle/config.php >/dev/null <<'EOF'
<?php
unset($CFG); global $CFG; $CFG = new stdClass();
$CFG->dbtype='mariadb'; $CFG->dblibrary='native';
$CFG->dbhost='localhost'; $CFG->dbname='moodle';
$CFG->dbuser='moodleuser'; $CFG->dbpass='REPLACE_ME';
$CFG->prefix='mdlf2_'; # matches dump
$CFG->wwwroot='http://158.69.206.122'; # first set to IP/http
$CFG->dataroot='/var/www/moodledata';
$CFG->directorypermissions=02750;
require_once(__DIR__.'/lib/setup.php');
EOF
sudo chown www-data:www-data /var/www/moodle/config.php
sudo chmod 640 /var/www/moodle/config.php
Bump encountered: When $CFG->wwwroot lacked http://, the browser rewrote the URL to /IP/IP. Fixed by using full scheme.
4.6 Nginx vhost (HTTP) + enable site
sudo tee /etc/nginx/sites-available/moodle >/dev/null <<'EOF'
server {
listen 80;
server_name _; # later changed to moodle.newroadgi.com
root /var/www/moodle;
index index.php;
client_max_body_size 128m;
# ACME http-01
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /var/www/moodle;
allow all;
try_files $uri =404;
}
location / { try_files $uri $uri/ /index.php?$query_string; }
# (initially this was a simple \.php$ block — replaced later with PATH_INFO)
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.0-fpm.sock;
}
}
EOF
# enable moodle site, disable default, reload
sudo ln -sf /etc/nginx/sites-available/moodle /etc/nginx/sites-enabled/moodle
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx
Bump encountered: Initially hit the default Nginx page until the vhost was enabled and default removed.
4.7 Fix CSS/assets (PATH_INFO block) Replaced the simple PHP block with PATH_INFO-aware block:
location ~ \.php(/|$) {
fastcgi_split_path_info ^(.+\.php)(/.*)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name $fastcgi_script_name/ =404;
fastcgi_pass unix:/run/php/php8.0-fpm.sock;
include fastcgi_params;
fastcgi_param PATH_INFO $path_info;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_read_timeout 300;
}
Result: CSS/theme assets loaded correctly (no more plain HTML).
4.8 Switch host + clean redirects (still HTTP)
- Edited vhost
server_namefrom_tomoodle.newroadgi.com. - Changed
$CFG->wwwrootfromhttp://158.69.206.122→http://moodle.newroadgi.com. Result: Site loaded at the hostname (HTTP).
4.9 Issue TLS (Certbot) + redirect
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo certbot --nginx -d moodle.newroadgi.com --redirect
- ACME path already allowed by vhost.
- Certbot added 443 block + HTTP→HTTPS redirect.
4.10 Switch Moodle to HTTPS
# in /var/www/moodle/config.php
$CFG->wwwroot = 'https://moodle.newroadgi.com';
Result: Lock shown; CSS ok over HTTPS. Note: If CSS had broken again after TLS, PATH_INFO block needed to be effective under :443 as well (ours was).
4.11 Staging safety: disable email
# in config.php
$CFG->noemailever = true;
4.12 Cron (per minute)
echo '* * * * * www-data /usr/bin/php8.0 /var/www/moodle/admin/cli/cron.php >/dev/null 2>&1' \
| sudo tee /etc/cron.d/moodle
sudo chmod 644 /etc/cron.d/moodle
sudo systemctl restart cron
# one-off manual run for sanity
sudo -u www-data php8.0 /var/www/moodle/admin/cli/cron.php
# ... finished with "Cron script completed correctly"
Outcome / current state
- Staging live at https://moodle.newroadgi.com.
- Email suppressed; cron running.
- Nginx uses PATH_INFO-aware PHP block; PHP 8.0 active; PHP 8.2 installed and idle.
- DB prefix aligned to dump (mdlf2_).
Files I touched (summary)
/etc/nginx/sites-available/moodle(created + edited)/etc/nginx/sites-enabled/moodle(symlink)/var/www/moodle/config.php(created/edited)/etc/php/8.0/fpm/conf.d/50-moodle.ini(created)/etc/cron.d/moodle(created)
Bumps I hit (inline resolutions)
- Default site winning → enabled moodle vhost, removed
default. - URL rewriting to
/IP/IP→ added scheme to$CFG->wwwroot. - Plain HTML / no CSS → switched to PATH_INFO PHP block.
- “Not secure” after cert → changed
$CFG->wwwroottohttps://…. - moodledata double-nested → flattened; ownership/perms restored.
- PPA package names → exif/sodium not needed as separate packages.