Moodle plugin development with moodle-docker vs Lando: which environment integrates cleanly with an existing codebase and CI pipeline?
0 reputation · 16 Mar 2022, 04:42 UTC
0 reputation · 16 Mar 2022, 04:42 UTC
Our team maintains several Moodle plugins and wants a repeatable, version-controlled development environment that every developer and our CI pipeline can reproduce identically. We are deciding between the official moodlehq/moodle-docker compose setup and the Lando-based moodle-lando recipe, targeting a recent Moodle 4.x branch with PostgreSQL.
The integration boundary is where the two approaches seem to differ most. As I understand it, moodle-docker mounts the entire Moodle codebase and expects manual config.php adjustments for non-standard ports or reverse proxies, while Lando mounts only the plugin directory by default and assumes a particular directory structure. Our repositories hold only plugin code, not a full Moodle checkout, and CI jobs need to install a pinned Moodle version, run PHPUnit and Behat, and tear everything down deterministically.
Uncertainty: I have not found an official Moodle recommendation covering core versus plugin development for these two options, and I am unsure how each handles pinned PHP/database versions, database persistence across rebuilds, and Xdebug port mapping inside CI runners. Version assumptions here should be verified against the current moodle-docker repository and Lando plugin documentation.
Which environment maps more naturally onto a plugin-only repository layout without restructuring existing projects?
Can either setup pin Moodle, PHP, and database versions declaratively enough that local and CI runs are guaranteed identical?
How do teams typically handle config.php templating and Xdebug in CI with each option?
moodle-docker integrates more cleanly with a plugin-only repository and CI pipeline. It is the officially maintained stack, pins Moodle/PHP/PostgreSQL versions declaratively via git tags and image digests, and the Moodle project's own CI tooling (including moodle-plugin-ci) is built around it. Lando's community Moodle recipe assumes a full Moodle codebase as the app root, so a plugin-only repo requires extra bootstrap scripting that erodes Lando's convenience advantage.
Both tools can achieve identical local/CI runs if you pin image digests (not just tags) and destroy database volumes on teardown. moodle-docker makes this more straightforward because the entire environment — Moodle core checkout, PHP image, database image — is described in version-controlled compose files and env files.
moodle-docker expects a full Moodle checkout mounted at /var/www/html. A plugin-only repo does not need restructuring: in CI you clone a pinned Moodle tag (e.g., MOODLE_405_STABLE), then symlink or copy your plugin into mod/, local/, etc. Locally, developers can keep the Moodle checkout outside the plugin repo and mount the plugin directory via a compose override. No rearrangement of your existing plugin repository is required.
Lando's .lando.yml recipes typically set the app root to the Moodle codebase. To use a plugin-only repo you must script a Moodle core fetch and plugin placement step before lando start, which adds maintenance burden and diverges from the recipe's defaults.
| Component | moodle-docker | Lando |
|---|---|---|
| Moodle core | Git branch/tag checked out in CI / local | Your bootstrap script (not in .lando.yml) |
| PHP | moodlehq/moodle-php-apache:<tag> or digest in compose | php version in .lando.yml |
| PostgreSQL | postgres:<tag> or digest in compose | database version in .lando.yml |
For guaranteed identical runs, pin by image digest (e.g., moodlehq/moodle-php-apache@sha256:…) in a CI-specific compose override. Tags can drift; digests cannot.
The Moodle project's moodle-plugin-ci GitHub Actions workflows demonstrate the standard pattern:
bin/moodle-docker-compose up -d).php admin/tool/phpunit/cli/init.php and php admin/tool/behat/cli/init.php inside the web container.bin/moodle-docker-compose down -v (the -v flag removes named volumes, preventing state leakage).This runs non-interactively on any clean runner. Lando can script the same steps, but you maintain the bootstrap logic yourself.
moodle-docker ships a config.docker-template.php that reads from environment variables (DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASS, WWWROOT, etc.). In CI you provide a .env.ci file (committed or generated) with the compose service names and credentials. Locally, developers use a .env.local with host ports if needed. No hand-editing of config.php is required.
Lando injects its own environment variables (LANDO_INFO, service-specific vars) which you must map into a Moodle-compatible config.php — typically via a custom script or template.
Do not map Xdebug ports in CI. Interactive step debugging requires a fixed host port and an IDE listener — neither exists in a headless runner. Instead:
ENABLE_XDEBUG=1) that adds zend_extension=xdebug and xdebug.mode=coverage to the PHP ini.xdebug.mode=debug, xdebug.client_host=host.docker.internal, xdebug.client_port=9003) in a local-only compose override.moodle-docker's PHP images include Xdebug; toggling it via env is documented in the repo's README.md and bin/moodle-docker-compose helper.
moodlehq/moodle-docker repo and confirm the supported env vars, PHP image tags, and config.docker-template.php match your target Moodle 4.x branch (e.g., MOODLE_405_STABLE).down -v. Confirm it passes on a clean runner.config.php, PHP version (php -v), and PostgreSQL version (psql --version) between a local run and the CI run to confirm parity.Does your CI runner provide host.docker.internal (or equivalent) for local Xdebug connections, or do you need a fixed host IP? This only matters for the local debugging override; it does not affect the CI recommendation.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.