A lightweight Moodle Docker image built on Alpine Linux. (~100MB)
Repository: https://github.com/erseco/alpine-moodle
Key Features
- Built on the lightweight image https://github.com/erseco/alpine-php-webserver
- Compact Docker image size (~100MB)
- Uses PHP 8.3 FPM for better performance, lower cpu usage & memory footprint
- Includes Composer and Moosh CLI
- Support for HA installations: php-redis, php-ldap (also with self-signed certs)
- Multi-arch support: 386, amd64, arm/v6, arm/v7, arm64, ppc64le, s390x
- Optimized for 100 concurrent users
- Optimized to only use resources when there's traffic (by using PHP-FPM's ondemand PM)
- Uses
runit
instead ofsupervisord
to reduce memory footprint - Cron jobs run every 180 seconds by runit
- Sample
docker compose.yml
with PostgreSQL and Redis - Configuration via
ENV
variables - Services (
Nginx
,PHP-FPM
run under a non-privileged user (nobody
) for improved security - Logs are sent to container's STDOUT (
docker logs -f <container>
) - Extensible via pre/post configuration hooks
- Follows the KISS principle (Keep It Simple, Stupid) to make it easy to understand and adjust the image to your needs
From Docker Hub:
docker compose up
Log in using the credentials defined by environment variables.
From GHCR:
services:
moodle:
image: ghcr.io/erseco/alpine-moodle
# rest of your config
In certain situations, you might need to run commands as root
within your Moodle container, for example, to install additional packages. You can do this using the docker compose exec
command with the --user root
option. Here's how:
docker compose exec --user root moodle sh
Define the ENV variables in docker compose.yml file
Variable Name | Default | Description |
---|---|---|
LANG | en_US.UTF-8 | |
LANGUAGE | en_US:en | |
SITE_URL | http://localhost | Sets the public site url |
REVERSEPROXY | false | See Reverse Proxy Configuration. |
SSLPROXY | false | See Reverse Proxy Configuration. |
REDIS_HOST | Set the host of the redis instance. Ej. redis | |
DB_TYPE | pgsql | mysqli - pgsql - mariadb |
DB_HOST | postgres | DB_HOST Ej. db container name |
DB_PORT | 5432 | Postgres=5432 - MySQL=3306 |
DB_NAME | moodle | |
DB_USER | moodle | |
DB_FETCHBUFFERSIZE | Set to 0 if using PostgresSQL poolers like PgBouncer in 'transaction' mode | |
DB_DBHANDLEOPTIONS | false | Set to true if using PostgresSQL poolers like PgBouncer which does not support sending options |
DB_HOST_REPLICA | Database hostname of the read-only replica database | |
DB_PORT_REPLICA | Database port of replica, left it empty to be same as DB_PORT | |
DB_USER_REPLICA | Database login username of replica, left it empty to be same as DB_USER | |
DB_PASS_REPLICA | Database login password of replica, left it empty to be same as DB_PASS | |
DB_PREFIX | mdl_ | Database prefix. WARNING: don't use numeric values or moodle won't start |
MY_CERTIFICATES | none | Trusted LDAP certificate or chain getting through base64 encode |
MOODLE_EMAIL | [email protected] | |
MOODLE_LANGUAGE | en | |
MOODLE_SITENAME | New-Site | |
MOODLE_USERNAME | moodleuser | |
MOODLE_PASSWORD | PLEASE_CHANGEME | |
SMTP_HOST | smtp.gmail.com | |
SMTP_PORT | 587 | |
SMTP_USER | [email protected] | |
SMTP_PASSWORD | your_password | |
SMTP_PROTOCOL | tls | |
MOODLE_MAIL_NOREPLY_ADDRESS | noreply@localhost | |
MOODLE_MAIL_PREFIX | [moodle] | |
AUTO_UPDATE_MOODLE | true | Set to false to disable performing update of Moodle (e.g. plugins) at docker start |
DEBUG | false | |
client_max_body_size | 50M | |
post_max_size | 50M | |
upload_max_filesize | 50M | |
max_input_vars | 5000 | |
PRE_CONFIGURE_COMMANDS | Commands to run before starting the configuration | |
POST_CONFIGURE_COMMANDS | Commands to run after finished the configuration | |
RUN_CRON_TASKS | true | Set to false to disable the moodle cron job from running automatically |
When using a reverse proxy (e.g., Caddy, Traefik, nginx-proxy-manager) that handles SSL termination, you might encounter issues like missing CSS or a "too many redirects" error. This is a common problem and can be solved with the correct configuration.
-
Missing CSS: If you set
SITE_URL
to your internal Docker IP (e.g.,http://172.19.0.1:89
), Moodle will generate URLs with this internal address. When your browser, accessing the site viahttps://moodle.example.com
, tries to load these assets, it will block them due to mixed content (trying to loadhttp
content on anhttps
page). -
ERR_TOO_MANY_REDIRECTS
: If you setSITE_URL
to your public HTTPS address (e.g.,https://moodle.example.com
) but don't configure the proxy settings correctly, Moodle might get confused about the protocol and enter a redirect loop.
The key is to tell Moodle that it's behind an SSL-terminating proxy. You can do this with the following environment variable settings:
SITE_URL
: Set this to your public, external URL with thehttps
scheme (e.g.,https://moodle.example.com
).REVERSEPROXY
: Set this tofalse
for most reverse proxy setups. Set totrue
only if your Moodle site is intentionally accessible from multiple different base URLs (for example, if users access the site using different domain names or protocols). In a typical reverse proxy scenario where all users access Moodle through a single public URL, this should remainfalse
. See Moodle's documentation on reverse proxies for more details.SSLPROXY
: Set this totrue
. This tells Moodle to trust theX-Forwarded-Proto
header from your proxy and understand that the connection is secure, even though the internal connection to the Docker container is over HTTP.
Here's an example of a docker-compose.yml
file and a Caddyfile
for a reverse proxy setup.
docker-compose.yml
services:
moodle:
image: erseco/alpine-moodle
restart: unless-stopped
environment:
SITE_URL: https://moodle.example.com
REVERSEPROXY: "false"
SSLPROXY: "true"
# ... other environment variables
# No ports need to be exposed externally, as Caddy will connect
# to the container directly via the Docker network.
volumes:
- moodledata:/var/www/moodledata
- moodlehtml:/var/www/html
depends_on:
- postgres
# ... other services (postgres, etc.)
volumes:
moodledata:
moodlehtml:
Caddyfile
moodle.example.com {
reverse_proxy moodle:8080 {
header_up Host {host}
header_up X-Forwarded-Proto {scheme}
header_up X-Forwarded-For {remote}
}
}
By using this configuration, you can avoid the common pitfalls of running Moodle behind a reverse proxy. For more details, see issue #101.
---
services:
postgres:
image: postgres:alpine
restart: unless-stopped
environment:
- POSTGRES_PASSWORD=moodle
- POSTGRES_USER=moodle
- POSTGRES_DB=moodle
volumes:
- postgres:/var/lib/postgresql/data
moodle:
image: erseco/alpine-moodle
restart: unless-stopped
environment:
MOODLE_USERNAME: moodleuser
MOODLE_PASSWORD: PLEASE_CHANGEME
ports:
- 80:8080
volumes:
- moodledata:/var/www/moodledata
- moodlehtml:/var/www/html
depends_on:
- postgres
volumes:
postgres: null
moodledata: null
moodlehtml: null
This image includes Moosh — a powerful CLI tool to manage Moodle installations. You can invoke any Moosh command using:
docker compose exec moodle moosh <command>
Examples:
docker compose exec moodle moosh plugin-list
docker compose exec moodle moosh plugin-install mod_attendance
You can force the installation of unsupported plugins with the
--force
option.
NOTE:There is a bug in moosh and the first installation is not working, so we recommend calling again the install function with the
--delete
flag option or use themodule-reinstall
option: eg:docker compose exec moodle moosh plugin-install --delete theme_almondb
or calldocker compose exec moodle moosh module-reinstall theme_almondb
Backup course with provided id. By default, logs and grade histories are excluded.
Example: Backup course id=3 into default .mbz file in /opt/moosh/
directory from container:
docker compose exec moodle moosh course-backup 3
Create a new Moodle user. Provide one or more arguments to create one or more users.
Example: create user "testuser" with the all the optional values
docker compose exec moodle moosh user-create --password pass --email [email protected] --digest 2 --city Valverde --country ES --institution "IES Garoé" --department "Technology" --firstname "first name" --lastname name testuser
Delete user(s) from Moodle. Provide one or more usernames as arguments. Example: delete user testuser
docker compose exec moodle moosh user-delete testuser
These examples can be included directly in POST_CONFIGURE_COMMANDS
to automate plugin installation, backups, or any Moosh-supported functionality.
Using Moosh promotes the DRY (Don't Repeat Yourself) principle and leverages a powerful toolset for Moodle administration.
For the full list of commands, visit: https://moosh-online.com/commands/
You can define commands to be executed before and after the configuration of Moodle using the PRE_CONFIGURE_COMMANDS
and POST_CONFIGURE_COMMANDS
environment variables. These can be useful for tasks such as installing additional packages or running scripts.
environment:
PRE_CONFIGURE_COMMANDS: "cat config-dist.php"
POST_CONFIGURE_COMMANDS: |
moosh plugin-list
moosh plugin-install --delete theme_almondb
moosh plugin-install --delete theme_almondb
Calling docker compose build
uses the latest version of Moodle from the main branch. If you need to use a specific Moodle version, you can specify it using the MOODLE_VERSION
build argument.
To use a specific version, edit the build section for the moodle service in your docker compose.yml file:
moodle:
image: erseco/alpine-moodle
build:
context: .
args:
MOODLE_VERSION: v4.5.3 # Replace with your desired version
You can find the list of available version tags at: https://github.com/moodle/moodle/tags
Moodle includes a tool to create test scenarios under Admin > Development > Create testing scenarios
. To enable it, run the following command, or add it in POST_CONFIGURE_COMMANDS
:
php admin/tool/generator/cli/runtestscenario.php
This tool allows generating all necessary elements for manual testing using .feature
file syntax.
Install Additional Alpine Packages (as root):
docker compose exec --user root moodle sh -c "apk update && apk add nano"
Manual Database Upgrade:
docker compose exec moodle php admin/cli/upgrade.php
Access Logs:
docker compose logs -f moodle