Moodle is one of the most popular open source learning management platform empowering educators and researchers across the world to disseminate their work efficiently. It is also one of the most matured and robust OSS application which is developed and improvised by the community over the years. We have seen customers deploying Moodle in Azure from small, medium and large enterprises to schools, public sector and government organizations. In this blog post, I will be sharing some of the best practices and tips for deploying Moodle on Azure based on our experience working with some of our customers.

Provisioning a Scalable Moodle Cluster on Azure

To simplify the deployment and provisioning of Moodle Cluster on Azure, our engineering team has developed some canned Azure Resource Manager (ARM) templates and made it available in our GitHub repository.

If you have Azure account, you can deploy Moodle via the Azure portal using the button below, or you can deploy Moodle via the CLI. Please note that while you can use an Azure free account to get started depending on which template configuration you choose you will likely be required to upgrade to a paid account.

Below is a list of pre-defined deployment options based on typical deployment scenarios (i.e. dev/test, production etc.). All configurations are fixed, and you just need to pass your ssh public key to the template for logging in to the deployed VMs. For production deployments, large size or Maximum template below is highly recommended which provisions high performance SKUs and configures the environment for high availability.

Deployment Type Description Estimated Cost Launch Minimal This deployment will use NFS, Azure Database for MySQL, and smaller autoscale web frontend VM sku (1 core) that'll give faster deployment time (less than 30 minutes) and requires only 2 VM cores currently that'll fit even in a free trial Azure subscription. link Small to Mid-Size Supporting up to 1000 concurrent users. This deployment will use NFS (no high availability) and MySQL (8 vCores), without other options like elastic search or redis cache. link Large size deployment (with high availability) Supporting more than 2000 concurrent users. This deployment will use Gluster (for high availability, requiring 2 VMs), MySQL (16 vCores) and redis cache, without other options like elastic search. link Maximum This maximal deployment will use Gluster (for high availability, adding 2 VMs for a Gluster cluster), MySQL with highest SKU, redis cache, elastic search (3 VMs), and pretty large storage sizes (both data disks and DB). link

NOTE: Depending on the region you choose to deploy the stack in - the deployment might fail due to SKUs being hardcoded in the template where they are not available. If your deployment fails, please revert to the fully configurable template and change the SKU parameter to one that exists in your region (i.e. Gen-5).

The template deploys the following architecture in Azure.

Configuring Azure Database for MySQL for performance and scale

Moodle supports multiple databases like MySQL, PostgreSQL or SQL Server but the best performance and scale for Moodle is observed with MySQL database. As a result, MySQL is a preferred choice for customers and community when deploying Moodle for large number of concurrent users (1K – 10K). Azure Database for MySQL is a fully managed database service with built-in high availability, elastic scaling and automatic backups. Leveraging a managed service ensures you do not have to worry about managing your database for backups, high availability and setting up additional database monitoring.

When provisioning Azure Database for MySQL server for Moodle deployments, following is the recommended tier which provides best price to performance ratio.

Concurrent Users Compute (vCores) Storage Tier < 1000 Users 8 vCores – General Purpose tier 1TB 1K – 2K Users 16 vCores – General Purpose Tier 2TB 2K – 5K Users 32 vCores – General Purpose Tier 2TB > 5K Users 32 - 64 vCores – General Purpose Tier 2TB

If you notice, the recommendation for Storage tier is 2TB for higher concurrenct users irrespective of the size of the database. This is due to fact that in Azure Database for MySQL service, IOPs is not charged separately and it increases progressively with storage size provisioned (~3 IOPs/GB). With 2TB, you get 6000 provisioned IOPs which is recommended and required for Moodle when the number of concurrent users exceeds 1000 users.

In addition to server scaling, configuring the right server parameters also plays an important role in the performance of Moodle application as recommended in the official documentation. You can leverage Azure portal or Azure CLI to configure the following server parameters

Enable the query cache

query_cache_type = 1

query_cache_size = 36M

query_cache_min_res_unit = 2K

Increase the size of open table cache

table_open_cache = 1024

If you are migrating or restoring an existing Moodle database to Azure Database for MySQL service, it is recommended to perform OPTIMIZE ALL on the tables in the database following the restore. If it is a new deployment with no preexisting data, you can skip this step

mysqlcheck -a moodle

Lastly, we would highly recommend to enable Query Performance Insights on your Azure Database for MySQL server which is an opt-in monitoring feature available at no additional cost. This will enable you to monitor and identify long running and slow performing queries on the database which might lead to poor performance or slowness in the application



Query Performance Insights can be enabled by enabling query store which requires setting the following server parameters from Azure Portal

Set query_store_capture_mode to ALL

Set query_store_wait_sampling_capture_mode to ALL



To give you an example of relevance of this feature in performance tuning, I want to share the following screenshot of the Long Running Queries report for a Moodle application running 3K concurrent users which was seeing an avg response time of > 100 seconds which was unacceptable.



After turning ON Query Performance Insights, we were able to identify the top query (Query ID 1065) in the report below which further enabled us to discover that we are hitting a known bug in Moodle causing slow performance in the application. On applying the workaround suggested in the bug tracker of truncating the expired cache in mdl_cache_flags table and creating indexes on expiry and timemodified columns, the performance was back to < 10 seconds which was expected.





It is also important to check and validate that binlog and slow_query_log is disabled as it may cause additional IO overhead and adds slowness to the server for high concurrent users.

Ensure log_bin is set to OFF

Ensure slow_query_log is set to OFF

Finally, it is important to leverage Redis Cache for Session handling and OPCache to ensure MySQL database is not flooded with queries as caching can help alleviate a lot of pressure on the backend database and improve the overall performance and scale for the application.

Next Steps

Get started by deploying your Moodle application on Azure using the above template.

If you have some feedback or discover any additional finding, please reach out to us AskAzureDBforMySQL@service.microsoft.com and we will be happy to incorporate it to our blog.

Parikshit Savjani

Senior Program Manager, Microsoft