Skip to content

Upgrading

How to move between Moodle versions safely.

Moving to Moodle 5.x? PHP 8.4 is available

Moodle 5.3+ uses PHP 8.4 by default. For 5.0–5.2, opt in using the -php84 tag (e.g. v5.2.1-php84). Moodle 4.5 retains PHP 8.3. See PHP 8.4 (opt-in). Do not use -php84 tags on Moodle 4.x.

The basics

The image applies Moodle database upgrades automatically on startup unless you opt out:

environment:
  AUTO_UPDATE_MOODLE: "true"   # default — runs admin/cli/upgrade.php at boot

Steps the container takes on start, when an existing installation is detected:

  1. admin/cli/maintenance.php --enable
  2. admin/cli/upgrade.php --non-interactive --allow-unstable
  3. admin/cli/maintenance.php --disable

This covers database schema upgrades. Replacing the Moodle PHP code is handled separately by the version-aware code sync (SYNC_MOODLE_CODE, default auto) when you use a persistent moodlehtml volume (#103).

Upgrading the Moodle code

On start, if the volume's Moodle $version differs from the image, the container:

  1. Preserves config.php, third-party plugins (any directory with a version.php the image does not ship — SYNC_PRESERVE_PLUGINS=true, default) and any EXTRA_PLUGIN_PATHS
  2. Rsyncs /usr/src/moodle/ → /var/www/html/ (--delete removes leftover core files)
  3. Restores the preserved paths — relocating them into public/ when upgrading across the Moodle 5.1 layout change
  4. Writes .alpine-moodle-release
  5. Continues with the usual AUTO_UPDATE_MOODLE / upgrade.php flow

This mirrors Moodle's official upgrade procedure: replace the code, keep config.php, carry custom plugins over, then run the upgrade. Moodle itself decides afterwards whether each carried-over plugin needs a DB upgrade or is incompatible.

Operator steps:

  1. Back up the database and moodledata (see Persistence & Volumes).
  2. Change the image tag:

    services:
      moodle:
        image: erseco/alpine-moodle:v5.0.2
    
  3. Pull and recreate:

    docker compose pull
    docker compose up -d
    docker compose logs -f moodle
    

    Look for a log line like Moodle code sync: 2025041401.00 → 2025041402.00.

Third-party plugins that live only on the volume are kept automatically (SYNC_PRESERVE_PLUGINS=true, default). Custom paths without a version.php should either be listed in EXTRA_PLUGIN_PATHS or reinstalled declaratively via PLUGINS / Moosh after each sync. Set SYNC_PRESERVE_PLUGINS=false if you want the sync to produce a pristine image tree instead (third-party plugin code is then deleted).

To keep today's "volume always wins" behaviour (no automatic core refresh):

environment:
  SYNC_MOODLE_CODE: "never"

The container filesystem is the code. Upgrading is just:

docker compose pull
docker compose up -d

Re-install plugins on every recreate with PLUGINS or POST_CONFIGURE_COMMANDS + Moosh.

Still valid if you want a clean tree:

docker compose down
docker volume rm <project>_moodlehtml
docker compose pull
docker compose up -d

Always back up first

Take a database dump and a tarball of moodledata before major upgrades. Code sync replaces files under /var/www/html (except config.php, preserved third-party plugins and EXTRA_PLUGIN_PATHS).

Upgrading from Moodle < 5.1 to ≥ 5.1

Moodle 5.1 introduces a public/ subdirectory for all web-exposed files (MDL-83424). The container handles this automatically: when it detects /var/www/html/public, it rewrites the Nginx root and runs composer install --no-dev --classmap-authoritative.

Recommended upgrade flow:

  1. Back up:
    • config.php
    • the moodledata volume
    • the database
  2. Stop the stack and remove the moodlehtml volume (as above) — this is essential because the old 5.0 layout will otherwise confuse the new server config.
  3. Change the image tag to a 5.1.x (or newer) release.
  4. Start the stack. The container installs the new code, serves it from /public, and runs composer install.
  5. Reapply customisations (plugins, themes, config.php tweaks).

If you see "/var/www/html/vendor/composer does not exist" (#117), the container has not finished bootstrapping yet. Watch docker compose logs -f moodle — the error is transient unless it recurs after 30+ seconds.

Upgrading moodledata mounted from an older installation

Mounting an existing populated moodledata from a different image (for example migrating from Bitnami) can hit permission or layout mismatches (#114, #105):

Data directory (/var/www/moodledata/) cannot be created by the installer.

Checklist:

  • The volume must be writable by UID 65534 (nobody). Fix with sudo chown -R 65534:65534 moodledata.
  • config.php on the new container must match the database — mount it alongside or inject it via POST_CONFIGURE_COMMANDS.
  • The target Moodle version must be equal to or newer than the version that created the data.

For a full Bitnami migration see #105. In short: restore the database first, mount moodledata second, ensure the admin credentials in config.php match the database, then start the container.

Disabling automatic upgrades

Set AUTO_UPDATE_MOODLE=false if you prefer to run admin/cli/upgrade.php manually:

environment:
  AUTO_UPDATE_MOODLE: "false"

Manual upgrade:

docker compose exec moodle php admin/cli/maintenance.php --enable
docker compose exec moodle php admin/cli/upgrade.php --non-interactive
docker compose exec moodle php admin/cli/maintenance.php --disable

Skipping versions

Follow the target release's minimum supported source version. Moodle 5.3 supports upgrading directly from 4.5; intermediate 5.0/5.1/5.2 upgrades are not required. Test the database requirements and actual plugins on a restored staging copy, and back up both the database and files. See the 5.3 migration plan.