This guide walks you through deploying ipMoodle using the deploy.sh automation script.
Overview
The deployment process consists of three main phases:
Environment setup - Generates .env file with database credentials
Infrastructure deployment - Builds Docker images and starts containers
Moodle installation - Downloads and configures Moodle 4.3
The entire deployment process typically takes 5-10 minutes, depending on your internet connection and system resources.
Pre-Deployment Configuration
Set Required Environment Variable
Before running the deployment script, configure your site URL:
export SITE_URL = "https://your-domain.com"
The SITE_URL variable is required . The deployment script will use this value to configure Moodle’s base URL.
Optional: Customize Database Credentials
The deployment script uses default database credentials. For production environments, you should customize these:
export DB_NAME = "moodle"
export DB_USER = "moodle"
export DB_PASS = "moodle" # Change this for production!
By default, the script uses:
Database Name : moodle
Database User : moodle
Database Password : moodle
These are defined in deploy.sh:5-7.
Installation Steps
Navigate to the installation directory
Make the deployment script executable
Run the deployment script
The script will execute the following operations automatically:
Phase 1: Environment Generation
The script creates a .env file with your configuration: cat << EOF > .env
SITE_URL= $SITE_URL
DB_NAME= $DB_NAME
DB_USER= $DB_USER
DB_PASS= $DB_PASS
EOF
This file is used by Docker Compose to configure the containers.
Phase 2: Building Docker Image
This builds the custom PHP-FPM image defined in the Dockerfile:
Base image: php:8.2-fpm-alpine
Installs Moodle-required PHP extensions
Configures PHP settings (512M memory limit, 512M upload size)
Sets up proper permissions for www-data user
Expected duration : 3-5 minutes on first run
Phase 3: Starting Infrastructure
This starts all services:
db : PostgreSQL 16 database
app : PHP-FPM application server
cron : Automated task scheduler
web : Nginx web server
All containers start in detached mode (-d flag).
Phase 4: Moodle Code Download
If the ./html directory is empty, the script automatically downloads Moodle: docker compose exec -T app sh -c "curl -L https://download.moodle.org/download.php/direct/stable403/moodle-latest-403.tgz | tar xz --strip-components=1"
Then sets proper permissions: docker compose exec -T app chown -R www-data:www-data /var/www/html
docker compose exec -T app chown -R www-data:www-data /var/www/moodledata
docker compose exec -T app chmod -R 755 /var/www/html
Expected duration : 2-3 minutes
Verify deployment
Check that all containers are running: You should see four containers:
moodle_db (PostgreSQL)
moodle_app (PHP-FPM)
moodle_cron (Cron scheduler)
moodle_web (Nginx)
All should show status “Up”.
Access Moodle installer
Open your browser and navigate to the URL you configured: You’ll be greeted by the Moodle installation wizard.
Deployment Script Breakdown
Here’s what happens when you run deploy.sh:
#!/bin/bash
# Phase 1: Generate .env file
echo "Generando entorno..."
cat << EOF > .env
SITE_URL= $SITE_URL
DB_NAME= $DB_NAME
DB_USER= $DB_USER
DB_PASS= $DB_PASS
EOF
# Phase 2: Build custom image
echo "--- 1. Construyendo Imagen Propia (Esto tomará unos minutos) ---"
docker compose build
# Phase 3: Start all services
echo "--- 2. Levantando Infraestructura ---"
docker compose up -d
# Phase 4: Download Moodle if needed
echo "--- 3. Verificando Código Moodle ---"
if [ -z "$( ls -A ./html)" ]; then
echo "Descargando Moodle 4.3 (Latest Stable)..."
docker compose exec -T app sh -c "curl -L https://download.moodle.org/download.php/direct/stable403/moodle-latest-403.tgz | tar xz --strip-components=1"
echo "Asignando permisos correctos..."
docker compose exec -T app chown -R www-data:www-data /var/www/html
docker compose exec -T app chown -R www-data:www-data /var/www/moodledata
docker compose exec -T app chmod -R 755 /var/www/html
else
echo "El código ya existe. Omitiendo descarga."
fi
echo -e "\n--- ¡DESPLIEGUE FINALIZADO! ---"
echo "Accede a: $SITE_URL "
echo "Tus archivos están en: /opt/moodleip/html"
What Gets Created
After deployment, your directory structure will look like this:
.
├── deploy.sh
├── docker-compose.yml
├── Dockerfile
├── .env # Generated during deployment
├── html/ # Moodle application files
├── moodledata/ # Moodle data directory
├── db_data/ # PostgreSQL database files
└── nginx/
└── default.conf # Nginx configuration
All data is persisted in local directories, making backups and upgrades straightforward.
Container Services
The deployment creates four interconnected containers:
Database Container
db :
image : postgres:16-alpine
container_name : moodle_db
restart : always
environment :
POSTGRES_DB : ${DB_NAME}
POSTGRES_USER : ${DB_USER}
POSTGRES_PASSWORD : ${DB_PASS}
volumes :
- ./db_data:/var/lib/postgresql/data
Application Container
app :
build : .
container_name : moodle_app
restart : always
environment :
MOODLE_DB_TYPE : pgsql
MOODLE_DB_HOST : db
MOODLE_DB_NAME : ${DB_NAME}
MOODLE_DB_USER : ${DB_USER}
MOODLE_DB_PASSWORD : ${DB_PASS}
MOODLE_URL : ${SITE_URL}
volumes :
- ./html:/var/www/html
- ./moodledata:/var/www/moodledata
Cron Container
cron :
build : .
container_name : moodle_cron
restart : always
command : sh -c "echo '*/1 * * * * /usr/local/bin/php /var/www/html/admin/cli/cron.php > /dev/null 2>&1' > /var/spool/cron/crontabs/www-data && crond -f -l 8"
The cron container runs Moodle’s maintenance tasks every minute, as recommended by Moodle documentation.
Web Container
web :
image : nginx:alpine
container_name : moodle_web
restart : always
ports :
- "80:80"
volumes :
- ./html:/var/www/html:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
Troubleshooting Installation
Port 80 Already in Use
If port 80 is occupied:
# Check what's using port 80
sudo netstat -tlnp | grep :80
# Stop the conflicting service or modify docker-compose.yml
# to use a different port (e.g., "8080:80")
Permission Denied Errors
Ensure your user is in the docker group:
sudo usermod -aG docker $USER
# Log out and back in for changes to take effect
Download Fails
If Moodle download fails, check your internet connection and try manually:
docker compose exec app sh
curl -L https://download.moodle.org/download.php/direct/stable403/moodle-latest-403.tgz -o /tmp/moodle.tgz
tar xzf /tmp/moodle.tgz --strip-components=1 -C /var/www/html
Containers Won’t Start
View container logs:
docker compose logs db
docker compose logs app
docker compose logs web
Next Steps
Once deployment is complete:
Complete the Moodle web-based installation wizard
Review Configuration for post-installation setup
Configure SSL/HTTPS for production environments
The default database password is insecure. Change it immediately for production deployments by modifying the DB_PASS variable in deploy.sh before running the script.