Moodle CloudRun Module — Configuration Guide
Moodle is the world's most popular open-source Learning Management System (LMS), used by educational institutions, corporations, and online learning platforms worldwide. This module deploys Moodle on Google Cloud Run using a custom PHP 8.3/Apache container, backed by a managed Cloud SQL PostgreSQL instance and shared NFS storage for course materials.
Moodle CloudRun is a wrapper module built on top of App CloudRun. It delegates all GCP
infrastructure provisioning to App CloudRun (Cloud Run service, Cloud SQL, networking, Secret
Manager, GCS, CI/CD) and uses a Moodle Common sub-module to supply Moodle-specific
application configuration, secret generation, and initialization logic. The Moodle Common
outputs feed into App CloudRun's application_config, module_env_vars,
module_secret_env_vars, module_explicit_secret_values, and module_storage_buckets inputs.
This guide documents variables that are unique to
Moodle CloudRunor that have Moodle-specific defaults differing from theApp CloudRunbase module. For full documentation of variables with identical semantics, refer to the App_CloudRun Configuration Guide.
§1 · Module Overview
What Moodle CloudRun provides
- A PHP 8.3/Apache container auto-built from a custom Dockerfile and deployed as a Cloud Run
service.
enable_image_mirroringdefaults tofalse— there is no external prebuilt image to mirror. - A Cloud SQL PostgreSQL instance as the Moodle database backend.
MOODLE_DB_TYPE = "pgsql"is hardcoded and cannot be changed. - Cloud SQL Auth Proxy via Unix socket —
enable_cloudsql_volume = trueis forced inmoodle.tfwithin themoodle_modulelocal (theapplication_modulesmerge), and is not exposed as a configurable variable. The socket path is configured bycloudsql_volume_mount_path(default"/cloudsql"). - NFS (Cloud Filestore) enabled by default (
enable_nfs = true) as the Moodlemoodledatadirectory, mounted atnfs_mount_path(default"/mnt/nfs"). - Redis enabled by default (
enable_redis = true) for PHP session handling and Moodle application cache. Required for horizontal scaling across multiple Cloud Run instances. - A Cloud Scheduler job auto-provisioned on every deployment to trigger
admin/cron.phpevery minute using a secure, auto-generated cron password. - An additional
moodle-dataGCS bucket provisioned automatically viamodule_storage_buckets, alongside any user-definedstorage_buckets. MOODLE_CRON_PASSWORDandMOODLE_SMTP_PASSWORDauto-generated byMoodle Commonand stored in Secret Manager.DB_PASSWORDauto-generated by App CloudRun and injected viamodule_secret_env_vars.
Key differences from App CloudRun defaults
| Feature | App CloudRun default | Moodle CloudRun default |
|---|---|---|
enable_cloudsql_volume | false | true (forced — not a variable) |
enable_nfs | false | true |
enable_redis | false | true |
enable_image_mirroring | true | false |
memory_limit | "512Mi" | "2Gi" |
max_instance_count | 1 | 3 |
container_port | 8080 | 8080 |
| Health probe path | /healthz | /health.php |
| DB type | user-configured | PostgreSQL (forced) |
| Cron scheduler | none | auto-provisioned (* * * * *) |
| Extra GCS bucket | none | moodle-data auto-provisioned |
§2 · IAM & Project Identity
These variables configure the GCP project target, deployment identity, and platform metadata. Their semantics are identical to the App CloudRun equivalents — refer to App_CloudRun §2 for full detail.
| Variable | Default | Description |
|---|---|---|
project_id | (required) | GCP project into which all resources are deployed. |
tenant_deployment_id | "demo" | Short suffix appended to resource names to allow multiple deployments in the same project. |
resource_creator_identity | "rad-module-creator@tec-rad-ui-2b65.iam.gserviceaccount.com" | Service account used by Terraform. Override with a project-specific account for production. |
support_users | [] | Email addresses granted IAM access and added to monitoring alert channels. |
resource_labels | {} | Labels applied to all module-managed resources. |
module_description | (Moodle LMS description string) | Platform UI description. Do not modify unless customising the module. |
module_documentation | "https://docs.radmodules.dev/docs/modules/Moodle_CloudRun" | External documentation URL displayed in the platform UI. |
module_dependency | ["Services GCP"] | Platform modules that must be deployed before this one. |
deployment_id | "" | Optional fixed deployment ID. Auto-generated when blank. |
§3 · Core Service Configuration
§3.A · Application Identity
display_name is a Moodle-specific alias for the application_display_name input in
App CloudRun. description is passed only to Moodle Common — it is not forwarded to
App CloudRun's application_description. Similarly, db_name and db_user (documented in
§7.C) are aliases for application_database_name / application_database_user in
Moodle Common, not in App CloudRun directly.
| Variable | Default | Description |
|---|---|---|
application_name | "moodle" | Base name for the Cloud Run service, Artifact Registry repo, Secret Manager secrets, and GCS buckets. Do not change after initial deployment — renaming requires manual resource migration. |
display_name | "Moodle LMS" | Human-readable name in the platform UI and Cloud Run console. Mapped to application_display_name in App CloudRun. |
description | "Moodle LMS - Online learning and course management platform" | Description passed to Moodle Common (used as the db-init job description and application description within the Common layer). It is not forwarded to App CloudRun's application_description input. |
application_version | "4.5.1" | Container image version tag. Increment to trigger a new Cloud Build run and deploy a new Cloud Run revision. |
§3.B · Resource Sizing
cpu_limit and memory_limit are exposed as dedicated top-level variables rather than
requiring the full container_resources object. min_instance_count and max_instance_count
are user-configurable (unlike Ghost, where they are hardcoded).
| Variable | Default | Description |
|---|---|---|
cpu_limit | "1000m" | CPU limit per container instance. Increase to "2000m" for production with concurrent students. CPUs above "1000m" require cpu_always_allocated = true to prevent throttling during idle periods. |
memory_limit | "2Gi" | Memory limit per container instance. PHP 8.3 with OPcache and active sessions typically consumes 0.5–1.5 Gi; 2 Gi provides adequate headroom. |
min_instance_count | 0 | Minimum live instances. Scale-to-zero is enabled by default (0). Set to 1 for production to eliminate cold-start latency between student sessions. |
max_instance_count | 3 | Maximum concurrent instances. Moodle scales horizontally when Redis manages PHP sessions. |
container_port | 8080 | Port Apache/PHP-FPM binds to inside the container. Cloud Run routes HTTP traffic to this port. |
timeout_seconds | 300 | Maximum request duration before Cloud Run returns 504. Increase for course imports or large file uploads (maximum 3600). |
execution_environment | "gen2" | Cloud Run generation. "gen2" is required for NFS volume mounts. |
deploy_application | true | Set false to provision infrastructure (secrets, storage, IAM) without deploying the container. Useful for staged rollouts. |
enable_image_mirroring | false | Disabled — Moodle uses a custom Dockerfile; there is no external prebuilt image to mirror. |
enable_cloudsql_volumeis forced totrueinmoodle.tfand is not exposed as a variable. Moodle connects to Cloud SQL via the Auth Proxy Unix socket atcloudsql_volume_mount_path(default"/cloudsql").
§3.C · Environment Variables & Secrets
The following variables are automatically injected by Moodle CloudRun via
module_env_vars in addition to any user-defined environment_variables. Override any
auto-injected default by supplying the same key in your environment_variables map.
Auto-injected plain-text variables:
| Variable | Injected value | Notes |
|---|---|---|
MOODLE_DB_TYPE | "pgsql" | Hardcoded. Cannot be changed. Set in the moodle_module local via an environment_variables merge. |
ENABLE_REVERSE_PROXY | "TRUE" / "FALSE" | "TRUE" only when application_domains is non-empty (Cloud Armor + custom domain in use). Set in the moodle_module local. |
MOODLE_DATA_DIR | value of nfs_mount_path | "/mnt/nfs" by default. |
DATA_PATH | value of nfs_mount_path | "/mnt/nfs" by default. |
MOODLE_SITE_NAME | "Moodle LMS" | Override via environment_variables. |
MOODLE_SITE_FULLNAME | "Moodle Learning Management System" | Override via environment_variables. |
LANGUAGE | "en" | Override via environment_variables. |
MOODLE_ADMIN_USER | "admin" | Override via environment_variables. |
MOODLE_ADMIN_EMAIL | "admin@example.com" | Override before going live. |
MOODLE_SKIP_INSTALL | "no" | Override via environment_variables. |
MOODLE_UPDATE | "yes" | Override via environment_variables. |
MOODLE_SMTP_HOST | "" | Set to your SMTP hostname for outbound email. |
MOODLE_SMTP_PORT | "587" | Override if not using submission port. |
MOODLE_SMTP_USER | "" | Set to your SMTP username. |
MOODLE_SMTP_SECURE | "tls" | "tls" or "ssl". |
MOODLE_SMTP_AUTH | "LOGIN" | Override if using a different auth mechanism. |
MOODLE_REDIS_ENABLED | tostring(enable_redis) | "true" by default. |
MOODLE_REDIS_HOST | redis_host, "$(NFS_SERVER_IP)", or "" | When enable_redis = true and redis_host = "", the literal string "$(NFS_SERVER_IP)" is injected. cloudrun-entrypoint.sh expands this placeholder to the actual NFS server IP at container start-up. When enable_redis = false, this is set to "". |
MOODLE_REDIS_PORT | value of redis_port | "6379" by default. |
MOODLE_REDIS_PASSWORD | value of redis_auth | "" by default. |
Auto-injected secrets (module_secret_env_vars):
DB_PASSWORD is automatically sourced from Secret Manager (generated by App CloudRun) and
merged in alongside any secrets generated by Moodle Common (including MOODLE_CRON_PASSWORD
and MOODLE_SMTP_PASSWORD). These are never exposed in Terraform state.
User-configurable variables:
| Variable | Default | Description |
|---|---|---|
environment_variables | {} | Plain-text key/value pairs injected into the Cloud Run revision. Use to override any auto-injected default above or to supply additional PHP/Moodle settings. |
secret_environment_variables | {} | Map of env var name → Secret Manager secret name. Resolved at runtime; plaintext never stored in Terraform state. |
secret_propagation_delay | 30 | Seconds to wait after secret creation before proceeding with dependent operations. |
secret_rotation_period | "2592000s" | Pub/Sub notification period for Secret Manager rotation reminders (30 days). |
§3.D · Networking
| Variable | Default | Description |
|---|---|---|
ingress_settings | "all" | "all" permits public internet traffic. Use "internal-and-cloud-load-balancing" when the service is fronted by Cloud Armor. |
vpc_egress_setting | "PRIVATE_RANGES_ONLY" | Routes only RFC 1918 traffic via the VPC connector. Set "ALL_TRAFFIC" for strict egress or on-premises connectivity. |
cloudsql_volume_mount_path | "/cloudsql" | Container path where the Cloud SQL Auth Proxy Unix socket is mounted. Must match the socket path referenced in Moodle's database config. enable_cloudsql_volume is always true. |
container_protocol | "http1" | HTTP protocol version. Use "h2c" only if the application supports HTTP/2 cleartext. |
service_annotations | {} | Kubernetes-style annotations applied to the Cloud Run service for advanced configuration. |
service_labels | {} | Additional labels applied to the Cloud Run service resource. |
§3.E · Initialization & Bootstrap
Auto-provisioned Cloud Scheduler cron job:
Moodle CloudRun always creates a Cloud Scheduler job targeting Moodle's built-in cron
endpoint. This job is hardcoded and cannot be disabled via variables.
| Property | Value |
|---|---|
| Schedule | * * * * * (every minute) |
| Method | GET |
| Target | https://<service_url>/admin/cron.php?password=<MOODLE_CRON_PASSWORD> |
| Deadline | 320s |
| Timezone | Etc/UTC |
The MOODLE_CRON_PASSWORD is auto-generated by Moodle Common, stored in Secret Manager, and
embedded in the scheduler job URL at provision time.
User-defined jobs:
| Variable | Default | Description |
|---|---|---|
initialization_jobs | [] | Cloud Run jobs executed once during deployment for tasks such as database setup, plugin installation, or seed data loading. Each job runs sequentially in the order listed. |
cron_jobs | [] | Recurring Cloud Run jobs triggered by Cloud Scheduler. Supplement (not replace) the auto-provisioned Moodle cron job. |
For the initialization_jobs and cron_jobs object schemas refer to
App_CloudRun §3.E.
§4 · Advanced Security
§4.A · Secret Management
Three secrets are auto-generated and stored in Secret Manager on every deployment:
| Secret | Generated by | Injected as |
|---|---|---|
DB_PASSWORD | App CloudRun | DB_PASSWORD environment variable |
MOODLE_CRON_PASSWORD | Moodle Common | Embedded in Cloud Scheduler job URL |
MOODLE_SMTP_PASSWORD | Moodle Common | MOODLE_SMTP_PASSWORD environment variable |
| Variable | Default | Description |
|---|---|---|
secret_rotation_period | "2592000s" | Duration between Secret Manager rotation Pub/Sub notifications (30 days). Set null to disable. Format: "<seconds>s". |
secret_propagation_delay | 30 | Seconds to wait after secret creation before dependent operations proceed. Increase if deployments fail with "secret not found" errors. Valid range: 0–300. |
enable_auto_password_rotation | false | Deploys an automated database password rotation job using Cloud Run and Eventarc. Rotates on the secret_rotation_period schedule. |
rotation_propagation_delay_sec | 90 | Seconds to wait after password rotation before restarting Cloud Run to pick up the new value. |
§4.B · Identity-Aware Proxy (IAP)
IAP enforces Google identity authentication before users reach Moodle. Useful for internal institutional deployments where access should be restricted to staff or enrolled students with Google accounts.
| Variable | Default | Description |
|---|---|---|
enable_iap | false | Enables Cloud Run native IAP. When true, configure iap_authorized_users and/or iap_authorized_groups. |
iap_authorized_users | [] | Individual users or service accounts granted access. Format: "user:email@example.com". |
iap_authorized_groups | [] | Google Groups granted access. Format: "group:name@example.com". Preferred for institution-level access management. |
§4.C · Cloud Armor & CDN
Cloud Armor provides a WAF security policy fronted by a Global HTTPS Load Balancer. When
application_domains is non-empty, MOODLE_REVERSE_PROXY and ENABLE_REVERSE_PROXY are
automatically set to "true" / "TRUE" so Moodle generates correct HTTPS URLs behind the
load balancer.
| Variable | Default | Description |
|---|---|---|
enable_cloud_armor | false | Enables Cloud Armor WAF + Global HTTPS Load Balancer. Configure application_domains with your custom domain names. |
application_domains | [] | Custom domain names associated with the load balancer. Google-managed SSL certificates are provisioned automatically. DNS must point to the LB IP before cert provisioning succeeds. |
enable_cdn | false | Enables Cloud CDN on the load balancer to cache static assets at Google edge locations. Only active when enable_cloud_armor = true. |
admin_ip_ranges | [] | IP CIDR ranges permitted to bypass certain network restrictions for administrative access. |
§4.D · VPC Service Controls
| Variable | Default | Description |
|---|---|---|
enable_vpc_sc | false | Restricts GCP API access to requests from within a VPC-SC perimeter, preventing data exfiltration. Requires an existing VPC-SC perimeter in the project. |
§4.E · Binary Authorization
| Variable | Default | Description |
|---|---|---|
enable_binary_authorization | false | Enforces a Binary Authorization policy requiring container images to carry a valid attestation before deployment. Requires a policy and attestor to be pre-configured in the project. |
§5 · Traffic & Ingress
§5.A · Ingress Controls
| Variable | Default | Options | Description |
|---|---|---|---|
ingress_settings | "all" | all / internal / internal-and-cloud-load-balancing | "all" allows public internet traffic directly to Cloud Run. "internal" restricts to VPC and Google internal services. "internal-and-cloud-load-balancing" is required when Cloud Armor is in front. |
vpc_egress_setting | "PRIVATE_RANGES_ONLY" | ALL_TRAFFIC / PRIVATE_RANGES_ONLY | "PRIVATE_RANGES_ONLY" routes only RFC 1918 outbound traffic via VPC. Use "ALL_TRAFFIC" when all egress must pass through a firewall or on-premises network. |
§5.B · Traffic Management
| Variable | Default | Description |
|---|---|---|
traffic_split | [] | Allocates traffic across Cloud Run revisions for canary or blue-green deployments. All entries must sum to 100. Empty list sends all traffic to the latest revision. Example: [{ type = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST", percent = 90 }, { type = "TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION", revision = "moodle-00003", percent = 10 }]. |
§5.C · Custom Domains & Load Balancer
Custom domains require enable_cloud_armor = true. The module provisions a Global HTTPS Load
Balancer with Google-managed SSL certificates. Setting application_domains also activates
MOODLE_REVERSE_PROXY = "true" and ENABLE_REVERSE_PROXY = "TRUE" automatically so Moodle
generates correct HTTPS URLs behind the load balancer.
| Variable | Default | Description |
|---|---|---|
application_domains | [] | List of custom domains (e.g. ["moodle.university.edu"]). DNS A records must point to the load balancer IP before SSL certificates can be provisioned. |
enable_cdn | false | Enables Cloud CDN on the load balancer. Caches static assets (JS, CSS, images) at Google's edge nodes, reducing latency for geographically distributed students. |
admin_ip_ranges | [] | CIDR ranges permitted to bypass network restrictions for administrative access. |
§6 · CI/CD Integration
§6.A · GitHub & Cloud Build
Moodle uses a custom Dockerfile (enable_image_mirroring = false). Cloud Build compiles
the image from source on every trigger, builds and tags it in Artifact Registry, then deploys
a new Cloud Run revision.
| Variable | Default | Description |
|---|---|---|
enable_cicd_trigger | false | Creates a Cloud Build trigger that builds and deploys on every push to the configured branch. |
github_repository_url | "" | Full HTTPS URL of the GitHub repository (e.g. "https://github.com/my-org/moodle-app"). Required when enable_cicd_trigger = true. |
github_token | "" | GitHub Personal Access Token for repository authentication. Required scopes: repo, admin:repo_hook, workflow. Mutually exclusive with github_app_installation_id. |
github_app_installation_id | "" | Cloud Build GitHub App installation ID. Preferred over PAT for organisation repositories. Mutually exclusive with github_token. |
cicd_trigger_config | { branch_pattern = "^main$" } | Controls branch filter, included/ignored file paths, trigger name, and build substitutions. |
§6.B · Cloud Deploy Pipelines
| Variable | Default | Description |
|---|---|---|
enable_cloud_deploy | false | Switches from direct Cloud Build deployments to a managed Cloud Deploy pipeline with defined promotion stages. Requires enable_cicd_trigger = true. |
cloud_deploy_stages | [dev, staging, prod(approval)] | Ordered promotion stages. Each stage maps to a Cloud Deploy target and associated Cloud Run service. Set require_approval = true for manual sign-off before production promotion. |
enable_binary_authorization | false | See §4.E. |
§7 · Reliability & Data
§7.A · Health Probes
Moodle exposes startup_probe and liveness_probe as dedicated top-level variables (not the
startup_probe_config / health_check_config names used in Django). Both default to the
/health.php endpoint, which reflects PHP availability and database connectivity — more
accurate for Moodle readiness than a generic path.
| Variable | Default | Description |
|---|---|---|
startup_probe | { enabled=true, type="HTTP", path="/health.php", initial_delay_seconds=0, timeout_seconds=10, period_seconds=30, failure_threshold=20 } | Determines when the instance is ready to receive traffic. failure_threshold=20 with period_seconds=30 allows up to 10 minutes of startup time — sufficient for first-boot schema creation and plugin registration. |
liveness_probe | { enabled=true, type="HTTP", path="/health.php", initial_delay_seconds=120, timeout_seconds=10, period_seconds=60, failure_threshold=3 } | Periodically checks whether the running instance is healthy. initial_delay_seconds=120 prevents premature restarts during the post-startup period. |
uptime_check_config | { enabled=true, path="/", check_interval="60s", timeout="10s" } | Cloud Monitoring uptime check. Probes the service from multiple global locations and fires an alert if the endpoint becomes unreachable. |
§7.B · Storage
NFS (Cloud Filestore):
| Variable | Default | Description |
|---|---|---|
enable_nfs | true | Provisions a Cloud Filestore NFS instance and mounts it into the container. Used as the Moodle moodledata directory for uploaded files, course materials, and user submissions. Requires execution_environment = "gen2". |
nfs_mount_path | "/mnt/nfs" | Container path where the NFS volume is mounted. Injected as MOODLE_DATA_DIR and DATA_PATH. |
GCS buckets:
An additional moodle-data bucket is always provisioned automatically via
module_storage_buckets in moodle.tf. It is created regardless of the storage_buckets
variable value.
| Variable | Default | Description |
|---|---|---|
storage_buckets | [{ name_suffix = "data" }] | Additional GCS buckets to provision. Each entry defines a suffix, storage class, versioning, and access controls. |
create_cloud_storage | true | Set false to skip provisioning storage_buckets (the moodle-data bucket is unaffected). |
gcs_volumes | [] | GCS buckets to mount as GCS Fuse filesystem volumes inside the container. |
nfs_instance_name | "" | Name of an existing NFS GCE VM to use. When set, targets this instance directly instead of auto-discovering one. Leave empty to auto-discover a Services GCP-managed instance. |
nfs_instance_base_name | "app-nfs" | Base name for the inline NFS GCE VM when no existing instance is found. The deployment ID is appended for uniqueness. |
manage_storage_kms_iam | false | When true, creates a CMEK KMS keyring and storage encryption key and enables CMEK on all storage buckets. |
enable_artifact_registry_cmek | false | When true, enables at-rest encryption of container images with a customer-managed key in Artifact Registry. |
§7.C · Database
Moodle requires PostgreSQL. db_name and db_user are passed to Moodle Common where
they configure the database name and user for initialization jobs and the application config.
They are not forwarded directly to App CloudRun's application_database_name /
application_database_user inputs — Moodle Common embeds them in the config output which
App CloudRun consumes. MOODLE_DB_TYPE = "pgsql" is hardcoded — setting a non-PostgreSQL
database type will prevent Moodle from starting.
| Variable | Default | Description |
|---|---|---|
db_name | "moodle" | PostgreSQL database name created within the Cloud SQL instance. Injected as DB_NAME. Do not change after initial deployment — Moodle stores all data here and renaming requires manual migration. |
db_user | "moodle" | PostgreSQL user for the Moodle application. Injected as DB_USER. Password auto-generated and injected as DB_PASSWORD. |
database_password_length | 32 | Length of the auto-generated database password (16–64 characters). |
enable_auto_password_rotation | false | Automates database password rotation via Cloud Run + Eventarc. See §4.A. |
rotation_propagation_delay_sec | 90 | Seconds to wait after password rotation before restarting Cloud Run. |
pg_trgmextension: Required by Moodle for full-text search performance. This extension is created automatically by thedb-init.shscript (part of theMoodle Commondefault initialization jobs) — no user configuration is needed.
§7.D · Backup & Recovery
backup_uri is the Moodle-specific name for the backup_file input in App CloudRun. The
mapping is applied in main.tf (backup_file = var.backup_uri).
| Variable | Default | Description |
|---|---|---|
backup_schedule | "0 2 * * *" | Cron expression (UTC) for the automated backup job. Leave empty to disable. |
backup_retention_days | 7 | Days to retain backup files in GCS before automatic deletion. |
enable_backup_import | false | Triggers a one-time import job to restore the backup at backup_uri. Runs after the database is provisioned. Test in a non-production environment before enabling on a populated database. |
backup_source | "gcs" | Source of the backup file: "gcs" (full GCS URI) or "gdrive" (Google Drive file ID). |
backup_uri | "" | For GCS: full URI e.g. "gs://my-bucket/backups/moodle.sql.gz". For Google Drive: the file ID from the share URL. Mapped to backup_file in App CloudRun. |
backup_format | "sql" | Format of the backup file. Accepted: sql, gz, tar, tgz, tar.gz, zip. |
§8 · Integrations
§8.A · Redis Cache
Redis is enabled by default (enable_redis = true) because Cloud Run routes requests
across multiple instances simultaneously. Without a shared session store, users would be logged
out whenever a request reached a different instance. MOODLE_REDIS_* variables are injected
automatically (see §3.C). redis_port is typed as a string (not integer).
| Variable | Default | Description |
|---|---|---|
enable_redis | true | Enables Redis as the PHP session handler and Moodle application cache backend. |
redis_host | "" | Redis server hostname or IP. When empty and enable_redis = true, defaults to the NFS server IP. Override with a Cloud Memorystore instance IP for production. |
redis_port | "6379" | Redis port (string type). Change if the Redis instance is not on the default port. |
redis_auth | "" | Redis AUTH password. Leave empty if the instance does not require authentication. Treated as sensitive. |
For production deployments with multiple Cloud Run instances, provision a Cloud Memorystore for Redis instance and set
redis_hostto its IP. The default NFS-server-as-Redis-host approach is suitable for development only.
§8.B · Custom SQL Scripts
| Variable | Default | Description |
|---|---|---|
enable_custom_sql_scripts | false | Runs .sql files from a GCS bucket against the Moodle database after provisioning. Use for schema migrations, pg_trgm extension installation, or seed data. |
custom_sql_scripts_bucket | "" | GCS bucket name (without gs:// prefix) containing the SQL scripts. |
custom_sql_scripts_path | "" | Path prefix within the bucket. All .sql files under this prefix are executed in lexicographic order. |
custom_sql_scripts_use_root | false | Execute scripts as the root database user. Enable when scripts require elevated privileges (e.g. CREATE EXTENSION). |
§8.C · Observability
| Variable | Default | Description |
|---|---|---|
uptime_check_config | { enabled=true, path="/", check_interval="60s", timeout="10s" } | Cloud Monitoring uptime check. Probes the service from multiple global locations and fires an alert if unreachable. |
alert_policies | [] | Cloud Monitoring alert policies. Each policy monitors a single metric and notifies support_users when the threshold is exceeded. |
service_annotations | {} | Cloud Run service-level annotations. |
service_labels | {} | Additional labels on the Cloud Run service. |
§8.D · SMTP & Outbound Email
Moodle sends course notifications, grade reports, and account emails via SMTP. There are no
dedicated Terraform variables for SMTP configuration — override the auto-injected defaults
(see §3.C) using environment_variables:
environment_variables = {
MOODLE_SMTP_HOST = "smtp.sendgrid.net"
MOODLE_SMTP_PORT = "587"
MOODLE_SMTP_USER = "apikey"
MOODLE_SMTP_SECURE = "tls"
MOODLE_ADMIN_EMAIL = "admin@university.edu"
MOODLE_SITE_NAME = "University LMS"
}
secret_environment_variables = {
MOODLE_SMTP_PASSWORD = "moodle-smtp-password" # Secret Manager secret name
}
MOODLE_SMTP_PASSWORD is auto-generated by Moodle Common and stored in Secret Manager.
Override its value in Secret Manager with your actual SMTP credential after deployment.
§9 · Platform-Managed Behaviours
The following behaviours are applied automatically by Moodle CloudRun regardless of the
variable values in your tfvars file. They cannot be overridden by user configuration.
| Behaviour | Detail |
|---|---|
enable_cloudsql_volume = true | Forced in moodle.tf locals merge. Moodle always connects to Cloud SQL via the Auth Proxy Unix socket. Not exposed as a variable. |
MOODLE_DB_TYPE = "pgsql" | Hardcoded in moodle.tf via the moodle_module environment_variables merge. PostgreSQL is the only supported Moodle database backend on this platform. |
ENABLE_REVERSE_PROXY conditional | Set to "TRUE" only when application_domains is non-empty. Set to "FALSE" otherwise. Injected via the moodle_module environment_variables merge. Note: MOODLE_REVERSE_PROXY is not injected by Moodle CloudRun; only ENABLE_REVERSE_PROXY is set. |
| Cloud Scheduler cron job | Always created (* * * * *). Targets admin/cron.php with an auto-generated cron password. Cannot be disabled via variables. |
moodle-data GCS bucket | Always provisioned via the hardcoded module_storage_buckets in moodle.tf, independent of the user-defined storage_buckets variable. |
MOODLE_CRON_PASSWORD secret | Auto-generated by Moodle Common. Embedded in the Cloud Scheduler job URL. |
MOODLE_SMTP_PASSWORD secret | Auto-generated by Moodle Common. Override the generated value in Secret Manager with the real SMTP credential. |
DB_PASSWORD secret | Auto-generated by App CloudRun. Injected via module_secret_env_vars. |
scripts_dir | Set to Moodle Common's bundled scripts/ directory. Cannot be overridden via tfvars. |
§10 · Variable Reference
Complete reference of all Moodle CloudRun variables, their defaults, and the UI metadata
group they belong to.
| Variable | Default | Group |
|---|---|---|
module_description | (Moodle LMS description) | 0 |
module_documentation | "https://docs.radmodules.dev/docs/modules/Moodle_CloudRun" | 0 |
module_dependency | ["Services GCP"] | 0 |
module_services | (list of GCP services) | 0 |
credit_cost | 50 | 0 |
require_credit_purchases | false | 0 |
enable_purge | true | 0 |
public_access | true | 0 |
deployment_id | "" | 0 |
resource_creator_identity | "rad-module-creator@tec-rad-ui-2b65.iam.gserviceaccount.com" | 0 |
project_id | (required) | 1 |
tenant_deployment_id | "demo" | 1 |
support_users | [] | 1 |
resource_labels | {} | 1 |
application_name | "moodle" | 2 |
display_name | "Moodle LMS" | 2 |
description | "Moodle LMS - Online learning and course management platform" | 2 |
application_version | "4.5.1" | 2 |
deploy_application | true | 3 |
cpu_limit | "1000m" | 3 |
memory_limit | "2Gi" | 3 |
min_instance_count | 0 | 3 |
max_instance_count | 3 | 3 |
container_port | 8080 | 3 |
execution_environment | "gen2" | 3 |
timeout_seconds | 300 | 3 |
service_annotations | {} | 3 |
service_labels | {} | 3 |
enable_image_mirroring | false | 3 |
traffic_split | [] | 3 |
container_protocol | "http1" | 3 |
cloudsql_volume_mount_path | "/cloudsql" | 3 |
ingress_settings | "all" | 4 |
vpc_egress_setting | "PRIVATE_RANGES_ONLY" | 4 |
enable_iap | false | 4 |
iap_authorized_users | [] | 4 |
iap_authorized_groups | [] | 4 |
environment_variables | {} | 5 |
secret_environment_variables | {} | 5 |
secret_propagation_delay | 30 | 5 |
secret_rotation_period | "2592000s" | 5 |
backup_schedule | "0 2 * * *" | 6 |
backup_retention_days | 7 | 6 |
enable_backup_import | false | 6 |
backup_source | "gcs" | 6 |
backup_uri | "" | 6 |
backup_format | "sql" | 6 |
enable_cicd_trigger | false | 7 |
github_repository_url | "" | 7 |
github_token | "" | 7 |
github_app_installation_id | "" | 7 |
cicd_trigger_config | { branch_pattern = "^main$" } | 7 |
enable_cloud_deploy | false | 7 |
cloud_deploy_stages | [dev, staging, prod(approval)] | 7 |
enable_binary_authorization | false | 7 |
enable_custom_sql_scripts | false | 8 |
custom_sql_scripts_bucket | "" | 8 |
custom_sql_scripts_path | "" | 8 |
custom_sql_scripts_use_root | false | 8 |
enable_cloud_armor | false | 9 |
admin_ip_ranges | [] | 9 |
application_domains | [] | 9 |
enable_cdn | false | 9 |
create_cloud_storage | true | 10 |
storage_buckets | [{ name_suffix = "data" }] | 10 |
enable_nfs | true | 10 |
nfs_mount_path | "/mnt/nfs" | 10 |
gcs_volumes | [] | 10 |
nfs_instance_name | "" | 8 |
nfs_instance_base_name | "app-nfs" | 8 |
manage_storage_kms_iam | false | 10 |
enable_artifact_registry_cmek | false | 10 |
db_name | "moodle" | 11 |
db_user | "moodle" | 11 |
database_password_length | 32 | 11 |
enable_auto_password_rotation | false | 11 |
rotation_propagation_delay_sec | 90 | 11 |
initialization_jobs | [] | 12 |
cron_jobs | [] | 12 |
startup_probe | { path="/health.php", failure_threshold=20, period_seconds=30 } | 13 |
liveness_probe | { path="/health.php", initial_delay_seconds=120, period_seconds=60 } | 13 |
uptime_check_config | { enabled=true, path="/", check_interval="60s" } | 13 |
alert_policies | [] | 13 |
enable_redis | true | 21 |
redis_host | "" | 21 |
redis_port | "6379" | 21 |
redis_auth | "" | 21 |
enable_vpc_sc | false | 22 |
vpc_cidr_ranges | [] | 22 |
vpc_sc_dry_run | true | 22 |
organization_id | "" | 22 |
enable_audit_logging | false | 22 |
max_images_to_retain | 7 | 9 |
delete_untagged_images | true | 9 |
image_retention_days | 30 | 9 |
max_revisions_to_retain | 7 | 3 |
Configuration Pitfalls & Sensible Defaults
Risk levels: Critical (data loss, full outage, security breach) — High (service unavailable or significant degradation) — Medium (degraded function or increased cost) — Low (minor impact).
| Variable | Sensible Default | Risk | Consequence of Incorrect Value |
|---|---|---|---|
project_id | (required) | Critical | No default — deployment fails immediately. |
database_type | "POSTGRES_15" | Critical | Moodle supports both MySQL and PostgreSQL, but this module defaults to PostgreSQL 15. Changing to MYSQL requires verifying the Moodle config generation logic wires MySQL-specific settings. Setting NONE breaks all data persistence. |
enable_nfs | true | Critical | Moodle stores all moodledata (course files, user submissions, plugin caches, session data) on NFS. Without NFS, the moodledata directory is ephemeral — uploaded course materials and student submissions are lost on every new revision. Multiple instances will have inconsistent data directories. NFS is mandatory for any real Moodle deployment. |
enable_redis | true | High | Moodle uses Redis for session storage and application caching. Without Redis, multiple Cloud Run instances use isolated PHP file sessions — students hitting different instances lose their sessions between page loads. Redis is essential for multi-instance Moodle deployments. |
redis_host | "" (auto-resolves to NFS IP) | High | When redis_host = "" the module uses the NFS server IP. If enable_nfs = false and redis_host is also empty, Moodle cannot initialise sessions and will fail to serve authenticated pages. |
cron_jobs | [] (none configured) | Critical | Moodle requires a cron job running php /var/www/html/admin/cli/cron.php at least every minute (recommended every 5 minutes) to process course notifications, grade calculations, forum digests, assignment reminders, and scheduled tasks. Without cron, these functions are silently skipped — grade changes may not notify students, forum digests never send, and scheduled quiz windows do not open or close. |
memory_limit | "2Gi" | High | The default is 2Gi. Moodle with active plugin suites (Turnitin, H5P, BigBlueButton) can require 4–8Gi per instance under load. Insufficient memory causes PHP fatal errors during course rendering or file processing. Never run below 1Gi. |
cpu_limit | "1000m" | Medium | Moodle course rendering and quiz grading are CPU-intensive. For deployments with concurrent student activity, 2000m is recommended to avoid request queuing. |
min_instance_count | 0 | High | The default is 0 (scale-to-zero). Cold starts for Moodle are 30–60 seconds (PHP init + database connection pool). Students arriving during a cold start experience login failures or 504 timeouts. Set to 1 for any scheduled exam or live class period. |
execution_environment | "gen2" | High | NFS mounts require gen2. Changing to "gen1" with enable_nfs = true causes the Cloud Run service to fail to start with a volume mount error. |
application_database_name | "moodle" | Critical | Immutable after first deployment — changing this recreates the database, destroying all course data, enrolments, grades, and user accounts. |
application_database_user | "moodle" | Critical | Immutable after first deployment — changing this recreates the user, breaks the existing database connection, and locks out the application. |
database_password_length | 32 | Medium | Minimum 16 enforced. Moodle config stores the database password — longer passwords reduce brute-force risk on the Cloud SQL endpoint. |
ingress_settings | "all" | Medium | "all" exposes the Moodle admin panel to the public internet. Consider enable_iap = true or Cloud Armor rules to restrict /admin access to known IP ranges. |
enable_cloud_armor | false | Medium | Moodle login pages are common targets for credential stuffing, particularly for institutions with many student accounts. Cloud Armor WAF is recommended for any public-facing LMS. |
enable_backup_import | false | Critical | Requires backup_uri to be set and accessible. Enabling with an empty backup_uri causes the restore job to fail during apply. |
backup_retention_days | 7 | High | Educational data including grades and course records must be retained longer than 7 days for compliance. Increase to at least 30 days; 90+ days for accredited institutions. |
timeout_seconds | 300 | Medium | Moodle operations like course backup/restore, gradebook exports, and plugin installations can exceed 300 seconds. Increase to 3600 for deployments performing heavy administrative operations via HTTP. |
enable_custom_sql_scripts | false | Medium | Requires custom_sql_scripts_bucket and custom_sql_scripts_path when enabled. Leaving them empty with the flag true causes the init job to fail. |
secret_propagation_delay | 30 | Low | Increase to 60–90 seconds in multi-region deployments to prevent "secret not found" errors during apply. |
enable_iap | false | Medium | Moodle admin access is protected only by Moodle's own auth when IAP is disabled. Recommended for institutional deployments. |
Destroying Resources
Known Deletion Issue: Serverless IPv4 Address Release
When destroying a Cloud Run deployment, you may encounter an error similar to:
Error: Error waiting for Subnetwork to be deleted: The following serverless IPv4 address(es) on subnet ... are still in use.
Cause: GCP holds serverless IPv4 addresses on the VPC subnet asynchronously after a Cloud Run service is deleted. These addresses are released by GCP approximately 20–30 minutes after the Cloud Run service is removed. Terraform/OpenTofu cannot complete the subnet or VPC deletion until they are fully released.
Resolution: Wait 20–30 minutes after the initial destroy attempt, then re-run the destroy command:
tofu destroy
The second run will succeed once GCP has released the reserved addresses.