Moodle Multi Tenancy: How It Actually Works, and What It Cannot Do

Core Moodle has no concept of a tenant. If you need one platform to serve several organisations that must never see each other, you are choosing between IOMAD, Moodle Workplace, separate instances, or a category structure that only pretends to isolate. This page explains what each one really separates, with the version numbers, commands and failure modes that decide the answer.

In short: Moodle LMS has no built-in tenant concept, so multi tenancy comes from IOMAD's company model, Moodle Workplace's tenants, or separate instances — and all logical options share one database, one cron and one upgrade cycle.

What multi tenancy actually means in a Moodle context

People arrive at this question from very different places, and the word "tenant" is doing a lot of unspoken work. Before you compare products, separate the request into the six things that can be isolated. A multi-tenant build disappoints when someone assumed all six came in the box and the platform delivered five.

  • User visibility. Can an administrator in organisation A see, search, message or enrol a user belonging to organisation B?
  • Course catalogue. Does each organisation get its own courses, and can a course be deliberately shared across several?
  • Branding. Logo, colours, custom CSS, custom menus, certificates, and ideally a URL that carries the organisation's own name.
  • Delegated administration. Can someone inside the organisation manage their own users and enrolments without being a site administrator?
  • Reporting scope. Does a manager's report stop at their own organisation's boundary, automatically, without you remembering to filter?
  • Data separation. Are the rows in different databases, on different disks, in different backups, in different countries?

Every option below handles the first five to some degree. Only one of them handles the sixth, and that is the one requirement no amount of later configuration can retro-fit.

Why core Moodle has no true tenancy

Moodle's permission model is built from contexts, roles and capabilities. The context tree runs system, then course category, then course, then activity module, with separate branches for users and blocks. There is no context type that means "organisation", and there is no level above a category that can own a user.

A user context does exist, but it is not where you would need it. In current Moodle the user context class declares exactly one possible parent, the system context, so an account can never sit beneath a category. That is the crux: a role assigned at category context can scope what someone manages, but it cannot make other people's accounts disappear, because those accounts were never underneath the category in the first place. Every account lives in one flat site-wide user table. In a category-based setup you will find leaks in the places that are legitimately site-wide by design:

  • the user selectors used by manual enrolment, cohort membership and role assignment;
  • global search and the message recipient search;
  • site-wide reports, log reports and the site participants list;
  • calendar site events, site announcements, tag pages and badge listings;
  • course backups that include user data, which happily restore users into the wrong category;
  • web service calls, which are scoped by capability rather than by any notion of tenant.

There is also no delegated site administrator in core. A site administrator is global and bypasses capability checks entirely, so "give the client their own admin" has no safe core answer. Categories plus cohorts give you a tidy catalogue and reasonable management delegation. They do not give you privacy between organisations, and you should not sell them as though they do.

The four real options, honestly compared

IOMAD is a full Moodle distribution maintained by e-Learn Design that adds a company model on top of core. Moodle Workplace is Moodle HQ's commercial product, distributed only through Moodle Certified Partners and Service Providers, and it adds tenants, programmes, certifications and dynamic rules. Separate instances means one Moodle per organisation. Category pseudo-tenancy means core Moodle arranged carefully.

IsolatesIOMADMoodle WorkplaceSeparate instancesCategories only
Users hidden from other tenantsYes, via company membershipYes, by defaultAbsoluteNo
User in more than one tenantYes, since IOMAD 4.3, with a different role in each and a tenant switcher in the navbarNo, each user is allocated to a single tenantOnly as separate accountsNot applicable
Per-tenant course catalogueYes, plus open-shared and closed-shared coursesYes, plus a shared spaceAbsolute, but nothing is sharedPartial
Per-tenant branding and URLTheme, logo, favicon, colours, custom CSS, custom menus, certificate images, hostname matchingTenant theme settings and tenant URLsComplete freedomCategory themes only
Delegated administrationCompany manager, department manager, company reporter, educatorTenant administrator with configurable overridesA real site admin eachCategory manager, leaks upwards
Tenant-scoped reportingYes, a dedicated report set scoped by company and departmentYes, tenant-aware report builderYes, triviallyManual filtering
Separate plugin set and settingsNo, one codebaseNo, one codebaseYesNo
Separate database, files, backupsNoNoYesNo
Licensing and accessOpen source, GPLPartner or service provider contract requiredOpen source, GPLOpen source, GPL
Operational cost per tenantNear zeroNear zeroLinear and realNear zero

If you are weighing the first two against each other in detail, the trade-offs deserve their own treatment — see IOMAD compared with Moodle Workplace.

How IOMAD models companies, departments and delegated administration

IOMAD calls a tenant a company. A company record carries far more than a name: shortname, code, a parent company, theme, main and heading and link colours, custom CSS, custom menu items, a web server hostname, a maximum active user count, a contract end date, and a terminate-after date that clears licences and enrolments while keeping the reporting history intact.

Because core has nowhere to hang an organisation, IOMAD bolts on its own context level: a company context, numbered 13, whose records are inserted directly beneath the system context. That single detail summarises the whole design. Tenancy sits alongside core's permission model rather than inside it, which is why it works well and why it can never be watertight.

Inside a company sits a department tree with parent and child relationships, so you can model Glasgow and London, each with Engineering, HR and Sales beneath. Membership and rank live together in one table, which is why IOMAD reporting is fast but also why careless SQL double-counts people who sit in several departments.

The management level is a single integer on that membership row, and this is where the interface and the database disagree. The selector in the interface offers five values — 0 standard user, 1 company manager, 2 department manager, 3 educator, 4 company reporter — but only four of them are ever written to the managertype column. Educator is not a management level at all. It is a separate educator boolean on the same row, and the update path in IOMAD's company class is explicitly guarded so that managertype is left untouched when the chosen value is 3. The column comment in the install schema agrees, listing only 0, 1 and 2. Query WHERE managertype = 3 to find your educators and you will get an empty result set and a confident, wrong answer.

Company managers can only be assigned at the top level of a company. Department managers and reporters see their own department and everything beneath it, and nothing beside it.

-- Tenant headcount and management coverage, MySQL/MariaDB.
-- Swap mdl_ for your own table prefix.
-- Educators come from the `educator` flag, NOT from managertype = 3.
SELECT c.name AS company,
       COUNT(DISTINCT cu.userid) AS total_users,
       COUNT(DISTINCT CASE WHEN cu.managertype = 1 THEN cu.userid END) AS company_managers,
       COUNT(DISTINCT CASE WHEN cu.managertype = 2 THEN cu.userid END) AS department_managers,
       COUNT(DISTINCT CASE WHEN cu.managertype = 4 THEN cu.userid END) AS company_reporters,
       COUNT(DISTINCT CASE WHEN cu.educator = 1    THEN cu.userid END) AS educators,
       COUNT(DISTINCT CASE WHEN cu.suspended = 1   THEN cu.userid END) AS suspended_users
FROM mdl_local_iomad_companies c
JOIN mdl_local_iomad_company_users cu ON cu.companyid = c.id
JOIN mdl_user u ON u.id = cu.userid AND u.deleted = 0
WHERE c.suspended = 0
GROUP BY c.id, c.name
ORDER BY total_users DESC;

The DISTINCT is not decoration. The membership table is unique on company, user and department together, so a user genuinely holds one row per department and, since IOMAD 4.3, rows in more than one company. That multi-company capability is a genuine advantage over Workplace, where each user is allocated to exactly one tenant, and it is the right answer for consultants, contractors and franchise staff who legitimately work across brands.

Three other pieces are worth knowing before you design anything. Company domains assign self-signup users to a company based on their email domain. Role templates and capability restrictions let you define what a company manager is allowed to do and reuse that definition across tenants. Parent and child companies support a reseller model, where a parent allocates licences and a child splits them further.

The licence and enrolment model

IOMAD does not just use Moodle's manual and self enrolment. It ships an additional enrolment plugin, enrol_license, and a licence is the unit that company managers actually spend. A course must be marked as licensed in the IOMAD course settings before a licence can include it.

A licence record carries a name, a reference, an allocation count, a validity length in days, a start date, an expiry date, a type, a program flag, an instant-access flag, an optional cut-off date and a clear-on-expire flag. Several of those fields are conditional on the type, and that is where the confusion starts. There are five licence types, not three. The form hard-codes them as 0 Standard, 1 Reusable, 2 Course educator, 3 Course educator — reusable, and 4 Blanket.

  • Standard. A manager allocates seats. A seat cannot be recovered once the user has started using it.
  • Reusable. A manager allocates seats and can reclaim them at any time, even after the user has started. Access is removed; the user's course information is left alone.
  • Course educator and Course educator — reusable. The same two behaviours, except that the enrolment grants the teacher role rather than the student role. This is IOMAD's way of licensing the person delivering the course rather than the person taking it. If the site-level setting that auto-enrols managers as educators is switched on, both educator types are stripped out of the selector entirely.
  • Blanket. Users self-allocate by enrolling themselves, and the plugin finds the blanket licence at that moment. Blanket is also the one type that cannot be a program licence and cannot be flagged for instant access.

The duration rules are not the ones the type names suggest. It is the two reusable types that the form insists on: save one without either a validity length or a cut-off date and validation fails outright. Standard needs a validity length of at least one day. Only Course educator and Blanket escape duration validation altogether. And because the cut-off date field is disabled for both reusable types, in practice a reusable licence must carry a validity length. Clear-on-expire is disabled for the reusable types too, and stays greyed out until a cut-off date has actually been enabled.

The next trap is what the expiry date governs, because it is not when your users lose access. The schema comment is blunt: the expiry date is the date after which no further seats can be allocated. The end of a user's enrolment is set at the moment of allocation, in the licence enrolment plugin, and it depends on the type. For Standard, Course educator and Blanket, the enrolment ends at the allocation time plus the validity length in days — or on the cut-off date, if one is set. Only for the two reusable types is the enrolment end the licence's own expiry date. So on a default Standard licence, ten people who start in ten different months lose access in ten different months, and no amount of staring at the expiry date will explain it.

Clear-on-expire is likewise not tied to expiry, whatever the name suggests. IOMAD's cron looks for licences where the flag is set and the cut-off date has passed, then wipes the course interactions of the users on that licence whose tracking record is not already marked as cleared — that is, the ones who did not finish. Its own help text says the same thing in plainer words. That behaviour is exactly right for annual compliance retraining and exactly wrong if the completion record is your audit evidence. Decide which you are before you tick the box, and remember that without a cut-off date the box cannot be ticked and the clear-down would never fire anyway.

Finally, the seat arithmetic for a licence covering several courses, which also runs against intuition. On a non-program licence, the number you type is the number of course allocations: five seats across three courses means five enrolments in total, spent wherever you like. Flag the licence as a program and IOMAD multiplies your number by the course count before storing it — five users across three courses is written to allocation as 15 — while the figure you actually typed is preserved in the separate humanallocation column. The used counter follows the same units. Any reporting you write has to know which of the two it is reading, or the numbers will be wrong by exactly the number of courses.

-- Tenants close to running out of seats.
-- allocation and used are counted in COURSE seats. On a program licence
-- that is users x courses; the number the manager typed is humanallocation.
SELECT c.name AS company,
       l.name AS licence,
       l.program,
       l.humanallocation AS seats_as_entered,
       l.allocation      AS course_seats,
       l.used            AS course_seats_used,
       CASE WHEN l.program = 1
            THEN (l.allocation - l.used) / NULLIF(lc.coursecount, 0)
            ELSE  l.allocation - l.used
       END AS remaining,
       FROM_UNIXTIME(l.startdate)  AS valid_from,
       FROM_UNIXTIME(l.expirydate) AS no_new_seats_after
FROM mdl_local_iomad_company_licenses l
JOIN mdl_local_iomad_companies c ON c.id = l.companyid
LEFT JOIN (SELECT licenseid, COUNT(*) AS coursecount
             FROM mdl_local_iomad_company_license_courses
            GROUP BY licenseid) lc ON lc.licenseid = l.id
WHERE COALESCE(l.parentid, 0) = 0                 -- top-level licences only
  AND c.suspended = 0
  AND l.expirydate > UNIX_TIMESTAMP()
  AND l.allocation - l.used <= l.allocation * 0.1 -- under 10% left
ORDER BY remaining ASC, l.expirydate ASC;

The parentid filter matters the moment you run the reseller model described above. When a parent company hands part of a licence to a child, IOMAD writes a second licence row whose parentid points back at the original, so summing every row double-counts every re-allocated seat.

Branding and reporting per tenant

Branding in IOMAD is configured on the company itself. You choose a theme, upload large and small logos and a favicon, set main, heading and link colours, add custom CSS, override the custom menu, and override certificate images so a certificate carries the right organisation's marks. The distribution ships IOMAD-aware themes alongside Boost and Classic, and the IOMAD themes expose more of these per-company settings than a stock theme will.

The web server hostname setting matches the URL in the browser to a company, so clienta.yourlms.com loads that client's branding. Plan for what that implies at the infrastructure layer: DNS records per tenant, a certificate that covers every hostname or a wildcard, and a virtual host configuration that points each name at the same document root. You still run a single canonical $CFG->wwwroot, so treat hostname matching as a branding feature rather than as network isolation.

On reporting, IOMAD adds a dedicated set of tenant-scoped reports rather than asking you to filter core ones. The completion reports cover completion by course, by user, by month, and a compliance overview with a colour-coded status per user and course. Alongside those sit a company overview report, a licence allocation report, a user licence allocations report, a user login report, an outgoing email report and an attendance report for training events. All of them respect the department scope of the person running them.

What they do not do is replace bespoke analysis. Core report builder and the log reports remain site-scoped, and a site administrator sees everything. When a client asks a question the shipped reports cannot answer, custom SQL against the IOMAD tables is usually the fastest honest route.

Versions, requirements and the release lag you must plan around

IOMAD is a distribution, not a plugin bundle. You run IOMAD's copy of Moodle. Migrating an existing site means replacing your Moodle code with IOMAD code of the same version or higher and running the standard upgrade, which is straightforward but means your codebase is now IOMAD's, with IOMAD's release cadence.

That cadence is the single most under-discussed constraint. IOMAD's own stated target is three months after each Moodle release; across the last three lines the actual gap has run from three to five months. Moodle 5.0 arrived in April 2025 and IOMAD 5.0 in July 2025, a three-month gap. Moodle 4.5 arrived in October 2024 and IOMAD 4.5 in February 2025, four months. Moodle 5.1 arrived in October 2025 and IOMAD 5.1 in March 2026, five months. Moodle 5.2 has been out since 20 April 2026 and, as at 21 August 2026, there is still no IOMAD_502_STABLE branch — four months and counting. As at that date the position is:

Moodle versionReleasedGeneral support endsSecurity support endsIOMAD branch
4.5 (LTS)7 October 20246 October 20254 October 2027IOMAD_405_STABLE, at 4.5.13 — IOMAD's own LTS, released February 2025
5.014 April 202520 April 20265 October 2026IOMAD_500_STABLE, at 5.0.9 — released July 2025
5.16 October 20255 October 202619 April 2027IOMAD_501_STABLE, at 5.1.6 (Build: 20260810) — released March 2026
5.220 April 202619 April 20274 October 2027No IOMAD branch yet
5.3 (next LTS)Due 5 October 20264 October 20271 October 2029Not yet

Read that table twice, and read the general support column rather than the security one, because general support is where bug fixes and plugin compatibility stop, not merely where the security patches run out. If a feature you want landed in Moodle 5.2, no current IOMAD branch can give it to you. If you are running IOMAD 5.0.x, core security support for that line ends on 5 October 2026, so moving to the 5.1 branch is a dated obligation rather than a nice-to-have.

Be honest with yourself about what that move buys, though. Moodle 5.1 falls out of general support on 5 October 2026 as well — the very same day — and its security support runs only to 19 April 2027. Moving to the 5.1 branch in late 2026 therefore buys cover to 19 April 2027 and nothing beyond it, after which you move again, most plausibly to whichever IOMAD branch follows Moodle 5.3, the next LTS, due 5 October 2026 and supported until 1 October 2029. If you are building new rather than upgrading, IOMAD's own LTS is the 4.5 branch, at 4.5.13, with security support to 4 October 2027 — the longest runway available on a current IOMAD branch, paid for in two Moodle versions' worth of missing features. Planning either jump is the sort of work covered by my Moodle upgrade service.

Platform requirements follow core exactly, because they are core. Each column below is that version's own floor, and the figures do not carry across:

RequirementMoodle/IOMAD 5.0Moodle/IOMAD 5.1Moodle 5.2
PHP minimum8.2.08.2.08.3.0
PHP also supported8.3, 8.48.3, 8.48.4
PostgreSQL minimum141516
MySQL minimum8.48.48.4
MariaDB minimum10.11.010.11.010.11.0
MS SQL Server minimum201720172019

PHP must be 64-bit, the sodium extension is required, and max_input_vars must be at least 5000. Oracle is no longer supported from Moodle 5.0 onwards.

The 5.1 line also brought a directory restructure that trips up anyone working from older runbooks. Web-facing code moved into a public/ directory, which becomes your document root, while the live config.php sits one level above it at the installation root and the admin CLI scripts stay at admin/cli/, also above public/. The root index.php now does nothing but throw a rootdirpublic error, which is usually how you discover the document root is still pointing at the old place. IOMAD's own code lives at public/local/iomad on that branch.

The step that is easy to miss is what happens to third-party plugins. The upgrade does not move them. After the jump they are still sitting in their old locations above public/, silently doing nothing, and each one has to be moved by hand into the matching path inside public/ before it will work again. On a multi-tenant site that inventory is not optional: an authentication or enrolment plugin that quietly stops loading is a tenant that cannot log in.

# A fresh IOMAD 5.1 install. Clone the branch rather than unpacking a
# tarball, so future patching is a pull. Document root goes to public/.
git clone -b IOMAD_501_STABLE --single-branch \
    https://github.com/iomad/iomad.git /var/www/iomad
# Moving an existing IOMAD 5.0 site onto the 5.1 branch.
# Moodle 5.1 layout: config.php at the install root, above public/,
# and the CLI scripts still at admin/cli/.
cd /var/www/iomad
sudo -u www-data php admin/cli/maintenance.php --enable

git fetch origin
git checkout IOMAD_501_STABLE
git pull

# Now relocate every third-party plugin from its old path above public/
# into the same relative path inside public/, before the upgrade runs.
# For example: mv local/mycustomthing public/local/mycustomthing

sudo -u www-data php admin/cli/upgrade.php --non-interactive
sudo -u www-data php admin/cli/purge_caches.php

# Let cron drain the adhoc queue before you let tenants back in.
sudo -u www-data php admin/cli/cron.php

sudo -u www-data php admin/cli/maintenance.php --disable

Every command above runs unattended. The --non-interactive flag on upgrade.php is what stops the script waiting on a confirmation prompt, which matters if you are driving this from a deployment pipeline rather than a terminal you are watching.

One IOMAD-specific warning that no brochure carries: the upgrade gets dramatically slower as tenants and language packs multiply, and the reason is visible in the code. IOMAD stores email templates per company and per language, so the migration walks every company, times every template, times every installed language pack. It is queued as adhoc tasks rather than run inline, and while it is in flight the site sets a migration flag and the template management screens refuse to open at all. In practice that means patching your current branch to its latest point release first, letting cron run a full cycle and empty the adhoc queue, and only then changing branches. Rehearse the whole sequence on a clone and time it, because your maintenance window is shared by every tenant at once.

The operational traps nobody puts in the brochure

Logical multi tenancy trades isolation for efficiency. These are the places where you pay for it.

  • One plugin set. Every tenant runs the same plugins at the same versions. The moment one client needs a plagiarism plugin, a specific SCORM behaviour or an authentication method that another client's security team objects to, you are negotiating between clients instead of configuring a system.
  • One cron. A single cron process serves every tenant. A large tenant's overnight bulk enrolment, backup run or notification storm delays another tenant's completion emails. There is no per-tenant queue.
  • One upgrade cycle. Every tenant upgrades on the same night, whether or not their compliance audit is next week. Tenants cannot stay behind, and cannot go ahead.
  • One database and one moodledata. Backups are all-or-nothing. Restoring a single tenant to yesterday, because their admin deleted a category, means either restoring everyone or doing careful surgery on a subset of tables. Plan and rehearse that scenario before you need it.
  • Noisy neighbours. Slow queries, a runaway report, or one tenant scheduling a thousand-user course backup will be felt by everyone. Per-tenant rate limiting does not exist, so your controls are database tuning, PHP-FPM pool sizing and a hard rule about who may run heavy reports during working hours.
  • Site administrators are global. There is no tenant-limited superuser. Whoever holds site admin can see and change everything, and your contract with each client has to say so.
  • Backup and restore crosses boundaries. A course backup containing user data can be restored into the wrong company. Restrict who holds the restore capability and keep an eye on the log.
  • Logical separation is not data residency. This is the one no amount of configuration can answer. If a client's contract or regulator requires their personal data to be held separately, or in a specific country, one shared database does not satisfy it, however well the interface hides one tenant from another. Moodle's data privacy tooling handles subject access and erasure requests at site level, and its output is not naturally partitioned by tenant. Read the client's data processing agreement before you promise a shared platform.

When multi tenancy is the wrong answer

Separate instances cost more to run and are frequently the right decision anyway. Choose them when any of the following is true:

  • a client contract or regulator requires physically separate data, separate backups, or hosting in a specific jurisdiction;
  • tenants need different plugin sets, or different Moodle versions, and will not compromise;
  • tenants need different maintenance windows, or one has a change freeze the others do not;
  • one tenant is dramatically larger than the rest and will dominate the shared resources;
  • tenants may be sold, spun out or offboarded, and you will one day need to hand over a clean, complete site;
  • you have only a handful of tenants and no plan to add more, where the operational overhead is small and the isolation is free.

Multi tenancy earns its keep with many similar tenants, a shared course catalogue, central content authoring, and an economic model where adding a tenant must cost close to nothing.

A decision framework you can apply

Work through these in order and stop at the first firm answer.

  1. Does any tenant contractually require separate data storage or a specific data region? If yes, separate instances. Nothing below changes this.
  2. Will any tenant need a plugin, a version, or a maintenance window the others will not accept? If yes, separate instances.
  3. Do you need a tenant administrator who cannot see other tenants' users? If no, core Moodle with categories, cohorts and category-level roles is enough. Do not add a distribution you do not need.
  4. Do any users legitimately belong to more than one tenant? If yes, IOMAD, because Workplace ties each user to a single tenant.
  5. Do you sell seats, or resell training to other organisations? If yes, IOMAD, for the licence model and the parent-and-child reseller structure.
  6. Do you need programmes, certifications, dynamic rules and appraisals as first-class objects, and do you have budget for a partner contract? If yes, Moodle Workplace.
  7. Otherwise, IOMAD. Then check that the IOMAD branch for the Moodle version you need actually exists before you commit to a date.

Whichever way you land, write down the answers to the six isolation questions at the top of this page and get your client to sign them. Multi-tenant disputes trace back to assumptions about isolation that were never written down, and writing them down costs an afternoon now against a renegotiation later.

How I can help

I am Rohin Grover, and I have spent more than seven years installing, administering, migrating and repairing Moodle. For multi-tenant Moodle I work with IOMAD. Work I take on covers scoping the tenancy model, building the company and department structure, designing licence types that match how you actually sell, setting up per-tenant branding and hostnames, writing the custom SQL reports the shipped ones do not cover, and the server work underneath.

If you already have a Moodle site and are wondering whether to move it to IOMAD or split it into separate instances, that is a conversation worth having before the build, not after. You can see the full range of Moodle services, look at the projects I can describe, read a little more about how I work, or simply get in touch and describe the situation. If you would rather talk it through, book a 30-minute Moodle session or email me at rgrover@hooknot.com. I reply personally, usually within a working day — that is a habit, not a service level agreement. Smaller, self-contained pieces of work can also come through my Fiverr profile. I will tell you if multi tenancy is the wrong answer for you, because sorting that out afterwards is considerably more expensive than deciding it now.

Common questions

Does Moodle support multi tenancy out of the box?
No. Moodle LMS has no tenant concept in core. Its permission model uses contexts, roles and capabilities, and there is no context type that represents an organisation. A user context exists but its only possible parent is the system context, never a course category, so an account can never sit beneath a category. Every account lives in one site-wide user table and users remain visible to each other through user selectors, global search, messaging and site reports. Multi tenancy comes from IOMAD, from Moodle Workplace, or from running separate Moodle instances.
What is the difference between IOMAD and Moodle Workplace?
IOMAD is an open source Moodle distribution that adds companies, departments, delegated managers and a course licence model, and since IOMAD 4.3 it lets one user belong to several companies with a different role in each. Moodle Workplace is Moodle HQ's commercial product, available only through Moodle Certified Partners and Service Providers, and adds tenants, programmes, certifications and dynamic rules, with each user allocated to a single tenant.
What is the current IOMAD version and which Moodle version does it run?
As at 21 August 2026 the IOMAD_501_STABLE branch reports release 5.1.6 with build date 20260810, tracking the Moodle 5.1 line. IOMAD_500_STABLE sits at 5.0.9 and IOMAD_405_STABLE, which is IOMAD's own LTS, sits at 4.5.13. There is no IOMAD branch for Moodle 5.2, even though Moodle 5.2 was released on 20 April 2026. IOMAD's stated target is three months after each Moodle release; across 4.5, 5.0 and 5.1 the actual gap ran three to five months, and Moodle 5.2 has been waiting four months so far.
What are IOMAD's PHP and database requirements?
They match core Moodle for the equivalent version, and the floors differ by version rather than carrying across. IOMAD 5.1, tracking Moodle 5.1, needs 64-bit PHP 8.2.0 or later, with 8.3 and 8.4 also supported, the sodium extension, and max_input_vars set to at least 5000. Moodle 5.1's own minimum database versions are PostgreSQL 15, MySQL 8.4, MariaDB 10.11.0 or SQL Server 2017. Moodle 5.0's are PostgreSQL 14, MySQL 8.4, MariaDB 10.11.0 or SQL Server 2017. Oracle is not supported from Moodle 5.0 onwards.
Can I use Moodle course categories instead of a multi-tenant platform?
You can, if you only need a tidy catalogue and delegated course management. Categories will not hide users from each other. Data leaks through user selectors, global search, message recipient search, site-wide reports, calendar site events, tag pages and course backups containing user data. There is also no delegated site administrator in core, since a site administrator is always global and bypasses capability checks.
Does IOMAD multi tenancy satisfy GDPR or data residency requirements?
Not on its own. IOMAD gives logical separation, not physical separation. All tenants share one database, one moodledata directory and one backup set, so the data of every organisation sits together. If a client contract or regulator requires personal data to be stored separately or in a specific country, you need separate Moodle instances. Check the data processing agreement before committing to a shared platform.
How do IOMAD licences differ from normal Moodle enrolment?
IOMAD adds an enrolment plugin called enrol_license. A company manager is allocated a pool of seats and spends them on users. There are five licence types, not three: Standard, Reusable, Course educator, Course educator - reusable, and Blanket. The two educator types behave like Standard and Reusable but grant the teacher role instead of the student role. Blanket lets users allocate a seat to themselves by enrolling, and is the only type that cannot be a program licence or be flagged for instant access. The reusable types are the ones the form requires a validity length or cut-off date for; Standard needs a validity length of at least one day, while Course educator and Blanket escape duration validation altogether.
When does a user lose access to an IOMAD licensed course?
Not on the licence expiry date, which only marks the point after which no further seats can be allocated. The enrolment end date is fixed when the seat is allocated. For Standard, Course educator and Blanket licences it is the allocation date plus the validity length in days, or the cut-off date if one is set. Only for the two reusable types is it the licence expiry date. Separately, clear-on-expire fires on the cut-off date, not the expiry date, and wipes the course interactions of users who have not completed.
What changes in an IOMAD upgrade to the Moodle 5.1 line?
Web-facing code moves into a public/ directory, which becomes the document root, while config.php stays at the installation root above it and the admin CLI scripts stay at admin/cli/, also above public/. The root index.php only throws a rootdirpublic error. Third-party plugins are not moved by the upgrade: they remain in their old paths above public/ and must each be relocated by hand into the matching path inside public/ before they work again. On IOMAD 5.1 the distribution's own code sits at public/local/iomad.
What are the biggest operational risks of multi-tenant Moodle?
Everything is shared. One plugin set means tenants cannot have different plugins. One cron means a large tenant's overnight jobs delay another's notifications. One upgrade cycle means every tenant upgrades on the same night. One database means restoring a single tenant to yesterday requires surgery or a full restore. Site administrators are global, and heavy reports from one tenant slow the whole platform for everyone. Upgrades also slow down sharply as tenants and language packs multiply, because IOMAD stores email templates per company per language.

Talk it through with Rohin

Bring the messy version of the problem. A free 30-minute session is usually enough to tell you whether it is a small fix or a real project, and what it would take either way.

Rohin's Brain
Ask about Moodle, IOMAD multi-tenant LMS, plugins, reports, support, or whether your project is a fit for Hooknot Digital.
Schedule time with Rohin