{"id":31078123,"url":"https://github.com/seunayolu/moodle-docker-compose","last_synced_at":"2026-04-08T18:02:12.810Z","repository":{"id":314005851,"uuid":"1053696798","full_name":"seunayolu/moodle-docker-compose","owner":"seunayolu","description":"This project provisions a production-ready Moodle LMS instance on an AWS EC2 t3.medium instance for 200–500 concurrent users. It leverages Docker Compose with five services: Moodle, MySQL, phpMyAdmin, Nginx (reverse proxy), and Certbot (SSL). Includes automated config.php generation via entrypoint script and secure secrets management.","archived":false,"fork":false,"pushed_at":"2025-09-10T11:15:27.000Z","size":637,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-25T10:34:39.487Z","etag":null,"topics":["aws","certbot","devops","docker","docker-compose","letsencrypt","moodle","moodle-lms","mysql","nginx","reverse-proxy"],"latest_commit_sha":null,"homepage":"","language":"Dockerfile","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/seunayolu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-09T19:52:54.000Z","updated_at":"2025-09-10T11:15:32.000Z","dependencies_parsed_at":"2025-09-10T02:50:53.939Z","dependency_job_id":"8073e58b-5bfe-4912-89d4-12cf2a909649","html_url":"https://github.com/seunayolu/moodle-docker-compose","commit_stats":null,"previous_names":["seunayolu/moodle-docker-compose"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/seunayolu/moodle-docker-compose","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seunayolu%2Fmoodle-docker-compose","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seunayolu%2Fmoodle-docker-compose/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seunayolu%2Fmoodle-docker-compose/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seunayolu%2Fmoodle-docker-compose/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seunayolu","download_url":"https://codeload.github.com/seunayolu/moodle-docker-compose/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seunayolu%2Fmoodle-docker-compose/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31567227,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"ssl_error","status_checked_at":"2026-04-08T14:31:17.202Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","certbot","devops","docker","docker-compose","letsencrypt","moodle","moodle-lms","mysql","nginx","reverse-proxy"],"created_at":"2025-09-16T08:04:28.661Z","updated_at":"2026-04-08T18:02:12.773Z","avatar_url":"https://github.com/seunayolu.png","language":"Dockerfile","readme":"# Moodle LMS on AWS with Docker Compose\n\n[![Docker](https://img.shields.io/badge/Docker-Compose-blue?logo=docker\u0026logoColor=white)](https://www.docker.com/)\n[![Moodle](https://img.shields.io/badge/Moodle-LMS-orange?logo=moodle\u0026logoColor=white)](https://moodle.org/)\n[![MySQL](https://img.shields.io/badge/MySQL-8.4.5-blue?logo=mysql\u0026logoColor=white)](https://www.mysql.com/)\n[![Nginx](https://img.shields.io/badge/Nginx-Reverse_Proxy-green?logo=nginx\u0026logoColor=white)](https://nginx.org/)\n[![Certbot](https://img.shields.io/badge/SSL-Let%E2%80%99s_Encrypt-yellow?logo=letsencrypt\u0026logoColor=white)](https://letsencrypt.org/)\n[![AWS](https://img.shields.io/badge/AWS-EC2-orange?logo=amazon-aws\u0026logoColor=white)](https://aws.amazon.com/ec2/)\n\nThis project deploys a **Moodle Learning Management System (LMS)** on an **AWS EC2 instance (t3.medium)** using Docker Compose. It was designed to support an online exam setup for **200–500 concurrent users**.\n\nThe deployment includes a custom Moodle Docker image (built from the [Moodle Git repository](https://docs.moodle.org/500/en/Git_for_Administrators)), MySQL database, phpMyAdmin for database management, Nginx as a reverse proxy, and Let’s Encrypt SSL via Certbot.\n\n## 🚀 Project Overview\n\n* **Moodle Service**: Custom Docker image (`oluwaseuna/runmoodle:1.7`) with an `entrypoint.sh` script that dynamically generates `config.php` using environment variables and secret files.\n* **Database**: MySQL 8.4.5 with persistent storage via Docker named volumes.\n* **phpMyAdmin**: Database management tool to interact with Moodle’s MySQL database.\n* **Nginx**: Reverse proxy with SSL termination.\n* **Certbot**: Automated SSL certificate issuance from Let’s Encrypt.\n\nThis architecture ensures a **secure, scalable, and production-ready Moodle LMS** deployment.\n\n## 🏗️ Architecture\n\nThe architecture of the deployment is documented in the [`architecture`](https://github.com/seunayolu/moodle-docker-compose/tree/main/architecture) folder\n\n## 🏗️ Architecture\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/seunayolu/moodle-docker-compose/blob/main/architecture/moodle-arch.png?raw=true\" alt=\"Moodle Architecture\" width=\"600\"/\u003e\n\u003c/p\u003e\n\n## 📦 Services\n\n### 1. Moodle Service\n\n* Custom Docker image (`oluwaseuna/runmoodle:1.4`).\n* Runs Apache + PHP with Moodle codebase.\n* Uses **entrypoint.sh** to auto-generate `config.php` at container startup.\n* Persists uploaded files and cache in `moodleData` volume.\n\n### 2. MySQL Database\n\n* MySQL 8.4.5 container.\n* Stores Moodle database.\n* Persists data in `cloudDBdata` volume.\n* Reads root and user passwords from secret files in `./secrets/`.\n\n### 3. phpMyAdmin\n\n* Runs on port **8080**.\n* Allows DB administrators to log in with MySQL credentials.\n* Depends on `moodleDB` to be available.\n\n### 4. Nginx\n\n* Acts as reverse proxy.\n* Forwards traffic to Moodle and phpMyAdmin.\n* Handles ports **80** and **443**.\n* Uses bind mounts:\n\n  * `/home/ssm-user/nginx/conf.d`\n  * `/home/ssm-user/nginx/html`\n  * `/home/ssm-user/letsencrypt`\n\n### 5. Certbot\n\n* Obtains SSL certificates from Let’s Encrypt.\n* Shares the same bind mounts as Nginx for certificate storage.\n* Runs once to issue certificates, then exits.\n\n## 🔑 Secrets Management\n\nInstead of Docker secrets, this setup uses **plain text secret files** stored locally inside a `secrets/` folder:\n\n```bash\nmkdir secrets\necho \"secure_root_password\" \u003e secrets/db_root_password.txt\necho \"secure_db_password\" \u003e secrets/db_password.txt\n```\n\nIn `compose.yml`, these files are mounted and read by MySQL and Moodle containers.\n\n## ⚙️ Deployment\n\n### Prerequisites\n\n* AWS EC2 **t3.medium** instance (Ubuntu 24).\n* **AmazonSSMManagedInstanceCore** IAM role attached to the instance profile (for Systems Manager Session Manager access instead of SSH).\n* Domain name pointing to EC2 public IP.\n* Docker \u0026 Docker Compose installed.\n\n### Steps\n\n1. **Clone Repository**\n\n   ```bash\n   git clone https://github.com/seunayolu/moodle-docker-compose.git\n   cd moodle-docker-compose\n   ```\n\n2. **Prepare Environment Variables**\n   Create a `.env` file:\n\n   ```bash\n   MYSQL_DATABASE=moodle\n   MYSQL_USER=moodleuser\n   ```\n\n3. **Create Secret Files**\n\n   ```bash\n   mkdir secrets\n   echo \"secure_root_password\" \u003e secrets/db_root_password.txt\n   echo \"secure_db_password\" \u003e secrets/db_password.txt\n   ```\n\n4. **Prepare Bind Mount Directories**\n\n   ```bash\n   mkdir -p /home/ssm-user/nginx/conf.d /home/ssm-user/nginx/html /home/ssm-user/letsencrypt\n   ```\n\n5. **Start Services**\n\n   ```bash\n   docker compose up -d\n   ```\n\n6. **Access Services**\n\n   * Moodle LMS → `https://moodle.teachdev.online`\n   * phpMyAdmin → `http://\u003cEC2-IP\u003e:8080`\n\n## 📝 Understanding `entrypoint.sh`\n\nThe `entrypoint.sh` script is responsible for **configuring Moodle automatically** when the container starts. Let’s break it down step by step:\n\n```bash\n#!/bin/bash\nset -e\n```\n\n* Uses **bash shell**.\n* `set -e` → stop immediately if any command fails.\n\n```bash\nMOODLE_DIR=/var/www/html\nMOODLE_CONFIG=$MOODLE_DIR/config.php\nMOODLE_DATA=/var/www/moodledata\n```\n\n* Defines paths for Moodle code, config file, and data directory.\n\n```bash\nif [ ! -f \"$MOODLE_CONFIG\" ]; then\n  echo \"Generating Moodle config.php...\"\n  DB_PASS=\"$(cat \"$MOODLE_DATABASE_PASSWORD_FILE\")\"\n```\n\n* Checks if `config.php` already exists.\n* If not, it **creates one**.\n* Reads DB password securely from a file (`/run/secrets/...` or mounted file).\n\n```php\n  cat \u003e \"$MOODLE_CONFIG\" \u003c\u003cEOF\n\u003c?php\nunset(\\$CFG);\nglobal \\$CFG;\n\\$CFG = new stdClass();\n...\nEOF\n```\n\n* Writes a fresh **Moodle config.php** with PHP settings.\n* Sets database connection, site URL (`wwwroot`), and data directory.\n\n```php\n\\$CFG-\u003esslproxy  = true;\n```\n\n* Tells Moodle it’s running behind an SSL-terminating proxy (Nginx).\n\n```bash\nelse\n  echo \"Moodle config.php already exists — skipping generation.\"\nfi\n```\n\n* If config already exists → skip regeneration (avoids overwriting).\n\n```bash\nexec apache2-foreground\n```\n\n* Finally, starts the Apache web server in the foreground → keeps the container alive.\n\n👉 In simple terms:\n**“On first run, generate Moodle’s config file from environment variables and secrets, then start Apache. On later runs, skip config generation and just start Apache.”**\n\n## 🛡️ Security Notes\n\n* Secrets are stored in local files (`./secrets`) and not hardcoded.\n* SSL certificates auto-renew with Certbot.\n* Restrict phpMyAdmin access (firewall or security groups).\n* Use AWS SSM Session Manager for secure access instead of SSH.\n\n## 🔗 Useful Links\n\n* [Moodle Git Repository](https://docs.moodle.org/500/en/Git_for_Administrators)\n* [Certbot Docker Image](https://hub.docker.com/r/certbot/certbot)\n* [Certbot Documentation](https://eff-certbot.readthedocs.io/en/latest/install.html#running-with-docker)\n\n## 📌 Author\n\n**Oluwaseun Alausa**\nDevOps Engineer | Enabling Secure, Scalable, and Observable Infrastructure\n🚀 [LinkedIn](https://www.linkedin.com/in/alausa-oluwaseun) | [YouTube](https://www.youtube.com/@alausaseun)","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseunayolu%2Fmoodle-docker-compose","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseunayolu%2Fmoodle-docker-compose","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseunayolu%2Fmoodle-docker-compose/lists"}