[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 Full allowed.
  • 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 -c for 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_name from _ to moodle.newroadgi.com.
  • Changed $CFG->wwwroot from http://158.69.206.122http://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->wwwroot to https://….
  • moodledata double-nested → flattened; ownership/perms restored.
  • PPA package names → exif/sodium not needed as separate packages.