HostEurope's backup system stores 14 days of snapshots that can be selectively restored via their KIS console. This guide covers navigating the restore interface, choosing backup dates, and handling the restored files once recovered.
Introduction
Our data center, Hosting Provider, creates daily backups of the entire file system of a VPS (Virtual Private Server), for a window of fourteen days. This also includes the binary database files. But these can be hard to restore on another system. Therefore, we install a Bash script for each virtual host (i.e. Moodle website) that creates a database dump and compresses it in one go.
The advantage of this is that a database dump, together with the moodledata directory and Moodle’ s codebase, allows you to restore the complete Moodle installation on any system which meets the minimum requirements for the Moodle version, not just the original server.
Please see the document Installing a New Moodle Website on a VPS for more details on how the backup script works exactly.
1. Create A Restore Directory
HostEurope has the option to restore a specific directory of the file system in the backup (up to 13 days) to a directory of your choice. For instance, you can choose to restore /home/mensen/ to /restore. Either in full, or as a tar.gz file.
This means that performing a restore will take up additional disk space. But it’s also extremely easy to execute.
To create a restore directory, login to the VPS using ssh and issue a command like:
Now pick the system on which you want to perform the restore, by clicking on the Konfigurieren button (the KIS website is originally in German, which pops up in odd places in the English version).
Finding The Right VPS
If you don’t know which system (VPS) has the Moodle installation you’re looking for, use the command nslookup with the domain name as its argument. For instance, if I want to know the IP address of academy.civic-rights.example:
Next to Datum des Backups (which means backup date), click on the select box and pick the right date. Please keep in mind that the backup is usually created around midnight CET.
Next, click on the Browse button next to Quell-Verzeichnis (source directory).
In the following screen, you’ll get to see the file system as it was at the time the backup was made. On your VPS systems, we always store the files for the Moodle installations in their own home directories. So click on home first:
Find the home directory of the Moodle installation you want to restore, which typically has a part of its domain name as the directory name.
Now click on the auswählen (choose) link next to the home directory’s name, mensen in this example:
3. Perform The Restore
Once you have selected the source of the backup, you’ll be presented with the following screen, on the KIS website:
Here’s a translation and clarification for the fields in the form:
Datum des Backups: backup date
Quell-Verzeichnis: source directory – this is the directory you have created on the VPS in the first step.
Benachrichtigungen an E-mail: e-mail notification – the system will send you an email once the restore is done.
Restore-Type: restore type – do you want to restore the target directory in its entirety (Direkt) or as a compressed file?
If you have filled out the form click Restore starten (Start restore).
The restore process will take some time (since it’s presumably copying the files from a slow backup medium).
Depending on the purpose of the restore (e.g. disaster recovery vs. restoring a single Moodle course based on a user’s request), you might want to check other SOPs on how to get the Moodle backup up and running. If the Moodle site needs to be installed on a new system, please see the document SOP Migrating a Moodle install to a new server and domain.
Once the restore is complete, don’t forget to inform your users of the results.
Considerations
Don’t look at Alpine Vista’s backup solution – it’s too complicated.
Instead, rely on a rotation scheme (e.g. as provided by /etc/logrotate.d) to remove old backups out of the window. Although this probably can’t be done on the storage server if it’s really only a file server you can only access through sftp. In that case we need something that cycles through the numbers 1 – 14. E.g.:
$backupday = $weekday;
if ($weeknumber % 2 == 0) {
// even week number
$backupday = $backupday + 7;
}
(The script should probably also remove yesterday’s backup before creating a new one to be copied to the remote system).
Also consider encrypting the backup file on the fly:
But these tend to be too complicated for our use case.
Incremental Backups
20230601
Large moodledata directories typically contain files that are added once but never changed afterward, such as video files. It doesn’t make much sense to store a complete copy of moodledata every day.
Instead, we need to create a full backup on day 1 (level 0, in tar terminology) and incrementally add to that until day 14 (or whatever the backup window is).
You can force ‘level 0’ backups either by removing the snapshot file before running tar, or by supplying the ‘–level=0’ option
It is common practice to name meta files with the extension .snar, think of this as an amalgamation of snapshot and tar.
It is also possible to create differential backups: these take the full backup as the base and then subsequently store all the mutations since the full backup was made. The difference is this: incremental backups store only the mutations since the last backup, whereas differential backups may store the same file multiple times (a file that was newly added since the full backup).
There is a user backupvhost on the La Forge server who has an ssh key pair. The public key is stored on the Strato server to enable authentication without the need of specifying a password (there is no passphrase).
Please note: there will always be one additional full backup to make sure the incremental parts from the previous period can also be restored.
Troubleshooting
20230602
Not working:
INM – destination folder on Hidrive was incorrectly named
Leerhuis – was no longer present in backupvhost cron job
Civic Rights Academy – first no cron job and then option --hostname="your-vps" was missing
20230619
Not working:
Kwil – backup script must run under php 5.6 since it’s going to include config.php, which in turn reads some of the Moodle files. This is Moodle 2.6, so php 5.6 is needed, not php 7.2
Alpine Vista – not clear why backup wasn’t created overnight. Test run works fine. I’ll have to keep an eye on it.
20230620
Alpine Vista may still not be working: there was a fresh full backup made at 1:51 am, but today is day 2 (Tuesday, not Monday) so we would have expected a day2 backup… Let’s keep an eye on it.
20230621
Alpine Vista is one day behind. Other than that, everything seems to be working fine. The cron job was set to work at 1:50 am, so maybe some date in the system is still at the previous day. I did another run, resulting in a ‘backup_day03’ file. I’ve also changed the cron job to run at 3:50 am.
Mailgun handles the infrastructure complexity of transactional email: reputation management, DKIM/SPF verification, and delivery tracking. This guide covers account setup, DNS configuration, SMTP integration with Moodle, and testing delivery from both the CLI and within Moodle.
Notes
Through this document we will use example-training.com as a test case, however any username or credentials shown will have been modified to be intentionally invalid for obvious reasons.
Mailgun is no longer free. Consider using something like Mailjet.
Pre-flight check.
We use a new mailgun account for each environment. Create a new account based on the current project and make sure to save and forward the credentials to the lead engineer. Best to use a custom email address for future transfer possibilities, e.g. example-training@your-domain.com.
Verify you have the right credentials, and that you are able to login to the following services:
Mailgun account.
Moodle site administration.
Domain DNS settings, these might live inside the Domain Registrar’s administration panel, or they might reside in an external service (Cloudflare, for example).
Specifically, it is common for services like Mailgun and DNS settings to be protected by 2 Factor Authentication. If that is the case, you will need to coordinate with the lead engineer to find a change window with the client so they are able to forward the second factor tokens to you.
If you use GMail and have enabled login codes through an app or SMS, this is a good example of working 2-Factor Authentication.
It goes without saying that disabling 2-Factor Authentication is something we must not do once we are logged in. If necessary, multiple additional change windows can be requested.
Mailgun
What is Mailgun
Mailgun is an online service that provides a set of APIs for sending, receiving, tracking and storing email. It can be used through an HTTP REST API, or as a traditional SMTP server.
Mailgun handles all the complex email stuff: the software services themselves, and all the email dark arts that surround them (SpamHaus, shared IPs, IP reputation, warming up IPs, the software side of DKIM and friends). We can stick to the explicit action we want to perform: sending emails, and let Mailgun worry about the rest.We are going to use Mailgun in SMTP mode, and we will add their SMTP server to Moodle so transactional emails are sent this way.
Create an account for Mailgun
Go to https://signup.mailgun.com/new/signup and create an account. Use your own email address and for “company” a combination of Solin with the company name of your customer.
You also have to provide credit card details otherwise you can only send emails to 5 authorized recipients only. In case the account details are shared with your users, make sure that you do not share credit card details from your colleagues!
Save the login details of this account in your password wallet and send it also to the lead engineer.
Adding the domain to Mailgun
After logging into the account, enter the “Domains” section (navigation pane Sending>Domains) and click on “Add New Domain”.
At the “Add your domain” page fill out the following details and click on Add Domain:
Domain name: Always use a subdomain to prevent any problems with the root domain. Use in this case mg.example-training.com
US/EU: Select the location of the sending server: For all companies residing in countries except from US, select EU. For the United States, select US.
Create DKIM Authority: leave this checked and select the longest DKIM key that is possible.
Domain or subdomain? What is the difference?
Explained in simple terms, these domains are going to develop a reputation over time depending on the rate of email delivery and the amount of times our users click on “Mark as Spam”.
Using the root domain is possible when it doesn’t have any MX records yet. However, to prevent any problems, we will always use a subdomain when we implement Mailgun.
Now that we have added the domain, we need to prove our ownership of it to Mailgun.
Proving to Mailgun that we own the domain.
Mailgun allows us to send email from any address and domain, as long as we can verify them. To do so, we will add specific TXT records through the DNS server of the domain. These records are generated and shown as soon as you click on the “Add Domain” button. You can also find these records by navigating to Sending>Domain Settings and tab DNS records. Make sure you select the correct domain on top of the page.
The DNS settings might live inside the domain registrar’s interface, or they might reside in an external service if this guide uses other services to manage the DNS (Cloudflare, for example).
Finding out who manages the DNS servers for the domain
Login to your hosting provider. This account might be applicable for several domains.
Select the domain of the Moodle website.
Navigate to the DNS section. Look for a section called Advanced domain settings or Manage DNS.
Add all records with the type, hostname and value that Mailgun has provided:
Add two separate sending TXT records, one for SPF and one for DKIM. These are required to send and receive email with Mailgun.
Add two separate receiving MX records. Use for priority 10.
Add a tracking CNAME record. The CNAME record is what enables Open and Click tracking. This is highly recommended to reap the full benefit of Mailgun and gain insights into your email performance.
Once you have added all records, you can click in Mailgun at the DNS records page on the button “Check DNS Records Now” to verify the records. If they have been verified, you see a green check sign in front of the record.
You will also receive an email from Mailgun once the DNS records have been verified.
If you are not familiar with DNS’ innerworkings, it might surprise you to read that this change might take up to 48 hours to propagate. Basically, TTL is the amount of time that records will be kept in cache throughout the whole DNS hierarchy; from the root servers to the servers that you use as DNS servers (such as 8.8.8.8).
GoDaddy “Helps” by Adding Your Full Domain in Name Part
Please note that GoDaddy doesn't clearly tell you that it automatically appends the domain name to the "Name" field — which leads to lots of confusion, especially when you're used to entering full FQDNs (Fully Qualified Domain Names) elsewhere.In short, when using GoDaddy’s interface, do not enter the full domain name, just the subdomain you’re editing.For example, if you have this DNS record:
then you should enter this as “mail”.
Integrating Mailgun with Moodle.
Now that we can send emails as whatever@example-training.com, it’s time to configure Moodle to send email through Mailgun.
Obtaining the credentials from Mailgun
Go to Sending>Domain settings and click on the SMTP credentials tab.
Here you cannot see directly the password, but you can reset the password to be able to copy it to your password wallet and use it in Moodle.
Beware: Anyone with the password and SMTP login will be able to send email as <anyone>@domain.com. Treat these credentials with utmost care.
Configuring Moodle
Go to the Moodle website and log in as an Administrator.
Click on Site Administration> Server> Outgoing mail configuration
Fill in the fields with the information obtained in the previous step, and click save.
If you want to have SMTP security enabled, please select the appropriate security mode.
For secure connections, port 465 is usually used with SSL, port 587 is usually used with TLS.
Done! Moodle is now configured to send email through Mailgun. Let’s test this setup, to confirm everything is working.
Send a test email.
From the shell.
Using swaks (available in the Debian/Ubuntu repositories) we are going to send a test email that will come from onno@example-training.com.
$ swaks --auth --server smtp.mailgun.org --au postmaster@example-training.com --ap <smtp password> --from onno@example-training.com --to email@yourinbox.com --h-Subject: "Hello" Test
=== Trying smtp.mailgun.org:25...
=== Connected to smtp.mailgun.org.
<- 220 ak47 ESMTP ready
-> EHLO MailDemo
<- 250-ak47
<- 250-AUTH PLAIN LOGIN
<- 250-SIZE 52428800
<- 250-8BITMIME
<- 250-ENHANCEDSTNorthwind UniversitySCODES
<- 250-SMTPUTF8
<- 250 STARTTLS
-> AUTH LOGIN
[..]
<- 235 2.0.0 OK
-> MAIL FROM:<onno@example-training.com>
<- 250 Sender address accepted
-> RCPT TO:<email@yourinbox.com>
<- 250 Recipient address accepted
-> DATA
<- 354 Continue
-> Date: Sun, 04 Feb 2018 20:53:12 +0100
-> To: email@yourinbox.com
-> From: onno@example-training.com
-> Subject: Hello
-> X-Mailer: swaks v20130209.0 jetmore.org/john/code/swaks/
->
-> This is a test mailing
->
-> .
<- 250 Great success
-> QUIT
<- 221 See you later. Yours truly, Mailgun
=== Connection closed with remote host.
From Moodle.
(Note: if the plugin is not installed, you can install it from https://moodle.org/plugins/local_mailtest. However, be mindful of your users’s environment! If you have any doubts about this step, please check with the lead engineer before proceeding.)
Go to Server/eMail Test and fill in the fields.
Moodle will show you exactly what happened, and if the email was sent successfully or not. Debug mode settings do not affect the rest of the website, and detailed output is always shown on error regardless of the debug settings specified in Moodle’s config.php.
Troubleshooting
SMTP ERROR: Failed to connect to server.
Either:
You have selected SSL in the SMTP settings, and it should be disabled since this guide uses plaintext auth.
The service provider is blocking port 25, 465 or 487.
To do: add DNS record to solve this: “solin.co is missing the required DNS TXT record to authorize the receipt of aggregate reports from coaguchek-elearning.nl. This is a requirement of the DMARC specification and you may be missing data as a result.”
Moving a Moodle install to a new server and domain requires careful pre-flight checks, code and database migration, DNS updates, and configuration adjustments. This guide covers the complete procedure with emphasis on avoiding downtime.
Pre-flight check.
Before proceeding, please double check that you have proper answers to the following questions at hand. Triple-check with the lead engineer if you have any doubts because a mistake throughout this process will result in publicly visible downtime at the very least.
Is the Moodle website using recaptcha? Unless the Recaptcha settings are modified the DESTINATION website will not have functioning captchas thus blocking users from registering and/or logging in.
Do you have root access to both servers?
Is the SOURCE website in maintenance mode? What window of time do you have for this process? If everything goes as expected you should be done in an hour or two, but if you encounter any hiccups it will take longer.
Do you have admin credentials for Moodle? What about root credentials for the database?
Does the DESTINATION server have the same post_max_size and upload_max_filesize php settings (usually in php.ini) as the SOURCE server? Otherwise users may run into upload limits of 2 MB.
Does the DESTINATION server have the same locales installed? Check this with locale -a (under Ubuntu Linux).
Are there any logging processes on the DESTINATION server that tend to eat up a lot of disk space, such as log_bin on MySQL? If so, turn them off if possible.
Does the DESTINATION server have enough free disk space? Check with df -h
apachectl configtest to test whether the config files are properly written.
service apache2 reload to process changes to the configuration.
The web server software: Apache (version 2.2 or 2.4, depending on the host OS version). This is important because some of the syntax has changed between those releases, but we will address that later in this document.
The database: MySQL.
The Moodle website folders, located on /home/<website name>/public_html. As you can guess, each website gets their own user. This will be relevant shortly.
The moodledata folder, usually located on /home/<website name/moodledata, contains files that are uploaded or created by the Moodle interface. The location of this folder will be specified in /home/<website name>/public_html/config.php.
First of all, we need to create the user and folder structure. Let’s get the uid of the SOURCE user:
As we can see, the uid is 1025. We will create a user with this same uid on the new system.
After creating it we are going to switch users, create the folder structure and assign the proper permissions so we can transfer files later.
If the UID were to be already in use, don’t worry. Create the user with a different UID and remember this when we are transferring the website files over to the destination server.
To create a backup of the database, first we need to know which database Moodle is using. This information can be obtained from the config.php file, like so:
And to back it up we use the following command:
This folder now contains the Moodle website, the moodledata folder and the database backup we just made. To transfer it to the new server, we are going to use Rsync and ask rsync to preserve all extra attributes (owner, permissions and so forth) with the “-a” switch.
“-a” will take care of assigning the proper permissions to the folder, so the files are accessible for Apache too.
This will take a while, and Rsync will keep us informed of the progress.
If earlier we had to create the user with a different UID, we can manually fix permissions in the DESTINATION server at this moment with the following set of commands.
Now that the website folder is in place, we need to add the site to Apache’s configuration. To do that, we will copy the original website from SOURCE:/etc/apache2/sites-available/<website>.conf into DESTINATION:/etc/apache2/sites-available/<website.conf>. We need to tweak some of the configuration parameters on DESTINATION, namely:
ServerName must match the new domain, if it changes. Same with ServerAlias (i.e. if the original website contained any ServerAlias instructions, they will have been copied over through the .conf file).
ErrorLog and CustomLog must have their paths modified to include the new domain name, if relevant.
To check for this, run apache2 -v in both the source and the destination. This will present a problem, as Apache made significant changes to the configuration directives between those two versions. You might encounter this scenario when the source server is Debian and the destination server is Ubuntu, due to their different packaging policies.
In this case, we are only concerned about one change: The way directory permissions are specified. We need to turn “allow from all” into “Require all granted”. The following is a snippet of an actual configuration file, before and after the change-
Before:
<Directory /home/12mprove/public_html>
Options -Indexes +IncludesNOEXEC +SymLinksIfOwnerMatch
allow from all
AllowOverride All Options=ExecCGI,Includes,IncludesNOEXEC,Indexes,MultiViews,SymLinksIfOwnerMatch
</Directory>
After:
<Directory /home/website/public_html>
Options -Indexes +IncludesNOEXEC +SymLinksIfOwnerMatch
Require all granted
AllowOverride All Options=ExecCGI,Includes,IncludesNOEXEC,Indexes,MultiViews,SymLinksIfOwnerMatch
</Directory>
If you do not check for this properly, Apache would error out when loading the new website configuration leading to downtime for every website on that server.
Moodle has a scheduled task that runs every minute, and takes care of running a variety of scheduled tasks at regular intervals (like sending mail, updating Moodle reports, RSS feeds, activity completions or posting forum messages).This scheduled task needs to be added to Apache’s crontab. To do so, we run the following command:
DESTINATION --: crontab -u www-data -e
And this is the line we need to add (careful, it is just one line with no breaks):
(It’s not necessary to use cgi-fcgi in order to run a specific php version on the command line.)
Running the “replace” script to update references to the domain name.
If we have changed the domain name, we need to run this tool so Moodle can update it’s internal references to the new domain. To do so, we run the following command on the DESTINATION server.
Updating the domain name in Moodle’s configuration
If the domain name has changed, you need to update /home/<website>/public_html/config.php so wwwroot points to the new domain name.
$CFG->wwwroot = 'https://<URL>';
In addition, you will also need to clear the caches (‘purge all caches’), especially for Totara, which caches the domain name for menu items like the gear icon (or cog wheel, in British English).
Adjust Quota
If you’re using something like Virtualmin to create the vhost, adjust the quota (usually 1GB or 2GB by default) – otherwise you will soon run into the limit.
Solving Database Connection Errors
If you run into any database connection problems, they may be due to using a recent MySQL version and an older Moodle version. Here are the known issues we have run into:
Authentication issue when using ‘traditional’ native MySQL native password
Using Moodle 2.x with MySQL 8
Authentication Issue
Add default-authentication-plugin=mysql_native_password to the [mysqld] section of /etc/mysql/mysql.conf.d.
Change the authentication method for the database user:
ALTER USER example-db@localhost IDENTIFIED WITH mysql_native_password BY 'thepassword';
Using Moodle 2.x with MySQL 8
If you are using a very old Moodle version, such as 2.x, together with a newer version of MySQL, e.g. version 8, then you will need to make some changes to the source code. You will also need to address the authentication mentioned above.
Change the code in lib/dml/mysqli_native_moodle_database.php, line 523:
$sql = "SELECT column_name as `column_name`, data_type as `data_type`, character_maximum_length as `character_maximum_length`, numeric_precision as `numeric_precision`, numeric_scale as `numeric_scale`, is_nullable as `is_nullable`, column_type as `column_type`, column_default as `column_default`, column_key as `column_key`, extra as `extra`
FROM information_schema.columns
To summarize: add aliases for every single column name.
It may also be necessary to make an additional change if you get the error message Unknown system variable 'storage_engine'.
Fix this issue by editing lib/dml/mysqli_native_moodle_database.php. Replace:
@@storage_engine
with
@@default_storage_engine
(In two places, in this case.)
There will still be a notice:
Notice: Undefined index: engine in /home/example-client/public_html/lib/dml/mysqli_native_moodle_database.php on line 173
But that will be ignored if you turn off full debugging mode.
Install a database backup script
The following is superseded by the section Configure backupvhost.php Script for Backups from “Installing a New Moodle Website on a VPS”.
Our data center (Hosting Provider) creates daily backups of the entire file system, for a window of fourteen days. This also includes the binary database files. But these can be hard to restore on another system. Therefore, we install a Bash script that creates a database dump and compresses it in one go. Here’s an example script:
This script creates a zipped database dump which will be included in the data center’s daily backup. Together with the public_html and the moodledata directories, this file can be used to completely restore a working Moodle installation on another system, should the need arise.
“The –single-transaction flag will start a transaction before running. Rather than lock the entire database, this will let mysqldump read the database in the current state at the time of the transaction, making for a consistent data dump.”
Create a db-backup directory in the home directory.
Use vim or another text editor to create the [customer]-backup.sh script inside the db-backup directory.
Set the owner to your users: chown -R [customer]:[customer] /home/[customer]/db-backup
Set the permissions to 770: chmod -R 770 db-backup
Give the database user (example-db in this example above) the proper permissions to user mysqldump: PROCESS (‘Manage processes’ in Webmin)
Check that the script is actually working properly: su (change user) to [customer] then run the script manually and check the contents of the zip file.
If you run into a disk quota issue: set the quota to unlimited for both the user and the group.
Create a cron job to execute the script on a daily basis, e.g. one hour before midnight (at that moment the data center starts their own backups). Use Webmin to create the cron job, but you can also use crontab:
@daily /home/[customer]/db-backup/[customer]-backup.sh #Creates a daily backup of the [customer] database (as a gz file)
The next day, check that the script has run properly (the timestamp should be shortly before midnight).
Here’s the Bash script without a specific customer’s name:
Typically, we have a Let’s Encrypt certificate installed for the domain, as well as the ‘www.’ version of the domain.
After migrating the site, run the following command to make sure the certificates are still in place and get renewed automatically:
certbot -d [thedomainname] -d www.[thedomainname]
(leave out the square brackets while typing in the command)
If there is no www.[thedomainname] version, skip that part.
You can test the results by visiting the website and checking that the certificate’s validity date starts today.
If you have certbot running on the old server, make sure you disable it for the old domain (unless you still need it).
Enabling the new website and testing that everything works.
To enable the new website, we need to run the following command on the DESTINATION server:
a2ensite <website>
service apache2 reload
You should be able to visit the domain now, and see the Moodle “maintenance” page. Log in with your admin credentials at https://websitedomain.com/login/ and browse through the courses. You should be able to access both courses and files, if the server is working correctly.
Test Email
Don’t forget to test the email delivery. To prevent the system from sending out emails to everyone before you’re ready, use this directive in config.php:
Now use the email test plugin to see if the system is still sending out email properly.
Disable Maintenance Mode
After you have checked that everything is working fine, use Administration > Site administration > Server > Maintenance mode to take the website out of maintenance mode.
Possible issues.
The embedded videos don’t work.
We use Vimeo as a video hosting service, and it is configured to only allow embedding from certain domains. If we are migrating to a new domain, we will need to add it to Vimeo’s settings so we can access the material.
Check with the lead engineer for more details.
The upload limits are too low.
Moodle allows users to upload files in a number of places. The upload limit (size of uploaded file) may be too low if the php.ini settings for the server have not been changed. You can check this under /admin/phpinfo.php – or Site administration > Server > PHP info – where you need to look for the post_max_size and upload_max_filesize settings. These should be 1000M. If they are not, change the php.ini file (you can't do that in Moodle, this is a server administration task).
Mailgun is no longer working
For Horizon, we had to switch to port 465 and ssl to get it working again, after migrating their websites to your server.
If you want to make sure that the system can send out email at all, use your own email provider (e.g. Gmail through smtp.gmail.com:465) to test.
Mailgun is no longer free. Consider using something like Mailjet.
MySQL Database Import Fails
If you can’t import a large DB dump because you encounter the following error message:
ERROR 2013 (HY000) at line 21770: Lost connection to MySQL server during query
Then check if you also have a memory error message somewhere. I had to edit /etc/mysql/mysql.conf.d/mysqld.cnf and change innodb_buffer_pool_size from 8589934592 to 4294967296:
Once we have taken the new Moodle site out of maintenance, we are going to redirect visitors from the old domain to the new one. To do this, we edit /etc/apache2/sites-available/<website>.conf and add a new Redirect directive.
RedirectMatch ^/(.*)$ https://<new domain>/$1
Streaming a tarred moodledata directory from SOURCE to DESTINATION
Overview
For very large moodledata directories (tens or hundreds of gigabytes), traditional copying methods like scp or rsync can be too slow or can run into memory/disk limits. A more robust approach is to stream a tar archive over SSH, unpacking it immediately on the DESTINATION server without creating a temporary tar file. This method:
avoids creating a giant .tar file on disk
avoids double I/O (read + write on both ends)
is resilient and efficient
can be monitored with pv (pipe viewer)
works across servers as long as firewall rules permit SSH traffic
This section documents the complete process.
Prerequisites
Before streaming moodledata:
You must be logged in on the SOURCE server.
The DESTINATION server must allow SSH connections from the SOURCE server’s IP address. (If the firewall blocks it, the stream will freeze at the SSH step.)
SSH public-key authentication must be working between SOURCE and DESTINATION.
You should use screen so that the transfer continues even if your session disconnects.
Understanding the Components
1. tar -C /path -cf – .
This command creates a tar archive on STDOUT instead of writing to a file.
-C /path changes into the moodledata directory before tarring. This ensures we put only the contents into the stream, not the folder itself.
-c means create archive
-f - means write the archive to stdout
. means "archive everything in this directory"
Example:
tar -C /home/webroot/leren/moodledata -cf - .
This produces a byte stream representing the entire moodledata contents.
2. pv (Pipe Viewer)
pv sits between the tar creation and the ssh transmission:
tar ... | pv | ssh ...
It shows:
total bytes streamed
current throughput
ETA
If pv is not installed, you can remove it — the stream will still work, just without progress feedback.
3. SSH agent forwarding (ssh -A)
If you connect to SOURCE from your laptop and then connect from SOURCE DESTINATION, you often need your local SSH keys available on the SOURCE machine. Using ssh -A forwards your SSH agent:
ssh -A source.example.com
This makes your laptop’s SSH key available transparently on SOURCE, so SOURCE can authenticate to DESTINATION without storing private keys on SOURCE.
If authentication fails or the agent doesn’t forward, DESTINATION will reject the connection.
4. Unpacking on DESTINATION (tar -C /path -xf -)
On DESTINATION, we unpack the incoming stream immediately:
-C /path = change into the target moodledata directory
Testing that Moodle plugins correctly post data to remote endpoints requires a local test server to capture requests. This guide shows how to set up a simple PHP endpoint, configure the plugin, and inspect the actual HTTP payloads being sent.
SOP: Testing Moodle Plugins That Transmit Data to a Remote Endpoint
Purpose
To verify that a Moodle plugin correctly sends data (e.g., grades) to a remote web service (typically via HTTP POST) when specific events are triggered, such as grading an assignment.
Prerequisites
Moodle instance with the plugin installed and enabled
Ensure your curl call includes: Example:CURLOPT_RETURNTRANSFER => true
Success Criteria
Endpoint receives a POST request
Data includes expected keys and values
No errors are logged in Moodle or PHP logs
Cleanup
Press Ctrl+C to stop the test server
Revert endpoint configuration if needed
Building AMD modules in Moodle plugins using esbuild avoids Grunt's complexity while maintaining full compatibility. This guide covers setup, configuration, and the complete build-to-production workflow.
Standard Operating Procedure: Using esbuild to Build AMD Modules in a Moodle Plugin
Purpose
To compile modern JavaScript source files in amd/src/ into AMD-compatible .min.js files in amd/build/ using esbuild, in a way that's fully Moodle-compliant but avoids Grunt.
Directory Structure
Your Moodle plugin should contain:
mod/yourplugin/
amd/
src/
editor.js
runtime.js
build/ (created automatically by esbuild)
Each source file should begin with a valid AMD define() block:
Moodle will load your AMD modules from amd/build/*.min.js
Setting up a new Moodle instance on a VPS requires careful configuration of DNS, virtual hosts, and web server settings. This guide covers the complete workflow from domain setup through initial Moodle installation.
1. Create a DNS record
There are two options here (described in more detail below):
The customer has access to their own DNS server or provides you with access data: link their preferred (sub)domain to the ip address of your VPS server.
Create a DNS Record for a Subdomain under Our Domain: this means that you have to adjust the DNS settings for one of your own domains, to link a subdomain to the VPS.
Link the Customer’s Preferred (sub)domain to the IP Address of your VPS Server
If your users already has a (sub)domain name for their new Moodle installation, you have to go into their own DNS server and create the necessary changes. Since there’s no telling what DNS server or provider they’re using, the exact steps vary by DNS provider.
The outcome should usually be an A record that links their (sub)domain to the ip address of your server:
lms.westwood.com. IN A 123.456.789.101
In short: their (sub)domain should be link to our ip address.
Create a DNS Record for a Subdomain under Our Domain
2. Create a VirtualHost
Log in to Virtualmin on the your-vps server: https://198.51.100.42:10000/?virtualmin.
Then select “Create Virtual Server”:
Input the details for the new virtualhost:
Please note:
Use the subdomain as the Administration username (use the “Custom username” feature). E.g. if the domain is westwood.example-lms.com, then ‘westwood’ becomes the Administration username.
Create a strong, random password and store it in a very secure password wallet (e.g. KeePassX).
If you use Virtualmin, be sure to adapt the quota (usually 1GB by default) – otherwise larger Moodle installations will run into the limit. (MySQL server will even crash entirely if you try to import data beyond the quota limit. I once had to manually delete the database directory from /var/lib/mysql/ to get it working again, because the MySQL server wouldn’t even start in recovery mode.) To double check whether a quota limit applies:
root@blueyondercoaching:~# quota -u example-db
Disk quotas for user example-db (uid 1020): none
If the database creation fails for some reason, simply use: create database mydatabasename character set utf8mb4 collate utf8mb4_unicode_ci;
3. Set the Database Password
After completing the previous step, click “Create Server” (see previous screenshot).
If the virtual server has been set up, click on “Webmin” in the top left corner of the window:
In the blue panel on the left, select “Servers” > “MySQL Database Server. Then scroll down and click “User Permissions” (Under “Global Options”)::
Then click on the database user with the name of the new subdomain (i.e. the Administration username), Westwood in this example:
Now type in a strong, random password and store that password in your password wallet (e.g. KeePassX):
Click “Save” to complete this step.
Solving Database Connection Errors
If you run into any database connection problems, they may be due to using a recent MySQL version and an older Moodle version. Here are the known issues we have run into:
Authentication issue when using ‘traditional’ native MySQL native password
Using Moodle 2.x with MySQL 8
Authentication Issue
Add default-authentication-plugin=mysql_native_password to the [mysqld] section of /etc/mysql/mysql.conf.d.
Change the authentication method for the database user:
ALTER USER example-db@localhost IDENTIFIED WITH mysql_native_password BY 'thepassword';
Using Moodle 2.x with MySQL 8
If you are using a very old Moodle version, such as 2.x, together with a newer version of MySQL, e.g. version 8, then you will need to make some changes to the source code. You will also need to address the authentication mentioned above.
Change the code in lib/dml/mysqli_native_moodle_database.php, line 523:
$sql = "SELECT column_name as `column_name`, data_type as `data_type`, character_maximum_length as `character_maximum_length`, numeric_precision as `numeric_precision`, numeric_scale as `numeric_scale`, is_nullable as `is_nullable`, column_type as `column_type`, column_default as `column_default`, column_key as `column_key`, extra as `extra`
FROM information_schema.columns
To summarize: add aliases for every single column name.
It may also be necessary to make an additional change if you get the error message Unknown system variable 'storage_engine'.
Fix this issue by editing lib/dml/mysqli_native_moodle_database.php. Replace:
@@storage_engine
with
@@default_storage_engine
(In two places, in this case.)
There will still be a notice:
Notice: Undefined index: engine in /home/example-client/public_html/lib/dml/mysqli_native_moodle_database.php on line 173
But that will be ignored if you turn off full debugging mode.
For Totara: Grant Permissions to Create Tables & Indexes
If you’re installing Totara, please make sure the database user has adequate permissions to create caching tables and indexes. (For the report builder.)
Using The MySQL Client To Create Database And DB User
Instead of using Webmin, you can also use the commandline tool mysql:
mysql -u root -p
CREATE DATABASE moodle_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'moodle_user'@'localhost' IDENTIFIED BY 'your_strong_password';
GRANT ALL PRIVILEGES ON moodle_db.* TO 'moodle_user'@'localhost';
FLUSH PRIVILEGES;
4. Create a Git Repository
Before we install or deploy the code for the new Moodle website, we need to set up a Git repository. In this guide this guide uses Bitbucket to store the remote repository, but any other system is fine too (e.g. Gitlab or Github).
New Bitbucket Repository
On Bitbucket.com, use the ‘Create’ button to add a new repository:
Bitbucket will also ask you to create a Project. Lately, I’ve made it a habit to use your users’s name for the project, and your users’s name for their platform (Moodle or Totara) as the name for the repository.
Clone Repo and Get the Moodle Code
On your local machine, clone the repo. Then add the Moodle repo as the upstream repo:
Get a specific branch. Here, we’re pulling the branch containing Moodle 4.1 because it is the current LTS version (the upstream branch name can be found in the upstream repo):
git pull upstream MOODLE_401_STABLE
Create a local branch based on the upstream branch:
Then copy all the files from moodle41 to the current branch:
git checkout moodle41 .
Yes, this looks like we’re switching to another branch altogether, but notice the dot. This apparently means: get the files from branch moodle41 and put them in the currently checked out branch (which is the master branch in this example).
After you’re done, commit and push the changes to remote.
Please note that we can’t add the Totara source code in this way because Totara is not really open source: there is no publicly available Git repository for it.
Caveat: 3rd Party Plugins
Please note: the method described above also works if you allow users to install their plugins (through the standard Moodle interface). But you wouldn’t have the complete code base in your git repo of course.
However, this still needs to be executed on your local machine since it requires a commit afterwards. From the link above:
$ cd /path/to/your/moodle
$ git submodule foreach git pull
The command git submodule foreach [another command] walks through all submodule repositiories and executes what is specified by [another command]. In this case it is git pull. Therefore, the module mylittleextension was updated and the main repository isn't clean anymore until changes are committed:
$ git commit -a -m "Plugin updates"
Since the git users on the live servers do not have write access to the repository, we can’t do the above on the live server itself. (We don’t want to give them write access because their private ssh keys are on the web server, accessible for everyone on that server).
Add Public Key of Vhost’s Unix User to Bitbucket
We are going to use Git to deploy the new code (and also later, e.g. for minor and major Moodle upgrades). Login on the web server and:
su to the unix user for the website
Execute the following command: ssh-keygen
Skip the passphrase – we don’t want to use a passphrase (but only in this case)
Put the public key in Bitbucket repo through “Repository settings > Access keys”
Provide a clear label, e.g. “mbcc user on your-vps”
Clone the repository: ‘git clone’. Make sure the directory containing the git repository is the webroot (usually called public_html or htdocs)
Set the permissions, e.g.: chmod -R 755 public_html
Secure the .git folder by doing a chmod -R 700 .git (we’ve had complaints from security officers that the .git folder was public, even though what’s in it is typically public anyway since it’s open source software).
5. Install & Configure Moodle
Make sure the (sub)domain can be reached, e.g.:
You will get an error message ‘Forbidden’, but that’s okay since there’s no actual website yet. If something went wrong, you’ll see a different message, e.g.: “This site can’t be reached”.
Login to the VPS, e.g.:
engineer@opsbox-computer14:~$ ssh sysadmin@198.51.100.42
Welcome to Ubuntu 16.04.3 LTS (GNU/Linux 4.4.0-042stab123.9 x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
Last login: Sun Aug 27 08:34:07 2017 from 83.162.44.210
sysadmin@yourserver:~$
Move to your users’s home directory:
sysadmin@yourserver:~$ cd /home/westwood
sysadmin@yourserver:/home/westwood$
If you have deployed the code through Git, which is the preferred way, you can skip ahead to step 11: Make a copy of public_html/config-dist.php. Otherwise, go to https://download.moodle.org/releases/latest/ and click on the download button for the package that is built weekly:
(Cancel the download to your local machine.) Copy the download link:
Edit the contents of public_html/config.php with your text editor, e.g. vim:
$CFG->dbtype = 'mysqli'; // 'pgsql', 'mariadb', 'mysqli', 'mssql', 'sqlsrv' or
$CFG->dblibrary = 'native'; // 'native' only at the moment
$CFG->dbhost = 'localhost'; // eg 'localhost' or 'db.isp.com' or IP
$CFG->dbname = 'westwood'; // database name, eg moodle
$CFG->dbuser = 'westwood'; // your database username
$CFG->dbpass = 'the_secret_password'; // your database password
$CFG->prefix = 'mdl_'; // prefix to use for all table names
$CFG->dboptions = array(
'dbpersist' => false, // should persistent database connections be
'dbsocket' => false, // should connection via UNIX socket be used?
'dbport' => '', // the TCP port number to use when connecting
'dbhandlesoptions' => false,// On PostgreSQL poolers like pgbouncer don't
'dbcollation' => 'utf8mb4_unicode_ci', // MySQL has partial and full UTF-8
);
$CFG->wwwroot = 'http://westwood.example-lms.com';
$CFG->dataroot = '/home/westwood/moodledata';
The lines that need changing are highlighted. This is an excerpt from a config.php file, not a complete config.php file.. Do not replace the code of your config.php file with this code, but make the changes in the file itself (new code may have been added by the Moodle core team).
Secure the Moodle Installation
Make sure the directories have the right permissions and ownership (for the username, we’re following the ‘westwood’ example – change this to match the client’s username):
sudo chown -R westwood:www-data public_html
sudo chmod -R 0755 public_html
sudo chown -R westwood:www-data moodledata
sudo chmod -R 0750 moodledata
Also consider closing public access to the .git folder if it’s in your webroot. Even though most of the code is open source, some clients are still paranoid about it.
Go to the website to actually install Moodle. It should redirect you to /admin/index.php:
Just follow the installation wizard from here on (ignore any notifications about https).
In the installation form (see screenshot below):
The username should be either siteadmin, yoursiteadmin or example-siteadmin (again, whatever is appropriate).
Use admin@example-lms.com, admin@your-domain.com or admin@example.com as the admin’s email address (whatever is appropriate, e.g. if the site is Frooxl’s then it should be admin@example-lms.com).
Set Email display to “Hide my email address from everyone”.
For the Front page settings, use the subdomain name (or your users’s chosen name for the LMS) as the “Full site name” and the “Short name for site”. Set the “Default timezone” to whatever timezone the site is in.
Create an Admin Account
In the panel (menu) on the left, click on “Site adminstration”, then select the “Users” tab. Click the “Add a new user” link.
Fill out the minimally required fields (see screenshot below):
Username: your users’s company name, followed by ‘admin’. E.g.: westwoodadmin.
Generate a strong, random password and store it securely (e.g. in KeePassX), together with the url and the username. Also fill in the password in the current Moodle form.
Use ‘Admin’ as the first name and the company name as the last name (e.g. “Westwood”).
If you’ve got a role based email address, use that, preferably the admin address – e.g. admin@westwood.com.
Add the new customer’s account as a site administrator. Select “Site administration” > “Users” > “Site administrators” (see screenshot below):
Select your users’s user and click “Add”:
Afterwards, the form should look like this:
(The customer’s account should appear under “Current site administrators”).
Add Outgoing Mail Configuration
Go to admin/settings.php?section=outgoingmailconfig and either:
Add your users’s SMTP settings
Or create a Mailgun account for your users (see Mailgun and Moodle Integration).
Change Upload Limits
By default, Moodle's upload limits are just one or two measly MBs. You need to change this upload limit to 1000 MB in a number of places:
On the server: in the php.ini file, you need to set post_max_size and upload_max_filesize to 1000M (this is typically already done, you can check this under /admin/phpinfo.php – or Site administration > Server > PHP info)
Under Site security settings (Site administration > Site security settings), where you need to select 1000 MB for "Maximum uploaded file size".
And finally for individual modules, by default at least the assignment module (mod assign), under /admin/settings.php?section=assignsubmission_file (go to Site administration > Plugins, select "File submissions"). This setting, again, needs to be 1000 MB.
Turn Off Update Notifications
Out of the box, Moodle will send out update notifications by email whenever a new version is available. This is alarming for some customers because they tend to think the software is outdated. Turn this feature off:
Site administration > Server > Update notifications
Disable the checkbox “Automatically check for available updates”.
Enable Apache X-Sendfile for large file delivery
Large Moodle sites usually serve videos, H5P files, SCORM packages, PDFs, and large images. By default these files are streamed through PHP-FPM, which reduces performance and limits concurrency. Enabling Apache’s X-Sendfile offloads file delivery to Apache, which is significantly faster.
Step 1: Install and enable mod_xsendfile
apt update
apt install libapache2-mod-xsendfile
a2enmod xsendfile
service apache2 restart
Step 2: Add X-Sendfile directives to the VirtualHost
Inside the VirtualHost configuration:
<IfModule mod_xsendfile.c>
XSendFile on
XSendFilePath /home/USERNAME/moodledata
XSendFilePath /home/USERNAME/public_html
</IfModule>
If the file downloads correctly, Apache X-Sendfile works.
Aside: If you later observe slow requests
If the site suffers performance issues when users view large files, verify X-Sendfile is actually in use by running the Moodle-level probe described in the troubleshooting guide.
When X-Sendfile is active, PHP-FPM usage for large files drops dramatically and site responsiveness improves.
6. Create a Cron Job
On your users’s Moodle website, select: “Site administration” > “Site administration” (tab) > “Site policies”:
On the next page, scroll down and:
Disable “Cron execution via command line only”.
Set a strong, random password for “Cron password for remote access”. But do not use any special characters in the cron password, because this password is used in a querystring parameter (i.e. in a url). Store the password in secure location (e.g. KeePassX).
Now test the url in your browser. In this example, the url is: http://westwood.example-lms.com/admin/cron.php?password=your_cron_password
Go to Webmin and select “System” > “Scheduled Cron Jobs”. Click on one of the existing cron jobs, e.g. the one for example.com:
In the “Command” field, replace the original url (example.com… in this example) with the new customer’s url. Then click on “Create” at the bottom of the screen.
7. Configure backupvhost.php Script for Backups
A simple solution for creating differential backups. Here is the script we are using:
On each server, there is a user backupvhost who has an ssh key pair. The public key is stored on the Strato server to enable authentication without the need of specifying a password (there is no passphrase).
Please note: there will always be one additional full backup to make sure the incremental parts from the previous period can also be restored.
Installation of Backup Script on Server
To get the backup script working on a new server, follow the steps below (see the Git repo for the most recent version of these instructions). If you already have the backup script installed on the server, skip to the next section.
Create a 'backupvhost' unix user on the web server.
Add the 'backupvhost' user to the user group of the vhost's unix user (add vhost unix user to backupvhost’s secondary group in Webmin)
Generate a public / private ssh key pair for the backupvhost user.
Add the public key to the git repo for the backup script.
Install this script in a 'scripts' folder in the home directory of the backupvhost user using git
Create a backup directory in the home directory of the backupvhost user. You should have the following files and folders now:
Add the public key of the backupvhost user to the destination server.
Initiate a very small scp copy to the destination server and answer yes on the following question:The authenticity of host 'scp.hidrive.strato.com (85.214.3.70)' can't be established. ED25519 key fingerprint is SHA256:RZwN5ypH59Jssd3qVhuAnCn3fEtveYCrF24igD8QDBs. This key is not known by any other names Are you sure you want to continue connecting (yes/no/[fingerprint])?
Add a folder {source hostname}_server_backups on the destination server.
On the source server, grant the right permissions to the script:
chmod 770 scripts/backupvhost.php
chown backupvhost scripts/backupvhost.php
Create a folder/users/solin/{source hostname}_server_backups/{databasename}/ on the destination server for each vhost that you want to backup.
Test the script manually. Remove the results afterwards from the destination server. Calling the script should look something like this:
/usr/bin/php7.2 /home/backupvhost/scripts/backupvhost.php --config="/home/{unix user for vhost}/public_html/config.php" --hostname="{source hostname}" >/dev/null
Run this script as user 'backupvhost', typically as a daily cron job (choose a time around midnight, but pay attention to other backups jobs so as not to tax the system too much at any given time).
Check the backup results the following day on the destination server.
Creating a Cron Job to Call the Backup Script for a New Moodle Installation
To get the backup script working for a new Moodle installation, follow these steps:
Create a folder/users/solin/{source hostname}_server_backups/{databasename}/ on the destination server (currently my.hidrive.com) for the vhost that you want to backup. (source hostname is typically yyour server’s name).
Add vhost unix user to backupvhost’s secondary group in Webmin.
Do the same for www-data and set the correct permissions e.g.:usermod -a -G www-data backupvhostchmod -R g+rX /home/example-db/moodledata
Test the script manually (through a screen session). Remove the results from the destination server afterwards. Calling the script should look something like this:
su backupvhost;
/usr/bin/php7.2 /home/backupvhost/scripts/backupvhost.php --config="/home/{unix user for vhost}/public_html/config.php" --hostname="{source hostname}" >/dev/null
Run this script as user 'backupvhost', typically as a daily cron job (choose a time around midnight, but pay attention to other backups jobs so as not to tax the system too much at the same time).
Check the backup results the following day on the destination server.
Previously: Install a database backup script [Out of Date]
Our data center (Hosting Provider) used to create daily backups of the entire file system, for a window of fourteen days. They don’t do that anymore, but here is how we used the daily backups in the past.
The daily backups also include the binary database files. But these can be hard to restore on another system. Therefore, we install a Bash script that creates a database dump and compresses it in one go. Here’s an example script:
This script creates a zipped database dump which will be included in the data center’s daily backup. Together with the public_html and the moodledata directories, this file can be used to completely restore a working Moodle installation on another system, should the need arise. Please see the document Restoring a Moodle Backup from HostEurope for more details on how to restore a backup.
Steps to get the Bash script working
Create a db-backup directory in the home directory.
Use vim or another text editor to create the [customer]-backup.sh script inside the db-backup directory.
Set the owner to your users: chown -R [customer]:[customer] /home/[customer]/db-backup
Set the permissions to 770: chmod -R 770 db-backup
Give the database user (example-db in this example above) the proper permissions to user mysqldump: PROCESS (‘Manage processes’ in Webmin)
Check that the script is actually working properly: su (change user) to [customer] then run the script manually and check the contents of the zip file.
If you run into a disk quota issue: set the quota to unlimited for both the user and the group.
Create a cron job to execute the script on a daily basis, e.g. one hour before midnight (at that moment the data center starts their own backups). Use Webmin to create the cron job, but you can also use crontab:
@daily /home/[customer]/db-backup/[customer]-backup.sh #Creates a daily backup of the [customer] database (as a gz file)
The next day, check that the script has run properly (the timestamp should be shortly before midnight).
Here’s the Bash script without a specific customer’s name:
To check that the website is up at all times, we add a “Remote HTTP Service” monitor to a Webmin installation. This should be done on another web server than the one hosting the current Moodle site.
Go to Tools > System and Server Status and click on the button Add monitor of type, while HTTP Request is selected. This should take you to the following screen:
Set the field “Connection timeout” to 10 seconds. This should also notify you if the loading times for the Moodle website get unacceptable (i.e. more than 10 seconds).
9. Share Access Credentials
If the Moodle installation is completely ready, and no further content editing is required, notify your users directly. In your email, include:
Url for the new Moodle installation
Username
Password
At any rate, send the company’s Moodle admin account (username and password) for this installation to the lead developer (the lead engineer, at the time of writing), including MySQL's password, the Unix password and the url. Be sure to do that in a secure way.
Large Moodle clones can consume enormous amounts of disk space and time. This guide identifies which tables and directories can safely be skipped for development or testing, and shows how to recreate missing tables afterward.
What to Skip When Cloning Large Sites
Some customers have very large Moodle installations, with literally hundreds of Gigabytes worth of data. In that case, you can skip:
The moodledata directory (you won’t be able to see any uploaded files such as scorms, videos or pictures though)
The mdl_files table when creating a database dump, as well as
the mdl_logstore_standard_log table
Recreating the Omitted Tables
If you’ve got a database dump without the mdl_files and mdl_logstore_standard_log tables, you need to recreate them in your own copy of the database. Use these SQL statements to do so:
CREATE TABLE `mdl_files` (
`id` bigint(10) NOT NULL,
`contenthash` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`pathnamehash` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`contextid` bigint(10) NOT NULL,
`component` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`filearea` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`itemid` bigint(10) NOT NULL,
`filepath` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`filename` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`userid` bigint(10) DEFAULT NULL,
`filesize` bigint(10) NOT NULL,
`mimetype` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`status` bigint(10) NOT NULL DEFAULT '0',
`source` longtext COLLATE utf8mb4_unicode_ci,
`author` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`license` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`timecreated` bigint(10) NOT NULL,
`timemodified` bigint(10) NOT NULL,
`sortorder` bigint(10) NOT NULL DEFAULT '0',
`referencefileid` bigint(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='description of files, content is stored in sha1 file pool' ROW_FORMAT=COMPRESSED;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `mdl_files`
--
ALTER TABLE `mdl_files`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `mdl_file_pat_uix` (`pathnamehash`),
ADD KEY `mdl_file_comfilconite_ix` (`component`,`filearea`,`contextid`,`itemid`),
ADD KEY `mdl_file_con_ix` (`contenthash`),
ADD KEY `mdl_file_lic_ix` (`license`),
ADD KEY `mdl_file_con2_ix` (`contextid`),
ADD KEY `mdl_file_use_ix` (`userid`),
ADD KEY `mdl_file_ref_ix` (`referencefileid`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `mdl_files`
--
ALTER TABLE `mdl_files`
MODIFY `id` bigint(10) NOT NULL AUTO_INCREMENT;
CREATE TABLE `mdl_logstore_standard_log` (
`id` bigint(10) NOT NULL,
`eventname` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`component` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`action` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`target` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`objecttable` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`objectid` bigint(10) DEFAULT NULL,
`crud` varchar(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`edulevel` tinyint(1) NOT NULL,
`contextid` bigint(10) NOT NULL,
`contextlevel` bigint(10) NOT NULL,
`contextinstanceid` bigint(10) NOT NULL,
`userid` bigint(10) NOT NULL,
`courseid` bigint(10) DEFAULT NULL,
`relateduserid` bigint(10) DEFAULT NULL,
`anonymous` tinyint(1) NOT NULL DEFAULT '0',
`other` longtext COLLATE utf8mb4_unicode_ci,
`timecreated` bigint(10) NOT NULL,
`origin` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`ip` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`realuserid` bigint(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Standard log table' ROW_FORMAT=COMPRESSED;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `mdl_logstore_standard_log`
--
ALTER TABLE `mdl_logstore_standard_log`
ADD PRIMARY KEY (`id`),
ADD KEY `mdl_logsstanlog_tim_ix` (`timecreated`),
ADD KEY `mdl_logsstanlog_couanotim_ix` (`courseid`,`anonymous`,`timecreated`),
ADD KEY `mdl_logsstanlog_useconconcr_ix` (`userid`,`contextlevel`,`contextinstanceid`,`crud`,`edulevel`,`timecreated`),
ADD KEY `mdl_logsstanlog_con_ix` (`contextid`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `mdl_logstore_standard_log`
--
ALTER TABLE `mdl_logstore_standard_log`
MODIFY `id` bigint(10) NOT NULL AUTO_INCREMENT;
LDAP and SSO are fundamentally different approaches to user authentication in Moodle. LDAP performs credential lookups directly against a directory service; SSO delegates authentication to a centralized identity provider. Understanding the distinction helps you choose the right approach for your setup.
LDAP Authentication
With Moodle’s LDAP plugin, each Moodle instance authenticates users itself by querying an LDAP directory — typically an on-premises Active Directory server accessible over the local network. When a user logs in:
Moodle connects to the LDAP server and queries the credentials.
If the credentials match, Moodle checks whether the user account exists locally; if not, it creates one.
Moodle creates a session.
This works well when your LDAP server is on the same network as Moodle. It is not SSO — each Moodle instance authenticates independently, so a user with access to three instances must log in to each one separately.
Single Sign-On (SSO)
An SSO solution centralizes authentication in a dedicated identity provider (IdP) — such as Azure AD, Okta, or any SAML2-compatible service. When a user logs in to a connected application:
The application redirects the user to the IdP.
The user authenticates once at the IdP — or is recognized as already authenticated.
The IdP redirects back with the outcome and the user’s profile data.
Moodle checks whether the user account exists locally; if not, it creates one.
Moodle creates a session.
Moodle never sees the user’s credentials — it only receives the result from the IdP. If the same user then accesses a second Moodle instance, they are already authenticated at the IdP and pass through without logging in again.
Azure AD: LDAP Is Not Available by Default
Azure AD (Entra ID) is a cloud service and does not expose a traditional LDAP endpoint. To use LDAP with Azure AD, you would need to set up Azure AD Domain Services (AD DS) — a managed domain add-on that does expose LDAP. This is a complex configuration and carries meaningful security risk: exposing LDAP over the internet is roughly equivalent to opening a database port publicly.
For Azure AD environments, SSO via SAML2 or OIDC is the straightforward and recommended path. If you do need to evaluate AD DS, these references cover the setup: