Guide to Setup a Moodle website in Google Cloud Platform (GCP) with HTTPS

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.

Post Updates

  • 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

Assumptions πŸ€”

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.

Overview πŸ“‘

There are 5 main steps to setup everything, listed as below:

Step 1: Provision a Compute Engine instance

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.

Step 2: Install server environments

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.

Set server time zone

Before we continue further, we need to make sure that our server time zone matches with ours. We can check via below command.

timedatectl status

If it shows different time zone than yours, then you need to change it. We can check available time zones with below command.

timedatectl list-timezones

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.

Environment preparations

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.

sudo mysql_secure_installation

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

Check listening ports

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

Step 3: Make our subdomain/domain secure

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.

Step 4: Get Moodle through Git

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.

cd /opt

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.

cd moodle

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

Step 5: Setup Moodle specific configurations

Copy installation to web root

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

Change web root entrypoint

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 https://(your domain).

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

Create new database and user

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.
Create database.

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.

mysql> quit;

Update opcache and some PHP configuration

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

Continue installation from web interface

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.

Change system path

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.

Setup cron job for Moodle

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.

Don't forget to revert Moodle directory permissions

Run this command to revert /var/www/html/moodle permissions.

sudo chmod -R 0755 /var/www/html/moodle

Monitor the usage

Don't forget to monitor the instance usage and cost. Simple but important.

Known Issues 🀯

  • 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.

Next steps ⏩

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 clamav component. 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.

Opinion πŸ’­

  • 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).

Conclusion 🏁

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.

Further reading πŸ“š

Please review these articles to enrich your knowledge:

πŸ“£ 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 😊

41