Install and maintain Moodle on Ubuntu
Moodle is installed with Apache, PHP 8.3, MariaDB, a private moodledata directory, cron, TLS, UFW, and Fail2ban.
How this guide was prepared: This is the command-line equivalent of the current Sive AppStore installation playbook. It covers the application installation and the parts you maintain after deployment. Platform provisioning, billing integration, and one-time orchestration are intentionally omitted.
Before you start
- Use a clean, supported Ubuntu server with root or sudo access.
- Point
app.example.comto the server before requesting a public TLS certificate. - Replace every value written as
CHANGE_MEand store the generated credentials in a password manager. - Take a snapshot before changing an existing installation.
- A dedicated database name/user/password and a strong Moodle administrator password.
- Capacity planning for course files and backups outside the public document root.
Important: The moodledata directory must never be web-accessible. Keep it outside the public Moodle code directory.
What the AppStore installation creates
- Moodle code below
/var/www/app.example.com/public_html - Private data below
/var/www/app.example.com/moodledata - MariaDB, Apache, PHP 8.3, Moodle cron, TLS, UFW, and Fail2ban
1. Install Apache, PHP 8.3, and MariaDB
sudo apt update
sudo add-apt-repository -y ppa:ondrej/php
sudo apt update
sudo apt install -y apache2 mariadb-server git unzip \
php8.3 php8.3-cli php8.3-mysql php8.3-curl php8.3-gd php8.3-intl \
php8.3-mbstring php8.3-soap php8.3-xml php8.3-zip php8.3-bcmath \
libapache2-mod-php8.3 certbot python3-certbot-apache ufw fail2ban
2. Create the database and directories
sudo mysql
CREATE DATABASE moodle CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'moodle'@'localhost' IDENTIFIED BY 'CHANGE_ME_DB_PASSWORD';
GRANT ALL PRIVILEGES ON moodle.* TO 'moodle'@'localhost';
FLUSH PRIVILEGES;
EXIT;
sudo install -d -o www-data -g www-data -m 0750 /var/www/app.example.com/public_html
sudo install -d -o www-data -g www-data -m 0770 /var/www/app.example.com/moodledata
3. Install Moodle and create config.php
Download the selected stable Moodle release, verify it, and extract it to the code directory. Configure wwwroot, dataroot, MariaDB credentials, UTF-8, and the database prefix.
curl -fL https://download.moodle.org/download.php/direct/stable500/moodle-latest-500.tgz -o /tmp/moodle-latest-500.tgz
sudo tar -xzf /tmp/moodle-latest-500.tgz --strip-components=1 -C /var/www/app.example.com/public_html
sudo chown -R www-data:www-data /var/www/app.example.com
sudo nano /var/www/app.example.com/public_html/config.php
4. Configure Apache, TLS, and cron
Point Apache to the Moodle code directory, enable rewrite/ssl, then schedule Moodle cron every 10 minutes as www-data.
echo '*/10 * * * * www-data /usr/bin/php /var/www/app.example.com/public_html/admin/cli/cron.php >/dev/null 2>&1' | sudo tee /etc/cron.d/moodle
sudo chmod 0644 /etc/cron.d/moodle
sudo a2enmod rewrite ssl
sudo apache2ctl configtest
sudo systemctl reload apache2
sudo certbot --apache -d app.example.com -m admin@example.com --agree-tos --redirect
Important files and data
- Moodle code and config:
/var/www/app.example.com/public_html - User/course data:
/var/www/app.example.com/moodledata - Cron:
/etc/cron.d/moodle - Apache site and PHP configuration
Health checks and logs
Run these checks after installation and after each upgrade:
sudo -u www-data php /var/www/app.example.com/public_html/admin/cli/cron.php
sudo -u www-data php /var/www/app.example.com/public_html/admin/cli/checks.php
sudo apache2ctl configtest
sudo systemctl status apache2 mariadb --no-pager
Routine maintenance
Review release notes and take a backup or snapshot before upgrading. Use the following playbook-aligned commands as the starting point:
cd /var/www/app.example.com/public_html
sudo -u www-data php admin/cli/maintenance.php --enable
# Back up, deploy the tested release, then:
sudo -u www-data php admin/cli/upgrade.php --non-interactive
sudo -u www-data php admin/cli/purge_caches.php
sudo -u www-data php admin/cli/maintenance.php --disable
Backup scope
- MariaDB Moodle database
- Moodle code including
config.phpand all plugins/themes - The complete moodledata directory
A usable backup needs both application files and application data. Test restoration on a separate server; an untested backup is not a recovery plan.
Troubleshooting
- Confirm DNS with
dig +short app.example.combefore retrying Certbot. - Test the web-server configuration before reloading it:
sudo nginx -torsudo apache2ctl configtest. - Check free space with
df -hand listening ports withsudo ss -ltnup. - If a service fails, inspect its systemd journal before changing configuration.
- Run cron manually as
www-datato reveal failures hidden by cron redirection. - Check Moodle’s environment report before upgrading PHP or Moodle.
Security notes
- Do not paste passwords, API keys, repository credentials, private keys, or access tokens into tickets or public logs.
- Expose only the documented public ports. Keep database and application backend ports bound to localhost or a private network.
- Keep SSH access working before enabling UFW, then allow only the ports this guide lists.
- Renewal can be tested safely with
sudo certbot renew --dry-runwhere Certbot manages TLS.

