Moodle GKE 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 GKE Autopilot using a custom PHP 8.3/Apache container, backed by a managed Cloud SQL PostgreSQL instance and shared NFS storage for course materials.
Moodle GKE is a wrapper module built on top of App GKE. It uses App GKE for all GCP infrastructure provisioning (cluster, networking, Cloud SQL, GCS, secrets, CI/CD) and adds Moodle-specific application configuration, an automated cron Cloud Scheduler job, and database initialisation on top.
Note: Variables marked as platform-managed are set and maintained by the platform. You do not normally need to change them.
How This Guide Is Structured
This guide documents only the variables that are unique to Moodle_GKE or that have Moodle-specific defaults that differ from the App_GKE base module. For all other variables — project identity, runtime scaling, backend configuration, storage, CI/CD, observability, networking, IAP, and Cloud Armor — refer directly to the App_GKE Configuration Guide.
Variables fully covered by the App GKE guide:
| Configuration Area | App GKE.md Section | Moodle-Specific Notes |
|---|---|---|
| Module Metadata & Configuration | §1 Module Overview | Different defaults for module_description and module_documentation. |
| Project & Identity | §2 IAM & Access Control | Refer to base App GKE module documentation. |
| Runtime & Scaling | §3.A Compute (GKE Autopilot) | See Moodle Runtime Configuration below for cpu_limit, memory_limit, and Moodle-specific scaling defaults. container_image_source defaults to "custom" — Moodle is built from a Dockerfile. |
| Environment Variables & Secrets | §3 Core Service Configuration | See Moodle Environment Variables below for Moodle-specific injected defaults. |
| GKE Backend Configuration | §3.A Compute (GKE Autopilot) | enable_custom_domain defaults to true and reserve_static_ip defaults to true. See Platform-Managed Behaviours. |
| Networking & Network Policies | §3.D Networking & Network Policies | Identical. |
| Jobs & Scheduled Tasks | §3.E Initialization Jobs & CronJobs | See Platform-Managed Behaviours for the auto-provisioned Moodle cron Cloud Scheduler job. Two default init jobs are defined: db-init (first) and nfs-init (second, needs_db = false). |
| Additional Services | §3.F Additional Services | Identical. |
| CI/CD & GitHub Integration | §6 CI/CD & Delivery | Refer to base App GKE module documentation. |
| Storage — NFS | §3.C Storage (NFS / GCS / GCS Fuse) | enable_nfs defaults to true. NFS is the active Moodle data directory (moodledata). See Platform-Managed Behaviours. |
| Storage — GCS | §3.C Storage (NFS / GCS / GCS Fuse) | Refer to base App GKE module documentation. An additional moodle-data GCS bucket is provisioned automatically. |
| Database Configuration | §3.B Database (Cloud SQL) | See Moodle Database Configuration below for the db_name and db_user variable naming. |
| Backup Schedule & Retention | §3.B Database (Cloud SQL) | Refer to base App GKE module documentation. See Backup Import & Recovery below for the backup_uri naming difference. |
| Custom SQL Scripts | §3.E Initialization Jobs & CronJobs | Refer to base App GKE module documentation. |
| Observability & Health | §3.A Compute (GKE Autopilot) | See Moodle Health Probes below for the startup_probe and liveness_probe variables and their /health.php defaults. |
| Cloud Armor WAF | §4.A Cloud Armor WAF | Refer to base App GKE module documentation. |
| Identity-Aware Proxy | §4.B Identity-Aware Proxy (IAP) | Requires iap_oauth_client_id, iap_oauth_client_secret, and optionally iap_support_email — enforced by validation.tf precondition. Refer to base App GKE module documentation. |
| Binary Authorization | §4.C Binary Authorization | Includes binauthz_evaluation_mode variable (default "ALWAYS_ALLOW"). Refer to base App GKE module documentation. |
| VPC Service Controls | §4.D VPC Service Controls | Identical. |
| Secrets Store CSI Driver | §4.E Secrets Store CSI Driver | Identical. |
| Traffic & Ingress | §5 Traffic & Ingress | enable_custom_domain defaults to true. See Platform-Managed Behaviours. |
| CDN | §5.B CDN | Identical. |
| Pod Disruption Budgets | §7.A Pod Disruption Budgets | enable_pod_disruption_budget defaults to true. |
| Topology Spread Constraints | §7.B Topology Spread Constraints | Identical. |
| Resource Quotas | §7.C Resource Quotas | Refer to base App GKE module documentation. |
| Auto Password Rotation | §7.D Auto Password Rotation | See Moodle Database Configuration. |
| Redis Cache | §8.A Redis / Memorystore | enable_redis defaults to true. See Redis Cache for Moodle-specific configuration. |
| Backup Import | §8.B Backup Import | Uses backup_uri instead of backup_file — see Backup Import & Recovery. |
| Service Mesh (ASM) | §8.C Service Mesh (ASM via Fleet) | Identical. |
| Multi-Cluster Services | §8.D Multi-Cluster Services (MCS) | Identical. |
Platform-Managed Behaviours
The following behaviours are applied automatically by Moodle GKE regardless of the variable values in your tfvars file. They cannot be overridden by user configuration.
| Behaviour | Detail |
|---|---|
| PostgreSQL forced | MOODLE_DB_TYPE = "pgsql" is injected automatically. Moodle requires PostgreSQL — do not set database_type to a MySQL or SQL Server variant. |
| Cloud SQL Auth Proxy | enable_cloudsql_volume defaults to true and is passed through as a user-configurable variable. Moodle connects to Cloud SQL via the Auth Proxy Unix socket by default. |
| NFS as moodledata | MOODLE_DATA_DIR and DATA_PATH are automatically set to the value of nfs_mount_path. The NFS volume is the active moodledata directory where Moodle stores uploaded files, course materials, and user submissions. |
| Reverse proxy headers | ENABLE_REVERSE_PROXY is set to "TRUE" when application_domains is non-empty and "FALSE" otherwise. This is injected via the moodle_module environment_variables merge in moodle.tf. Note: MOODLE_REVERSE_PROXY is not injected by Moodle GKE; only ENABLE_REVERSE_PROXY is set. |
| Moodle cron job | A Cloud Scheduler job is created automatically, targeting /admin/cron.php?password=CRON_PASSWORD on the application URL every minute. The cron password is a randomly generated 32-character string stored in Secret Manager and never exposed in plaintext. |
| SMTP defaults | MOODLE_SMTP_HOST, MOODLE_SMTP_PORT ("587"), MOODLE_SMTP_USER, MOODLE_SMTP_SECURE ("tls"), and MOODLE_SMTP_AUTH ("LOGIN") are injected with defaults. Override these via environment_variables to configure your SMTP server before going live. |
| Site identity defaults | MOODLE_SITE_NAME ("Moodle LMS"), MOODLE_SITE_FULLNAME, LANGUAGE ("en"), MOODLE_ADMIN_USER ("admin"), MOODLE_ADMIN_EMAIL ("admin@example.com"), MOODLE_SKIP_INSTALL ("no"), and MOODLE_UPDATE ("yes") are injected with defaults. Override via environment_variables. |
| Moodle data GCS bucket | An additional GCS bucket with the suffix moodle-data is provisioned alongside any buckets defined in storage_buckets. |
| CRON and SMTP secrets | MOODLE_CRON_PASSWORD and MOODLE_SMTP_PASSWORD are generated by Moodle Common and stored in Secret Manager. For GKE, their raw values are also passed via explicit_secret_values to bypass Secret Manager read-after-write consistency issues on initial apply. |
| Custom domain enabled | enable_custom_domain defaults to true and reserve_static_ip defaults to true. This ensures a stable external IP is reserved and Moodle's wwwroot is configured correctly from the first deployment without manual post-deployment steps. |
Moodle Application Identity
These variables control how the Moodle deployment is named and described. They correspond to the standard identity variables in App GKE but have Moodle-specific defaults. An additional description variable is also present, used by the Moodle Common sub-module interface.
| Variable | Default | Options / Format | Description & Implications |
|---|---|---|---|
application_name | "moodle" | [a-z][a-z0-9-]{0,19} | Internal identifier used as the base name for GKE workloads, Cloud SQL, GCS buckets, Artifact Registry, and the Kubernetes namespace. Functionally identical to application_name in App GKE. Do not change after initial deployment. |
application_version | "4.5.1" | Moodle version string, e.g. "4.5.1" | Version tag applied to the container image and used for deployment tracking. Increment this to trigger a new Cloud Build run and rolling update. |
application_display_name | "Moodle LMS" | Any string | Human-readable name shown in the platform UI and GKE monitoring dashboards. Equivalent to application_display_name in App GKE. Can be updated freely without affecting resource names. |
application_description | "Moodle Learning Management System on GKE Autopilot" | Any string | Brief description of the deployment. Populated into Kubernetes resource annotations and platform documentation. Equivalent to application_description in App GKE. |
description | "Moodle LMS - Online learning and course management platform" | Any string | Additional description used by the internal Moodle Common sub-module interface. Distinct from application_description — both are present. For most purposes, setting application_description is sufficient. |
Validating Application Identity
# Confirm the Deployment exists with the expected name and namespace
kubectl get deployments -n NAMESPACE -o wide
# View workload annotations
kubectl describe deployment moodle -n NAMESPACE | grep -A5 Annotations
Moodle Runtime Configuration
Moodle is a PHP 8.3/Apache application. The module exposes cpu_limit and memory_limit as dedicated top-level variables (passed through the Moodle Common configuration layer) in addition to the standard container_resources object which is passed directly to App GKE.
| Variable | Default | Options / Format | Description & Implications |
|---|---|---|---|
cpu_limit | "2000m" | Kubernetes CPU quantity string (e.g. "1000m", "4000m") | CPU limit for the Moodle application container. PHP with OPcache and concurrent student requests can generate significant CPU bursts during quiz rendering, grade calculations, and file operations. Minimum 1000m for development; 2000m recommended for production. |
memory_limit | "4Gi" | Kubernetes memory quantity string (e.g. "2Gi", "4Gi") | Memory limit for the Moodle application container. PHP 8.3 with OPcache, active student sessions, and file upload handling typically consumes 1–2 Gi under normal load. Minimum 1Gi for development; 4Gi recommended for production with concurrent course delivery. |
Note on
container_resources: The standardcontainer_resourcesobject (documented in App_GKE §3.A) is also available and is passed directly to App_GKE. Use it when you need to setcpu_request,mem_request, orephemeral_storage_limit. Thecpu_limitandmemory_limittop-level variables are applied via theMoodle_Commonapplication configuration layer and are the primary knobs for Moodle container sizing.
Moodle-specific runtime defaults that differ from App GKE:
| Variable | App GKE Default | Moodle GKE Default | Reason |
|---|---|---|---|
application_name | "gkeapp" | "moodle" | Moodle-specific application identifier. |
application_version | "1.0.0" | "4.5.1" | Default Moodle release version. |
max_instance_count | 3 | 5 | Moodle can scale horizontally when Redis handles PHP sessions and NFS provides shared file storage; a higher ceiling accommodates busy educational institutions. |
enable_custom_domain | false | true | Required for correct wwwroot URL generation — Moodle must know its external URL at startup to render links correctly. A static IP is reserved automatically. |
Validating Runtime Configuration
# View container resource requests and limits on the running pod
kubectl describe pod -n NAMESPACE -l app=moodle | grep -A10 "Limits:"
# Confirm the image and version being used
kubectl get deployment moodle -n NAMESPACE \
-o jsonpath='{.spec.template.spec.containers[0].image}'
Moodle Database Configuration
Moodle requires PostgreSQL. The module uses db_name and db_user (shorter names aligned with the Moodle Common interface) alongside the standard application_database_name and application_database_user variables from App GKE. Both naming pairs are present; they serve distinct roles in the module's two-layer architecture.
All other database variables (database_type, sql_instance_name, database_password_length, enable_auto_password_rotation, rotation_propagation_delay_sec, etc.) behave identically to the App_GKE equivalents — refer to App_GKE §3.B for their documentation.
| Variable | Default | Options / Format | Description & Implications |
|---|---|---|---|
db_name | "moodle" | [a-z][a-z0-9_]{0,62} | The database name passed to the Moodle Common sub-module, used in Moodle-specific initialisation scripts. The companion variable application_database_name (default "gkeapp") is passed to App GKE for Cloud SQL provisioning. Set both to the same value for a consistent deployment. Do not change after initial deployment. |
db_user | "moodle" | [a-z][a-z0-9_]{0,31} | The database user name passed to the Moodle Common sub-module. The companion variable application_database_user (default "gkeapp") is passed to App GKE. Set both to the same value for a consistent deployment. |
Important: Moodle requires PostgreSQL. Set
database_type = "POSTGRES_15"(or another supported PostgreSQL version) in yourtfvars. The module's default is"POSTGRES"(latest managed version). Settingdatabase_type = "NONE"or a MySQL/SQL Server type will prevent Moodle from starting.
pg_trgmextension: Required by Moodle for full-text search performance.Moodle Commonautomatically setsenable_postgres_extensions = trueandpostgres_extensions = ["pg_trgm"]in theapplication_configit passes to App GKE — no user configuration is needed.
Validating Database Configuration
# Confirm the database and user were created
gcloud sql databases list --instance=INSTANCE_NAME --project=PROJECT_ID
gcloud sql users list --instance=INSTANCE_NAME --project=PROJECT_ID
# Confirm DB environment variables are injected into the pod
kubectl exec -n NAMESPACE POD_NAME -- env | grep -E "^DB_"
Moodle Environment Variables
The environment_variables variable (documented in App_GKE §3) is supplemented by a set of Moodle-specific defaults that are automatically injected by the module.
Moodle environment variables injected automatically:
# Platform identity (auto-injected; override via environment_variables)
MOODLE_SITE_NAME = "Moodle LMS"
MOODLE_SITE_FULLNAME = "Moodle Learning Management System"
LANGUAGE = "en"
MOODLE_ADMIN_USER = "admin"
MOODLE_ADMIN_EMAIL = "admin@example.com"
MOODLE_SKIP_INSTALL = "no"
MOODLE_UPDATE = "yes"
# Storage paths (derived from nfs_mount_path)
MOODLE_DATA_DIR = "/mnt/nfs" # value of nfs_mount_path
DATA_PATH = "/mnt/nfs" # value of nfs_mount_path
# SMTP (defaults — configure for production email delivery)
MOODLE_SMTP_HOST = ""
MOODLE_SMTP_PORT = "587"
MOODLE_SMTP_USER = ""
MOODLE_SMTP_SECURE = "tls"
MOODLE_SMTP_AUTH = "LOGIN"
# Database type (hardcoded)
MOODLE_DB_TYPE = "pgsql"
# Reverse proxy (set based on whether application_domains is non-empty)
# Note: MOODLE_REVERSE_PROXY is NOT injected by Moodle_GKE.
ENABLE_REVERSE_PROXY = "TRUE" # "FALSE" when application_domains is empty
# Redis (derived from enable_redis, redis_host, redis_port, redis_auth)
MOODLE_REDIS_ENABLED = "true" # value of enable_redis
MOODLE_REDIS_HOST = "" # defaults to NFS server IP when redis_host is blank
MOODLE_REDIS_PORT = "6379"
MOODLE_REDIS_PASSWORD = ""
To configure SMTP for production email delivery:
environment_variables = {
MOODLE_SMTP_HOST = "smtp.sendgrid.net"
MOODLE_SMTP_USER = "apikey"
MOODLE_ADMIN_EMAIL = "admin@yourinstitution.edu"
MOODLE_SITE_NAME = "My University LMS"
}
secret_environment_variables = {
MOODLE_SMTP_PASSWORD = "moodle-smtp-password" # Secret Manager secret name
}
All other environment_variables and secret_environment_variables behaviour is identical to App_GKE — refer to App_GKE §3.
Moodle Health Probes
Moodle performs database schema validation and plugin checks on startup. The module exposes dedicated probe variables — startup_probe and liveness_probe — with Moodle-specific defaults targeting the /health.php endpoint, which reflects both PHP availability and database connectivity and is more accurate for Moodle's readiness than a generic /healthz path.
In addition to these, the App GKE passthrough variables startup_probe_config and health_check_config are also present in Moodle GKE. Prefer the dedicated Moodle variables below — they are applied at the application layer via Moodle Common.
| Variable | Default | Description & Implications |
|---|---|---|
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 Moodle container is ready to receive traffic. The /health.php endpoint checks PHP availability and database connectivity. failure_threshold = 20 with period_seconds = 30 allows up to 10 minutes of startup time — sufficient for first-boot schema creation and plugin setup. On subsequent deployments the schema is already in place and startup is significantly faster, so the high failure threshold is a safety margin for initial rollouts. |
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 Moodle instance is healthy. The initial_delay_seconds = 120 prevents premature restarts during the post-startup phase. A period_seconds = 60 interval is appropriate for a database-backed LMS — more frequent checks would add unnecessary load. |
Health probe routing:
Moodle GKEexposes two parallel probe variable sets that configure Kubernetes probes via different routing paths.startup_probeandliveness_probe(documented above) are passed toMoodle Common, which applies them to the application container's Kubernetes probe spec.startup_probe_configandhealth_check_configare passed directly toApp GKEand configure the App GKE-standard infrastructure probes. These are parallel paths, not aliases — changing one does not affect the other.startup_probe_configdefaults to TCP type (path is irrelevant);health_check_configdefaults to HTTP on"/". Overridehealth_check_configwithpath = "/health.php"to align the App GKE-standard liveness probe with Moodle's dedicated health endpoint.
Validating Health Probe Configuration
Google Cloud Console: Navigate to Kubernetes Engine → Workloads → moodle deployment, click a pod, and select the Events tab to view probe failure events.
# View probe configuration on the running pod spec
kubectl get deployment moodle -n NAMESPACE \
-o jsonpath='{.spec.template.spec.containers[0].startupProbe}' | jq .
kubectl get deployment moodle -n NAMESPACE \
-o jsonpath='{.spec.template.spec.containers[0].livenessProbe}' | jq .
# View pod restart counts (rising count indicates probe failures)
kubectl get pods -n NAMESPACE -o wide
# View Moodle startup logs
kubectl logs -n NAMESPACE -l app=moodle --since=15m | head -150
Redis Cache
Moodle uses Redis as the PHP session handler and application cache. When enable_redis = true, the MOODLE_REDIS_ENABLED, MOODLE_REDIS_HOST, MOODLE_REDIS_PORT, and MOODLE_REDIS_PASSWORD environment variables are injected automatically. Redis is enabled by default in Moodle GKE because session consistency across multiple pod replicas requires a shared external session store — without it, users may be logged out when a request is routed to a different pod.
For detailed documentation on the Redis variables enable_redis, redis_host, redis_port, and redis_auth, refer to App_GKE §8.A — the variable semantics are identical, but the defaults differ:
| Variable | App GKE Default | Moodle GKE Default | Reason |
|---|---|---|---|
enable_redis | — | true | Redis session handling is critical for Moodle with multiple pod replicas. |
redis_host | "" | "" | Defaults to NFS server IP when blank. Override with a Cloud Memorystore instance IP for production. |
redis_port | "6379" | "6379" | Standard Redis port. |
redis_auth | "" | "" | Set to the Memorystore AUTH string for production deployments. |
Validating Redis Configuration
# Confirm MOODLE_REDIS_* variables are injected into the pod
kubectl exec -n NAMESPACE POD_NAME -- env | grep MOODLE_REDIS
# Test Redis connectivity from a debug pod within the cluster
kubectl run redis-test --rm -it --image=redis:alpine --restart=Never -- \
redis-cli -h REDIS_HOST -p 6379 ping
Backup Import & Recovery
In addition to the scheduled backup (backup_schedule and backup_retention_days, documented in App_GKE §3.B), Moodle_GKE supports a one-time import of an existing Moodle database backup during deployment. This is designed for migrating an existing Moodle instance to GCP or seeding a new environment with production data.
The key naming difference from App GKE is that backup_uri (a full GCS object path or Google Drive file ID) is used instead of backup_file (a filename relative to the backup bucket). The value is mapped internally to the App GKE backup_file input.
| Variable | Default | Options / Format | Description & Implications |
|---|---|---|---|
enable_backup_import | false | true / false | When true, triggers a one-time Kubernetes Job to restore the backup at backup_uri. The import runs after the database is provisioned. If the database already contains data, the import may produce errors — test in a non-production environment first. |
backup_source | "gcs" | gcs / gdrive | The source from which the backup file is retrieved. gcs: provide the full GCS URI in backup_uri. gdrive: provide the Google Drive file ID in backup_uri. Only used when enable_backup_import = true. |
backup_uri | "" | Full GCS URI or Google Drive file ID | For GCS: e.g. "gs://my-bucket/backups/moodle.sql.gz". For Google Drive: the file ID from the share URL (the string after /file/d/ in the URL). Required when enable_backup_import = true. |
backup_format | "sql" | sql / tar / gz / tgz / tar.gz / zip / auto | The format of the backup file. The default "sql" is appropriate for pg_dump plain-text output. Use "gz" for gzip-compressed dumps. |
Validating Backup Import
# Confirm the import job completed successfully
kubectl get jobs -n NAMESPACE --selector=app=backup-import
kubectl logs -n NAMESPACE -l job-name=IMPORT_JOB_NAME
# Verify Moodle data is present after import
gcloud sql databases list --instance=INSTANCE_NAME --project=PROJECT_ID
StatefulSet PVC Configuration
When workload_type = "StatefulSet" is set (see App_GKE §3.A), the following variables configure the per-pod PersistentVolumeClaim created for each replica. For full documentation on each variable, refer to App_GKE §3.A.
Moodle use case: StatefulSets are generally not needed for
Moodle GKEbecause course files and uploads are stored on the shared NFS volume (accessible by all pods simultaneously) and the GCS bucket. The defaultDeploymentworkload type is recommended for Moodle. Use a StatefulSet only if your Moodle configuration requires per-pod local disk storage.
The following StatefulSet variables are present in Moodle GKE and pass through to App GKE with the same behaviour and defaults:
| Variable | Default |
|---|---|
stateful_pvc_enabled | false |
stateful_pvc_size | "10Gi" |
stateful_pvc_mount_path | "/data" |
stateful_pvc_storage_class | "standard-rwo" |
stateful_headless_service | true |
stateful_pod_management_policy | "OrderedReady" |
stateful_update_strategy | "RollingUpdate" |
Deployment Prerequisites & Validation
After deploying Moodle GKE, confirm the deployment is healthy:
# Confirm the Moodle pod is running and ready
kubectl get pods -n NAMESPACE -l app=moodle -o wide
# Confirm the Cloud Scheduler cron job was created (fires every minute)
gcloud scheduler jobs list \
--location=REGION \
--project=PROJECT_ID \
--filter="name~moodle-cron" \
--format="table(name,schedule,state)"
# View the reserved static IP (needed for DNS configuration)
gcloud compute addresses list \
--global \
--project=PROJECT_ID \
--format="table(name,address,status)"
# Confirm the GCS moodle-data bucket was provisioned
gcloud storage buckets list \
--project=PROJECT_ID \
--filter="name~moodle-data"
# Confirm the pg_trgm extension is installed (if enabled)
gcloud sql connect INSTANCE_NAME --user=postgres --database=DB_NAME --project=PROJECT_ID
# Inside psql: \dx
# View Moodle startup logs
kubectl logs -n NAMESPACE -l app=moodle --since=15m | head -150
# Check the Moodle health endpoint via port-forward
kubectl port-forward -n NAMESPACE svc/moodle 8080:80 &
curl -s http://localhost:8080/health.php
# Expect: a 200 response indicating PHP and database are healthy
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. The GKE module defaults to PostgreSQL 15. Changing to MYSQL requires matching the credential injection and config generation logic. Setting NONE leaves Moodle with no persistence. |
enable_nfs | true | Critical | Moodle moodledata must be on a shared volume accessible by all pods. Without NFS, each pod has an isolated data directory — uploaded course files are not visible across replicas, student submissions to one pod are lost on pod reschedule, and Moodle's locking mechanism fails. NFS is mandatory for any multi-pod Moodle deployment. |
enable_redis | true | High | Without Redis, Moodle uses file-based PHP sessions. With multiple pods, a student's session is tied to the specific pod that created it — any request routed to a different pod loses the session, causing random logouts. Redis provides a shared session store accessible by all pods. |
redis_host | "" | High | Falls back to NFS server IP when empty. If NFS is disabled and no explicit host is given, Moodle session initialisation fails. Specify an explicit Redis host for production GKE deployments. |
session_affinity | "ClientIP" | High | Even with Redis, Moodle has some in-process PHP state. "ClientIP" affinity prevents unnecessary cross-pod session handoffs. Disable only with a dedicated Redis-backed session store. |
cron_jobs | [] (none) | Critical | Moodle requires a Kubernetes CronJob running php /var/www/html/admin/cli/cron.php every 1–5 minutes. Without it, grade notifications, course completion triggers, forum digests, quiz availability windows, and scheduled backups do not execute. This is the most common Moodle GKE misconfiguration. |
container_resources.memory_limit | "512Mi" (GKE default) | Critical | The base GKE container_resources.memory_limit default is 512Mi. Moodle requires at least 1Gi to start, and 2–4Gi for production workloads with multiple plugins. A 512Mi limit causes PHP OOM kills immediately on startup. Always override to at least 2Gi. |
container_resources.cpu_limit | "1000m" (GKE default) | Medium | Moodle course rendering and quiz grading are CPU-intensive. Under-provisioning with many concurrent students causes request queuing and timeouts. Increase to at least 2000m for production. |
quota_memory_requests / quota_memory_limits | "" | Critical (GKE-specific) | Must use binary suffixes (Gi, Mi) when set. Bare integers are treated as bytes by Kubernetes, preventing all pods from scheduling. |
min_instance_count | 1 | High | 0 allows scale-to-zero. Moodle cold starts on GKE take 30–60 seconds. Students arriving after idle periods experience request failures or very slow first loads. Keep at 1 for any scheduled live learning activity. |
application_database_name | "moodle" (via application_database_name) | Critical | Immutable after first deployment — changing this recreates the database and destroys all course data, grades, and enrolments. |
application_database_user | "moodle" | Critical | Immutable after first deployment — changing this recreates the user and breaks the application database connection. |
workload_type | null | Medium | Setting stateful_pvc_enabled = true auto-selects StatefulSet. Setting workload_type = "Deployment" alongside stateful_pvc_enabled = true fails at plan time. |
stateful_pvc_size | "10Gi" | Medium | Moodle course files and user submissions accumulate quickly. A site with hundreds of students and video submissions can exhaust 10Gi within weeks. Plan for 100Gi+ for active courses. |
enable_pod_disruption_budget | true | Medium | Already enabled. Disabling allows all pods to be evicted simultaneously during node upgrades, causing a full Moodle outage during an active exam period. |
pdb_min_available | "1" | Medium | With a single replica and pdb_min_available = "1", node upgrades stall indefinitely. Run at least 2 replicas in production to allow rolling maintenance. |
enable_cloud_armor | false | Medium | Moodle login is a target for credential stuffing at scale. Recommended for institutions with student-facing Moodle deployments. |
backup_retention_days | 7 | High | Educational institutions may have compliance requirements for longer data retention. Increase to 90+ days for accredited programmes. |