--- title: "How to Install Moodle 4.5 on Ubuntu 24.04 with Nginx" description: "Install Moodle 4.5 LTS on Ubuntu 24.04 with Nginx, PHP-FPM 8.3 and MariaDB, secure it with Let's Encrypt, and set up cron, Redis sessions and nightly backups." category: "Web Applications Hosting" language: en source: https://cubepath.com/docs/web-applications-hosting/moodle-lms-installation-and-configuration updated: 2026-09-25 --- # How to Install Moodle 4.5 on Ubuntu 24.04 with Nginx Moodle is an open source learning management system used by schools, universities and companies to deliver courses, quizzes and assignments online. In this tutorial you will install Moodle 4.5 LTS on Ubuntu 24.04 with Nginx, PHP-FPM 8.3 and MariaDB, run the installer from the command line, secure the site with Let's Encrypt, and configure the scheduled task runner, Redis-backed sessions and nightly backups. ## Prerequisites To follow this guide you need: - A server running Ubuntu 24.04 LTS, for example a CubePath VPS, with at least 2 vCPUs and 4 GB of RAM. Plan disk space for course files: 20 GB is a reasonable start. - A non-root user with `sudo` privileges. - A domain or subdomain (this guide uses `your_domain`) with a DNS `A` record pointing to `your_server_ip`. - Ports 80 and 443 open to the internet. ## Step 1 - Installing Nginx, PHP and MariaDB Moodle 4.5 supports PHP 8.1 to 8.3 and MariaDB 10.6.7 or newer, so the versions in Ubuntu 24.04 (PHP 8.3 and MariaDB 10.11) work directly. Install Nginx, PHP-FPM with the extensions Moodle requires, and MariaDB: ```bash sudo apt update sudo apt install nginx mariadb-server php-fpm php-cli php-mysql php-xml php-mbstring php-curl php-zip php-gd php-intl php-soap php-opcache ``` Confirm the PHP version and that the FPM service is running: ```bash php -v systemctl is-active php8.3-fpm ``` ```output PHP 8.3.6 (cli) (built: ...) (NTS) ... active ``` Open the firewall for SSH and web traffic: ```bash sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw enable ``` ## Step 2 - Tuning PHP for Moodle Moodle refuses to install unless `max_input_vars` is at least 5000, and the default upload limit of 2 MB is too small for course materials. Put the overrides in a separate file for PHP-FPM, which serves the site: ```bash sudo nano /etc/php/8.3/fpm/conf.d/99-moodle.ini ``` ```ini max_input_vars = 5000 memory_limit = 256M upload_max_filesize = 100M post_max_size = 100M ``` The command-line PHP, used by the installer and the cron job, also checks `max_input_vars`: ```bash echo 'max_input_vars = 5000' | sudo tee /etc/php/8.3/cli/conf.d/99-moodle.ini ``` Restart PHP-FPM and verify the new value: ```bash sudo systemctl restart php8.3-fpm php -i | grep max_input_vars ``` ```output max_input_vars => 5000 => 5000 ``` ## Step 3 - Creating the database MariaDB on Ubuntu authenticates `root` through the Unix socket, so you can use `sudo` instead of a password. Run the hardening script first (answer `n` to switching to unix_socket authentication, since it is already in use, and `Y` to the rest): ```bash sudo mariadb-secure-installation ``` Open the MariaDB shell: ```bash sudo mariadb ``` Create the database with the full `utf8mb4` character set and a dedicated user. Replace `your_db_password` with a strong password: ```sql CREATE DATABASE moodle DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'moodleuser'@'localhost' IDENTIFIED BY 'your_db_password'; GRANT ALL PRIVILEGES ON moodle.* TO 'moodleuser'@'localhost'; FLUSH PRIVILEGES; EXIT; ``` Check that the user can connect: ```bash mariadb -u moodleuser -p -e 'SHOW DATABASES;' ``` ```output +--------------------+ | Database | +--------------------+ | information_schema | | moodle | +--------------------+ ``` ## Step 4 - Downloading Moodle and creating the data directory Download the latest 4.5 LTS package from the official download server and extract it into `/var/www`. The archive contains a single `moodle` directory: ```bash cd /tmp wget https://download.moodle.org/download.php/direct/stable405/moodle-latest-405.tgz sudo tar --no-same-owner -xzf moodle-latest-405.tgz -C /var/www/ ``` The code stays owned by `root`, so a compromised PHP process cannot modify it. Moodle keeps uploaded files, caches and sessions in a separate data directory, which must be writable by PHP-FPM (`www-data`) and must never be inside the web root: ```bash sudo install -d -o www-data -g www-data -m 0770 /var/moodledata ls -ld /var/www/moodle /var/moodledata ``` ```output drwxr-xr-x ... root root 4096 ... /var/www/moodle drwxrwx--- ... www-data www-data 4096 ... /var/moodledata ``` ## Step 5 - Configuring Nginx and HTTPS Create a server block for Moodle: ```bash sudo nano /etc/nginx/sites-available/moodle ``` ```nginx server { listen 80; listen [::]:80; server_name your_domain; root /var/www/moodle; index index.php; client_max_body_size 100m; location / { try_files $uri $uri/ =404; } # Files that must not be served, as recommended by Moodle location ~ (/vendor/|/node_modules/|composer\.json|/readme|/README|readme\.txt|/upgrade\.txt|/UPGRADING\.md|db/install\.xml|/fixtures/|/behat/|phpunit\.xml|\.lock|environment\.xml) { return 404; } location ~ /\. { return 404; } # Moodle uses "slash arguments" such as /pluginfile.php/12/... location ~ [^/]\.php(/|$) { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_index index.php; fastcgi_pass unix:/run/php/php8.3-fpm.sock; include fastcgi_params; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_read_timeout 300; } } ``` The PHP location captures the path after the script name into `PATH_INFO`, which Moodle needs to serve course files and images. Enable the site, remove the default one and reload Nginx: ```bash sudo ln -s /etc/nginx/sites-available/moodle /etc/nginx/sites-enabled/ sudo rm /etc/nginx/sites-enabled/default sudo nginx -t sudo systemctl reload nginx ``` Get a certificate before running the installer, because Moodle stores its public URL (`wwwroot`) and you want it to be `https://` from the start: ```bash sudo apt install certbot python3-certbot-nginx sudo certbot --nginx -d your_domain sudo certbot renew --dry-run ``` ## Step 6 - Running the Moodle installer The command-line installer creates the database tables, the administrator account and `config.php` in one run. It needs to write `config.php` into the code directory, so give `www-data` temporary ownership of that directory only (not its contents): ```bash sudo chown www-data /var/www/moodle ``` Run the installer as `www-data`. Replace the placeholders; the admin password must satisfy Moodle's default policy (at least 8 characters with a digit, a lowercase letter, an uppercase letter and a symbol): ```bash cd /var/www/moodle sudo -u www-data php admin/cli/install.php \ --lang=en \ --wwwroot=https://your_domain \ --dataroot=/var/moodledata \ --dbtype=mariadb --dbhost=localhost --dbname=moodle \ --dbuser=moodleuser --dbpass='your_db_password' \ --fullname="Your Site Name" --shortname="Moodle" \ --adminuser=admin --adminpass='your_admin_password' \ --adminemail=admin@your_domain \ --agree-license --non-interactive ``` The installer checks the environment, then installs each component. This takes several minutes: ```output ... -->System ++ Success ++ ... Installation completed successfully. ``` Return the directory to `root` and protect `config.php`, which contains the database password: ```bash sudo chown root /var/www/moodle sudo chown root:www-data /var/www/moodle/config.php sudo chmod 640 /var/www/moodle/config.php ``` Open `https://your_domain` and log in as `admin`. You should see the Moodle dashboard. ## Step 7 - Setting up the cron job Moodle depends on a scheduled task runner for sending forum and notification emails, grading, course backups and cleanup. The recommended frequency is every minute. Create a cron file that runs it as `www-data`: ```bash echo '* * * * * www-data /usr/bin/php /var/www/moodle/admin/cli/cron.php >/dev/null 2>&1' | sudo tee /etc/cron.d/moodle ``` Run it once by hand to make sure it works: ```bash sudo -u www-data php /var/www/moodle/admin/cli/cron.php | tail -n 3 ``` ```output ... Cron run completed correctly Cron completed at ... ``` In the web interface, **Site administration > Notifications** no longer shows the "cron has not been run" warning after a few minutes. ## Step 8 - Storing sessions and caches in Redis By default Moodle keeps sessions and caches as files in `/var/moodledata`. Redis is faster and avoids session locking problems once several people use the site at the same time. Install Redis and the PHP extension: ```bash sudo apt install redis-server php-redis sudo systemctl restart php8.3-fpm redis-cli ping ``` ```output PONG ``` Edit `config.php`: ```bash sudo nano /var/www/moodle/config.php ``` Add the following lines **above** the line `require_once(__DIR__ . '/lib/setup.php');`: ```php $CFG->session_handler_class = '\core\session\redis'; $CFG->session_redis_host = '127.0.0.1'; $CFG->session_redis_port = 6379; $CFG->session_redis_database = 0; $CFG->session_redis_prefix = 'moodle_sess_'; $CFG->session_redis_acquire_lock_timeout = 120; $CFG->session_redis_lock_expire = 7200; ``` Log out and back in, then confirm that sessions appear in Redis: ```bash redis-cli --scan --pattern 'moodle_sess_*' | head -n 3 ``` For the application cache, go to **Site administration > Plugins > Caching > Configuration**, add a **Redis** store instance with server `127.0.0.1` (use a different prefix from the sessions, for example `mdl_cache_`), and then use **Edit mappings** to set it as the store for **Application** caches. ## Step 9 - Scheduling backups A complete Moodle backup includes the database, `config.php` and the data directory. The cache, session and temporary directories inside `moodledata` can be skipped because Moodle rebuilds them. Create a backup script: ```bash sudo nano /usr/local/sbin/moodle-backup ``` ```bash #!/usr/bin/env bash set -euo pipefail umask 077 BACKUP_DIR=/var/backups/moodle STAMP=$(date +%F-%H%M) mkdir -p "$BACKUP_DIR" # Runs as root, so MariaDB authenticates through the Unix socket mariadb-dump --single-transaction --default-character-set=utf8mb4 moodle \ | gzip > "$BACKUP_DIR/moodle-db-$STAMP.sql.gz" tar -czf "$BACKUP_DIR/moodledata-$STAMP.tar.gz" -C /var \ --exclude=moodledata/cache --exclude=moodledata/localcache \ --exclude=moodledata/sessions --exclude=moodledata/temp \ moodledata cp /var/www/moodle/config.php "$BACKUP_DIR/config.php-$STAMP" find "$BACKUP_DIR" -type f -mtime +7 -delete ``` Make it executable, run it and check the files: ```bash sudo chmod 700 /usr/local/sbin/moodle-backup sudo /usr/local/sbin/moodle-backup sudo ls -lh /var/backups/moodle ``` ```output -rw------- 1 root root 1.5K Sep 25 11:40 config.php-2026-09-25-1140 -rw------- 1 root root 9.7M Sep 25 11:40 moodle-db-2026-09-25-1140.sql.gz -rw------- 1 root root 64M Sep 25 11:40 moodledata-2026-09-25-1140.tar.gz ``` Schedule it for a quiet hour: ```bash echo '30 3 * * * root /usr/local/sbin/moodle-backup' | sudo tee /etc/cron.d/moodle-backup ``` The `moodledata` archive grows with every course file, so keep an eye on disk usage and copy the backups to another server or to object storage. ## Troubleshooting - **The installer stops with "max_input_vars must be at least 5000"**: the CLI override from Step 2 is missing. Check with `php -i | grep max_input_vars`. - **Images and course files return 404**: the Nginx PHP location is not passing `PATH_INFO`. Compare your `location ~ [^/]\.php(/|$)` block with the one in Step 5. - **Installing a plugin from the web interface says the directory is not writable**: this is expected, because the code belongs to `root`. Extract the plugin into its directory (for example `/var/www/moodle/mod/`) with `sudo`, then run `sudo -u www-data php /var/www/moodle/admin/cli/upgrade.php --non-interactive`. - **Emails are never sent**: the cron job is not running. Check `/etc/cron.d/moodle` and run the cron script by hand as shown in Step 7. Configure SMTP under **Site administration > Server > Email > Outgoing mail configuration**. ## Conclusion Moodle 4.5 LTS is now running on Ubuntu 24.04 behind Nginx with HTTPS, with its task runner scheduled every minute, sessions in Redis and nightly backups. Next, configure outgoing email, create course categories and your first course, and review **Site administration > Reports > Security checks** to fix any warnings before you invite users.