CL Complete LMS

We upgrade a lot of LMS sites. Across Moodle™, Open edX, Totara, Canvas and a few others, the pattern is the same: point-release upgrades within the same major version are mostly boring (which is exactly how we like them). Major-version upgrades are where the pain lives.

This article is for anyone planning a major upgrade. It covers three things, in order: what can actually go wrong, why backing up to multiple locations matters before you commit, and the short pre-flight checklist we run before any upgrade. It is deliberately version-agnostic — the principles are the same whether you’re going from Moodle™ 3.x to 4.x, jumping a Totara release, upgrading Open edX across two named cuts, or moving a Canvas self-hosted install forward.

What actually goes wrong

In a decade of major-version upgrades, the failures are not exotic. They cluster into about six modes. If you know them in advance, you can design the pre-flight to catch them.

1. A third-party plugin breaks on the new version. Core LMS code is usually upgraded cleanly by its maintainers — they’ve test-rigged it. Third-party plugins are not. A plugin that’s no longer maintained, or whose maintainer hasn’t released a target-version build, can throw fatal errors on first page load. We see this on roughly half of large-site upgrades.

2. The active theme renders wrong, or doesn’t render. Themes depend on underlying frameworks (Bootstrap versions, Mustache templates, renderer hooks). Major-version jumps often change those. A theme that “just worked” before can show broken layouts, missing buttons, or unstyled forms after.

3. A custom local plugin or core patch uses a deprecated API. If your developer team has written code against the LMS’s internal APIs, capability names, database helpers, or course format hooks — that code can stop compiling. Particularly painful if the original author is unreachable and the code is undocumented.

4. A schema migration takes longer than your maintenance window. Some upgrades rewrite large tables (question banks, gradebooks, course-completion state). On a small site this takes seconds. On a 25,000-learner site it can take hours. If you booked a 30-minute window and the migration runs 90 minutes, your users come back to a half-upgraded site.

5. An integration’s token, endpoint or auth contract changes. LTI tool providers, SSO/SAML configurations, OAuth2 issuers, plagiarism detectors, video-conference connectors — any of these can quietly stop working when the LMS’s behaviour around them shifts.

6. The PHP or database version under the LMS is no longer supported. Major LMS releases regularly raise minimums. If you’re on PHP 7.4 and the target requires 8.1, the upgrade itself succeeds but the application fails to boot.

Each of these is preventable. The pre-flight checklist further down catches them.

Why backups in multiple locations matter — before you commit

The number-one rule of upgrades is: you should be able to fully revert to the pre-upgrade state. That sounds obvious. In practice, most teams discover it isn’t true at the moment they need it.

The reason is single points of failure in your backup arrangement. Almost every “we have backups” story has at least one:

The defensive posture before a major upgrade is straightforward: the data needs to exist in at least two independent locations, and both copies need to be verified as restorable in the past week. We use three locations for hosted clients — two clouds in different regions, plus a third immutable copy — because the marginal cost is small and the upside is enormous.

If your current arrangement is “we have one backup somewhere”, fix backups before you upgrade. A failed upgrade is a maintenance window. A failed upgrade with no working backup is an incident — and you don’t want to discover that one at 2 AM.

A workable minimum for an upgrade weekend:

That’s four artefacts, in three locations, with one of them rehearsed. If any of those is missing, postpone the upgrade until it isn’t.

The pre-flight checklist

This is the short version of what we actually run. It groups the 28 fine-grained items from our internal runbook into ten decisions. Each one is a go / no-go gate.

1. Verified, recent, multi-location backups

See the section above. If you cannot tick this honestly, stop here.

2. The exact source and target versions

Not “we’re on the LTS-ish version”. The exact build number of the source. The exact target. You’ll need both to find out what changed, and whether your plugins, theme and integrations will follow.

3. The target’s release notes, read end-to-end

Specifically the upgrade section. Note every breaking change, deprecation, capability rename, and any minimum-version bumps for PHP, the database engine, or any extension. Read the yellow flags, not just the red ones.

4. PHP, database, and OS versions confirmed supported

If any of these need a separate upgrade, do that first, on its own, with its own rehearsal and its own rollback. Never combine a PHP/DB upgrade with an LMS major upgrade — the failure surface multiplies.

5. Plugin and theme compatibility audit

For every third-party plugin and every theme, find out if a target-compatible version exists. Build a small spreadsheet: plugin name, current version, latest available, target-compatible version. Anything without a target-compatible version is a decision: replace, port, drop, or postpone.

6. Custom code reviewed against the changelog

Any local plugins, custom blocks, core patches, or theme overrides need a developer to walk through the upgrade notes and confirm none of the APIs the custom code uses have changed. Budget more time if the original author is unreachable.

7. Sandbox that’s a true clone

Same version, same plugins, same theme, same data shape, same PHP/DB versions, same OS. Restore from the same backup you’d use in a disaster. If the sandbox doesn’t match production, the rehearsal isn’t meaningful.

8. Full upgrade rehearsed end-to-end in the sandbox

Actually run it. The whole thing. Time it wall-clock from start to “Success”. If it doesn’t fit your maintenance window, change the plan — don’t try to be heroic on the night.

9. Smoke-test script run, and every integration re-checked

A defined list, written down, ticked off: admin login, course render, quiz attempt, assignment submission, file download, gradebook view, course backup/restore, learner enrolment, forum post, completion report. Plus every integration — LTI, SSO/SAML, OAuth2, payments, plagiarism, video, repositories — clicked through end-to-end.

10. Rollback plan, abort criteria, and a 24-hour change freeze

The rollback plan is normally: restore the pre-upgrade backup. Time the restore in the sandbox too — your maintenance window must be long enough for a rollback if needed, not just an upgrade. Write down the exact criteria for aborting (e.g. smoke-test fails on more than two items; p95 page-load worse than baseline by 30%; cron queue not draining within 30 minutes). Freeze production changes 24 hours before — no new courses, no plugin tweaks, no setting changes.

If you remember one thing

The upgrade itself is rarely the hardest part. The hardest part is being honest, before the night, about whether every item above is genuinely green. We have postponed upgrades — sometimes more than once on the same site — because something in this list wasn’t true. Every time, the team thanked us afterwards. Postponing is cheap. Failing live is not.


If you’d like us to drive an upgrade for you — Moodle™, Open edX, Totara, Canvas, Sakai, or another platform — the upgrades & maintenance service is this checklist, executed by us. Or if you’d just like a second pair of eyes on an upgrade you’re running yourself, say hello.