Moodle has been great for supporting many schools, campuses, and even companies in digitalized education system. Moodle is quite stable to be deployed by IT teams or individual for their organizations, simple to customized. It can be deployed offline or online depending on needs and situation. There are many third-party software installers that can ship and deploy Moodle almost instantly, most of them are managed by another third-party hosting services.
While it's pretty quick and simple to start, but at some point it's becoming difficult to maintain and keep up-to-date. We may not have a full control of the server that hosts our Moodle, hence we can't setup some new requirements needed or do advanced tinkering. We may overprovision resources that host the Moodle application, when most of the time it's always in underutilization. We also may have corrupted Moodle installation from those third-party tools, that we don't notice at first but then it's showing everywhere. We also may forget at some point to check whether our website is using a secure protocol (HTTPS) or not. Yeah, lot of risks, but it's not the end of the world.
In this post, I will guide you how to setup a Moodle LMS website hosted in a Compute Engine (CE) instance in Google Cloud Platform (GCP). The application will be secured using free and open source SSL certificate service so we don't have to pay if we don't want to.
- 30 June 2021: Modify cron steps as we only setup using PHP 7.4 and add opinion about scaling Moodle
- 29 June 2021: Add more reference and set server time zone
Below is the list of environment and app version used in this guide (at the time this post out):
- Moodle 3.11+
- PHP 7.4.20 and 8.0
- MySQL 8
- Ubuntu 20.04 LTS
While it's based on my experience, please note that you may adjust some steps to suit your needs. Another disclaimer is that the steps I'm going to show can be subjective, so I make some assumptions:
- You already have an active Billing Account set for your organization (or your personal Google Account).
- You are already prepared to pay for using GCP resources and you aware that pay-per-use model needs extensive monitoring of resource usage to avoid overpricing.
- You already have a DNS Records management set elsewhere outside GCP (eg. cPanel user account).
- You already have control of a school/campus domain name.
- You are trying to setup your Moodle LMS to be accessible on a subdomain or domain.
- Your user size won't be too big to handle, hopefully (approx. 10-100 concurrent users).
- You already have familiar knowledge of GCP, their policies, and techniques.
There are 5 main steps to setup everything, listed as below:
- Provision a Compute Engine instance
- Install server environments
- Make our subdomain/domain secure
- Get Moodle through Git
- Setup Moodle specific configurations
First things first, you have to create a CE instance in GCP. It's pretty easy and straightforward. You may want to use
gcloud shell commands, but I prefer to use the Cloud Console web interface.
As for initial setup, you can start with as low as below specifications:
- Region/Zone: choose the nearest possible one from your place (the price may be higher, but it provides lower latency). Our instance is zonal resource.
- Machine configuration:
- Machine family: General Purpose
- Series: E2
- Machine type: e2-micro (1 shared-core up to 2 vCPUs and 1 GB memory which only costs around US$11.14 per month on full run)
- Boot disk:
- Operating system: Ubuntu
- Version: latest LTS possible (choose 20.04 LTS as of the time I wrote this post)
- Boot disk type: SSD persistent disk
- Size: 10 GB (only costs around US$2.21 per month)
- Firewall: Check both Allow HTTP traffic and Allow HTTPS traffic
- Under Networking tab in advance configuration, click on one of the Network interfaces (should be default at first). You may want to use Static External IP instead of Ephemeral.
- (Optional) SSH keys: Provide your public SSH key(s) generated from your PC. Make sure your private SSH key(s) is secured in your user directory. Although it's optional, but I use this to provide access to the VM directly from my Terminal without bother opening Google Cloud Console in browser.
Noted that this specification is the lowest possible one and hopefully cost-effective. You may choose higher resources, but it's a good thing to start small when you are working in the cloud.
Please make note of your instance's external IP address.
If you choose to provide SSH key(s) to the instance, then you can open your terminal and try to connect through it with this command (I'm using Windows Terminal btw):
ssh <your_username>@<external_ip> -i <full/path/to/your/private/keys>
Otherwise, you can click on SSH button inline with instance name in Compute Engine > VM Instances table. This will open a new browser window that showing terminal access to the instance.
Before we continue further, we need to make sure that our server time zone matches with ours. We can check via below command.
If it shows different time zone than yours, then you need to change it. We can check available time zones with below command.
Find your time zone (mine is Asia/Jakarta, which is UTC+7), and then execute command below to set your time zone.
sudo timedatectl set-timezone <your time zone>
At this point, I reboot the machine, although the server's time zone already changed. Just to make sure, of course.
Now we are going to install environments needed, based on the official guide for installing Moodle in Ubuntu (check it here). I will show you here anyway.
Update Ubuntu components
sudo apt-get update sudo apt-get upgrade
Add apt repository to track updates for PHP
sudo add-apt-repository ppa:ondrej/php sudo apt-get update
Install Apache and MySQL
sudo apt install apache2 mysql-client mysql-server php7.4 libapache2-mod-php7.4
Run MySQL secure installation. You'll be asked for a new ROOT password using options of validation (choose 1 = MEDIUM). Then you'll just confirm following questions with Y (yes). This has to be done to make your MySQL server as secure as possible by disabling remote root access and provide strict validation when creating passwords.
Install additional software or dependencies. Please be aware that we are going to install components for PHP 7.4.
sudo apt install graphviz aspell ghostscript php7.4-pspell php7.4-curl php7.4-gd php7.4-intl php7.4-mysql php7.4-xml php7.4-xmlrpc php7.4-ldap php7.4-zip php7.4-soap php7.4-mbstring
Restart Apache server to make sure all components are loaded successfully.
sudo service apache2 restart
Install Git (for Step 4).
sudo apt install git
We need to check whether port 80 (not secure) and 443 (secure) is listening from your machine by running this command.
sudo ss -tulwn
If port 443 is not listed, we need to install ssl module for Apache.
sudo a2enmod ssl
Restart Apache once again
sudo service apache2 restart
Now check again all listening ports. Port 443 should be listed as LISTEN.
sudo ss -tulwn
Now we need to make sure that our subdomain (or main domain) uses HTTPS. That means, we need an SSL certificate. We will use the free way to obtain that.
First, in your DNS records manager, add or modify two A records just like below. By doing this, we are going to make sure that our subdomain/domain will pinpoint to your machine external IP address. In order (Name, TTL, Type, Record):
- example.com, 14400, A, (your external IP)
- www.example.com., 14400, A, (your external IP)
Then, we are going to install Certbot, a free, open source software tool for automatically using Let’s Encrypt certificates on manually-administrated websites to enable HTTPS (check it here).
You can follow the specific instructions through this link for our case (Ubuntu 20.04 and Apache).
If you follow all the Certbot instructions and get a successful result, then you should be able to access your subdomain/domain using HTTPS. Now that's easy and under your control.
Git is what is called a "version control system". By using git, it will much easier down the road to update the Moodle core application. We will use /opt directory for this installation.
Download the Moodle code and index.
sudo git clone git://git.moodle.org/moodle.git
⚠ Warning: Because we are using e2-micro (as I was), this command will somehow make our machine disk write and read to be at peak for several minutes (if not hours) and your SSH access might be interrupted because of timeout. Take your time, sip a cup of tea ☕. It will be finished anytime soon.
Then, change directory into the downloaded Moodle folder.
Retrieve a list of each branch available. Press Q to quit seeing.
sudo git branch -a
Corresponding with Moodle convention on branch naming, we will use the latest Moodle version (as the time of writing, it's Moodle 3.11). Tell git which branch to track or use.
sudo git branch --track MOODLE_311_STABLE origin/MOODLE_311_STABLE
Check out the Moodle version specified.
sudo git checkout MOODLE_311_STABLE
Now we have our Moodle installation, we need to copy that to the webroot (it's usually under
/var/www/html). Then we need to create
moodledata directory to store Moodle-generated files. We should change both directories' permissions accordingly.
sudo cp -R /opt/moodle /var/www/html/ sudo mkdir /var/moodledata sudo chown -R www-data /var/moodledata sudo chmod -R 777 /var/moodledata sudo chmod -R 0755 /var/www/html/moodle
At this point, we should have PHP 7.4 and MySQL 8 running. And also, our Moodle application is now accessible through
https://(your domain)/moodle. This is quite annoying. We need to change the web root config so that our user can access through
sudo nano /etc/apache2/sites-available/000-default.conf
Now change in the line of
DocumentRoot /var/www/html to
DocumentRoot /var/www/html/moodle. Save the file by presing Ctrl+X > confirm with Y > Enter to save with default file name (we are using Nano text editor).
Restart the Apache server.
sudo service apache2 restart
We need to create a user for our Moodle database. Open MySQL as root, use your MySQL root password from Step 2.
sudo mysql -u root -p
Now we are in MySQL shell.
mysql> CREATE DATABASE moodle DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Create user. Use your username and password of your choice.
mysql> CREATE USER 'moodledude'@'localhost' IDENTIFIED BY 'passwordformoodledude';
Grant some privileges to the user.
mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,CREATE TEMPORARY TABLES,DROP,INDEX,ALTER ON moodle.* TO 'moodledude'@'localhost';
Quit the shell.
Before we continue, we should update some opcache and PHP configuration according to Moodle docs. Please review the required settings for:
For opcache config file, you can open from this location (use
ls to check opcache.ini name file:
cd /etc/php/7.4/apache2/conf.d/ ls sudo nano 10-opcache.ini
For PHP config file, you can open from this location:
cd /etc/php/7.4/apache2/ ls sudo nano php.ini
Please update all required settings before we continue. Note that from my case, I don't need to uncomment extension sections in
php.ini. They are already enabled since component installation at Step 2.
After you finished, restart the Apache server to load the new settings.
sudo service apache2 restart
Now we can continue our Moodle installation from its web interface. Before that, change the permissions on
/var/www/html/moodle directory so the installation can succeed smoothly.
sudo chmod -R 777 /var/www/html/moodle
Open your browser and access
https://(your domain) and follow the prompts.
- Change path of moodledata to: /var/moodledata
- Change database type to: mysqli
- Change database settings:
- Host server: localhost
- Database: moodle
- User: moodledude (the user you created when setting up the database)
- Password: passwordformoodledude (the password for the user you created)
- Tables Prefix: mdl_
- Environment checks. At this point every thing should be in green OK. If there are any warnings, let me know.
- Next and next and confirm installations.
At this point, installation should finished successfully (just wait). Follow next prompts for creating Admin user and website configuration.
Now Moodle is already up and running. We need to update some little configuration a little more.
Navigate to Site Administration > Server > System Paths. Input path settings like the following:
- Path to du: /usr/bin/du
- Path to aspell: /usr/bin/aspell
- Path to dot: /usr/bin/dot
Save your changes.
As required by Moodle, we need to setup a cron job (as mentioned in this documentation). We can check the PHP CLI configuration using below command.
php -i | grep php.ini
Make sure that the PHP version used by CLI is the same with our initial installation, which is 7.4.
Now we add our cron job by using crontab.
crontab -u www-data -e
At first, you'll be asked which editor to use. Choose nano. Then, add the following at the end of the opened file.
* * * * * /usr/bin/php /var/www/html/moodle/admin/cli/cron.php >/dev/null
Save your changes. Now our Moodle has its cron job running every 1 minute.
Run this command to revert
sudo chmod -R 0755 /var/www/html/moodle
Don't forget to monitor the instance usage and cost. Simple but important.
- If you try to access VM directories via Visual Studio Code, you may encounter lag and sometimes timeout. In my case, the disk IOPS and I/O usage is full for several minutes and hours when the issue happens. Do you have any ideas on this? Solution: We have to scale up our machine type.
- Certbot may fail to give you SSL certificate if you don't properly setup DNS records and open port 443.
- Certbot may fail to renew SSL certificate on first check, even we already have a proper setup. Try executing the certbot command again.
Below is the list of what you can do further:
- Start installing plugins that suit your needs.
- Scale up your instance by turning it off first then change its machine type and disk size. Restart it whenever you are ready.
- Setup specific Firewall policies for your subnetworks.
- Avoid directory ownership mismanagement. You don't want to accidentally allow unintended access and authorization that can alter your Moodle source.
- Use external database authentication and enrolment plugins to speed up administration routines. I will cover this in the upcoming post.
- If you follow Moodle installation steps from official docs, pay attention on
clamavcomponent. This antivirus will cause hourly huge spikes in CPU utilization to process virus database. You may want to scale up your instance if you want to make use of it.
- There is "official" Docker image of Moodle, but it's solely used for development purposes, not production.
- You may think of using Kubernetes architecture to get autohealing and autoscaling features, but that may bring a lot of technical difficulties to setup. Always start simple.
- As stated by Moodle docs, Moodle LMS itself can be clustered (or at least scaled out). However, it's not necessary and will require complex architecture work to do so. Instead, Moodle LMS can be scaled up by just increasing memory, CPU, and disk. In this way, of course, Moodle is at higher risk of downtime since our single VM only exists in a single zone in a region.
- Actually, there is a way to separate Moodle source app, moodledata, and the database so it can be scaled out. But that will incur higher cost than we expected. For example, GCP itself allows use of Cloud Storage to be mounted for VMs using FUSE. The idea is to use it as a place for moodledata. But we will be charged with Cloud Storage pricing plus its performance may not significantly sufficient for Moodle to store and process its data (lower performance than SSD and limited concurency capability).
That is a hell lot of steps to install Moodle in GCP. But, we already established our very own Moodle installation. We have full control over resources and that is the main point and a good thing! We only pay for what we use (or provision to be exact), we can make use of cloud flexibility to scale up or scale down, and we can integrate with other features GCP offers.
Please review these articles to enrich your knowledge:
- Moodle Documentation
- Moodle Performance Recommendations as of 3.11
- Google Cloud Documentation
- Google Cloud Pricing Calculator (yeah, it's not a reading, but it's also important tool to estimate your cloud cost in GCP)
📣 Please leave a comment, suggestions, or any feedback on this matter. If you found this post useful, please share to others and make people happy.
😉 If you need further assistance, I'm happy to help you. Contact me anytime.
😎 If you need a hand for your related projects, I'm also happy to collaborate. Contact me then. Quick!
Thank you for reading this lengthy post. I appreciate that a lot 😊