lakedrops / docker4drupal
Composer Plugin to prepare local Drupal development environment for Docker.
Package info
gitlab.lakedrops.com/composer/plugin/docker4drupal
Type:composer-plugin
pkg:composer/lakedrops/docker4drupal
Requires
- php: >=8.1
- composer-plugin-api: ^2
- ext-json: *
- drupal/core: ^10 || ^11 || ^12
- henrywhitaker3/healthchecks-io: ^1.0
- lakedrops/ahoy: ^1.9||dev-develop
- lakedrops/composer-json-utils: ^2.5.5||dev-develop
- lakedrops/docker-traefik: ^3.3||dev-develop
Requires (Dev)
- composer/composer: ^2
- lakedrops/drupal-environment: ^3.2||dev-develop
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-main
- v3.10.104
- v3.10.103
- v3.10.102
- v3.10.101
- v3.10.100
- v3.10.99
- v3.10.98
- v3.10.97
- v3.10.96
- v3.10.95
- v3.10.94
- v3.10.93
- v3.10.92
- v3.10.91
- v3.10.90
- v3.10.89
- v3.10.88
- v3.10.87
- v3.10.86
- v3.10.85
- v3.10.84
- v3.10.83
- v3.10.82
- v3.10.81
- v3.10.80
- v3.10.79
- v3.10.78
- v3.10.77
- v3.10.76
- v3.10.75
- v3.10.74
- v3.10.73
- v3.10.72
- v3.10.71
- v3.10.70
- v3.10.69
- v3.10.68
- v3.10.67
- v3.10.66
- v3.10.65
- v3.10.64
- v3.10.63
- v3.10.62
- v3.10.61
- v3.10.60
- v3.10.59
- v3.10.58
- v3.10.57
- v3.10.56
- v3.10.55
- v3.10.54
- v3.10.53
- v3.10.52
- v3.10.51
- v3.10.50
- v3.10.49
- v3.10.48
- v3.10.47
- v3.10.46
- v3.10.45
- v3.10.44
- v3.10.43
- v3.10.42
- v3.10.41
- v3.10.40
- v3.10.39
- v3.10.38
- v3.10.37
- v3.10.36
- v3.10.35
- v3.10.34
- v3.10.33
- v3.10.32
- v3.10.31
- v3.10.30
- v3.10.29
- v3.10.28
- v3.10.27
- v3.10.26
- v3.10.25
- v3.10.24
- v3.10.23
- v3.10.22
- v3.10.21
- v3.10.20
- v3.10.19
- v3.10.18
- v3.10.17
- v3.10.16
- v3.10.15
- v3.10.14
- v3.10.13
- v3.10.12
- v3.10.11
- v3.10.10
- v3.10.9
- v3.10.8
- v3.10.7
- v3.10.6
- v3.10.5
- v3.10.4
- v3.10.3
- v3.10.2
- v3.10.1
- v3.10.0
- v3.9.19
- v3.9.18
- v3.9.17
- v3.9.16
- v3.9.15
- v3.9.14
- v3.9.13
- v3.9.12
- v3.9.11
- v3.9.10
- v3.9.9
- v3.9.8
- v3.9.7
- v3.9.6
- v3.9.5
- v3.9.4
- v3.9.3
- v3.9.2
- v3.9.1
- v3.9.0
- v3.8.12
- v3.8.11
- v3.8.10
- v3.8.9
- v3.8.8
- v3.8.7
- v3.8.6
- v3.8.5
- v3.8.4
- v3.8.3
- v3.8.2
- v3.8.1
- v3.8.0
- v3.7.60
- v3.7.59
- v3.7.58
- v3.7.57
- v3.7.56
- v3.7.55
- v3.7.54
- v3.7.53
- v3.7.52
- v3.7.51
- v3.7.50
- v3.7.49
- v3.7.48
- v3.7.47
- v3.7.46
- v3.7.45
- v3.7.44
- v3.7.43
- v3.7.42
- v3.7.41
- v3.7.40
- v3.7.39
- v3.7.38
- v3.7.37
- v3.7.36
- v3.7.35
- v3.7.34
- v3.7.33
- v3.7.32
- v3.7.31
- v3.7.30
- v3.7.29
- v3.7.28
- v3.7.27
- v3.7.26
- v3.7.25
- v3.7.24
- v3.7.23
- v3.7.22
- v3.7.21
- v3.7.20
- v3.7.19
- v3.7.18
- v3.7.17
- v3.7.16
- v3.7.15
- v3.7.14
- v3.7.13
- v3.7.12
- v3.7.11
- v3.7.10
- v3.7.9
- v3.7.8
- v3.7.7
- v3.7.6
- v3.7.5
- v3.7.4
- v3.7.3
- v3.7.2
- v3.7.1
- v3.7.0
- v3.6.7
- v3.6.6
- v3.6.5
- v3.6.4
- v3.6.3
- v3.6.2
- v3.6.1
- v3.6.0
- v3.5.7
- v3.5.6
- v3.5.5
- v3.5.4
- v3.5.3
- v3.5.2
- v3.5.1
- v3.5.0
- v3.4.1
- v3.4.0
- v3.3.11
- v3.3.10
- v3.3.9
- v3.3.8
- v3.3.7
- v3.3.6
- v3.3.5
- v3.3.4
- v3.3.3
- v3.3.2
- v3.3.1
- v3.3.0
- v3.2.5
- v3.2.4
- v3.2.3
- v3.2.2
- v3.2.1
- v3.2.0
- v3.1.6
- v3.1.5
- v3.1.4
- v3.1.3
- v3.1.2
- v3.1.1
- v3.1.0
- v3.0.4
- v3.0.3
- v3.0.2
- v3.0.1
- v3.0.0
- v2.1.0
- v2.0.2
- v2.0.1
- v2.0.0
- v1.17.0
- v1.16.2
- v1.16.1
- v1.16.0
- v1.15.1
- v1.15.0
- v1.14.0
- v1.13.2
- v1.13.1
- v1.13.0
- v1.12.2
- v1.12.1
- v1.12.0
- v1.11.7
- v1.11.6
- v1.11.5
- v1.11.4
- v1.11.3
- v1.11.2
- v1.11.1
- v1.11.0
- v1.10.3
- v1.10.2
- v1.10.1
- v1.10.0
- v1.9.9
- v1.9.8
- v1.9.7
- v1.9.6
- v1.9.5
- v1.9.4
- v1.9.3
- v1.9.2
- v1.9.1
- v1.9.0
- v1.8.8
- v1.8.7
- v1.8.6
- v1.8.5
- v1.8.4
- v1.8.3
- v1.8.2
- v1.8.1
- v1.8.0
- v1.7.0
- v1.6.1
- v1.6.0
- v1.5.0
- v1.4.2
- v1.4.1
- v1.4.0
- v1.3.0
- v1.2.2
- v1.2.1
- v1.2.0
- v1.1.3
- v1.1.2
- v1.1.1
- v1.1.0
- v1.0.1
- 1.0.0
- v0.9.0
- v0.8.7
- v0.8.6
- v0.8.5
- v0.8.4
- v0.8.3
- v0.8.2
- v0.8.1
- v0.8.0
- v0.7.3
- v0.7.2
- v0.7.1
- v0.7.0
- v0.6.0
- v0.5.0
- v0.4.2
- v0.4.1
- v0.4.0
- v0.3.1
- v0.3.0
- v0.2.8
- v0.2.7
- v0.2.6
- v0.2.5
- v0.2.4
- v0.2.3
- v0.2.2
- v0.2.1
- v0.2.0
- v0.1.4
- v0.1.3
- v0.1.2
- v0.1.1
- v0.1.0
- dev-develop
- dev-feature/147-ai-settings-opt-in
- dev-fix/105-revert-playwright-main-merge
- dev-fix/declare-drupal-core-dependency
- dev-95-apache-tika
- dev-issue-94/qdrant
- dev-issue-60/pre-configure-solr
- dev-issue-60/pre-configure-wodby-solr
- dev-nginx/custom-preset
- dev-issue-60/solr-mount-volume
- dev-issue-60/solr
- dev-issue-59
This package is auto-updated.
Last update: 2026-11-14 12:15:37 UTC
README
Composer plugin that renders Docker Compose services for Drupal projects.
Public and private service domains
DOCKER4DRUPAL_TRAEFIK_PRIVATE_DOMAIN affects CI builds only, including
Build Prod Site NG (LAKEDROPS_BUILD_NG=yes). When set, auxiliary services
use <service>-<project>.<private domain>. The Drupal web server retains its
configured project domain. Local development and L3D sandboxes ignore the private
domain variable.
Invoice Ninja always stays public in Build Prod Site NG, including staging
stacks built by that job. Its router and APP_URL both use
invoice-<public project domain>, preserving the URL from before v3.10.98.
For example, invoice-main-example.example.com does not change
when a runner adds a private domain. Other auxiliary services remain private.
The public project domain comes from the existing project configuration; this
change does not construct a different public hostname or add a second router.
Non-production pipeline previews still use the private domain for Invoice Ninja, including their container host mappings. They are test environments, not the customer-facing production application.
This is an explicit, non-configurable exception: servicedomain holds the shared
auxiliary domain, while the computed invoicedomain holds Invoice Ninja's domain.
A configurable per-service exposure switch would let a project accidentally
violate the requirement that customer-facing Invoice Ninja stays public.
OpenTelemetry endpoint address
DOCKER4DRUPAL_OPENTELEMETRY_ADDRESS is the IPv4 address that the php
container uses for the host of OPENTELEMETRY_ENDPOINT, the OTLP endpoint
that lakedrops/drupal-environment configures for the drupal/opentelemetry
module. It is meant for a log server that admits NetBird peers only while its
name still resolves to a public address, for example
DOCKER4DRUPAL_OPENTELEMETRY_ADDRESS=100.64.104.21 with
OPENTELEMETRY_ENDPOINT=https://logs.lakedrops.com/api/default. php then
gets the entry logs.lakedrops.com:100.64.104.21 in its extra_hosts, in the
same list as the names of LOCAL_IP and the private domain.
The mapping applies to pipeline builds, Build Prod Site NG, and local
development, where L3D exports the variables of ~/.env and the project's
.env. L3D sandboxes ignore it. Without the variable, or without a host in
OPENTELEMETRY_ENDPOINT, nothing changes. An address that is not IPv4 is
ignored with a warning.
The log server's own host. There, a request from a container to one of
the host's addresses arrives at Traefik from the gateway of traefik-public,
which Traefik refuses for NetBird-only names. A pipeline build recognizes this
host when the variable equals the runner's
DOCKER4DRUPAL_TRAEFIK_NETBIRD_ADDRESS: php joins traefik-public and maps
the endpoint's host to Traefik's address there, never to the gateway. As with
the private domain, gw_priority: 1 on the project's default network of php
and the web server keeps service names such as php resolving to the own
project once a php of another project joins traefik-public. This needs
Docker 28 and Compose 2.33 or later. The web servers of stacks rendered
without it on the same host, by older releases of this plugin or without these
variables, have no such priority.
Build Prod Site NG always uses the address as given. DeployNG writes the
gateway of traefik-public, not Traefik's address, for
TRAEFIK-IP-PLACEHOLDER, so live and staging stacks on the log server's own
host cannot reach a NetBird-only endpoint there. Such stacks are not
supported.
Invoice Ninja database
in-db runs MariaDB 12.3 LTS (supported until June 2029) and keeps its data in
in/mariadb. Versions up to 3.10.100 used MySQL 5.7 with data in in/db.
Invoice Ninja 5.13 needs window functions and REGEXP_REPLACE, and Laravel's
database queue uses SKIP LOCKED only on MySQL 8.0.1 or later and MariaDB 10.6
or later; without it, queue workers deadlock on the jobs table.
Its healthcheck pings the server over TCP with the root password from the
container's environment, not with the image's healthcheck.sh. In pipeline
workspaces with default ACLs, the image's .my-healthcheck.cnf becomes
world-writable, so the client ignores it and healthcheck.sh fails with
Access denied. TCP also keeps the temporary server of the initialization,
which runs without networking, from counting as healthy.
The new release does not read in/db. An existing project starts with an
empty Invoice Ninja database until its data is migrated; in/db stays
untouched as the rollback copy. To migrate, replacing <root password> with
the MYSQL_ROOT_PASSWORD of in-db:
With the old stack running, stop
in-app(docker compose stop in-app), then dump the database:docker compose exec -T in-db mysqldump -u root -p'<root password>' \ --single-transaction --routines --triggers --hex-blob \ --default-character-set=utf8mb4 ninja > ninja.sqlUpdate the plugin and regenerate
docker-compose.yml.Start only the database, wait until
docker compose ps in-dbreports it healthy, then import the dump:docker compose up -d in-db docker compose exec -T in-db mariadb -u root -p'<root password>' ninja < ninja.sqlStart the remaining services with
docker compose up -d;in-appruns its migrations on startup.- Verify Invoice Ninja, then remove
in/db.
Deployments that start all services at once, such as Build Prod Site NG,
give no chance to import between steps 3 and 4. Pre-seed in/mariadb before
deploying instead: run a temporary container with the same image, environment,
command, and data mount as in-db, import the dump there, and stop it cleanly.
Production jobs install the newest release, so migrate each Invoice Ninja production stack in the same window as the deployment that picks up this release.
AI agent settings
The plugin writes no AI agent settings by default. When enabled, it writes
.claude/mcp.json, .ai/template.md, and .ai/prepare.md, and adds
/.claude/ to .gitignore; ahoy d4d aiprepare then shows how to prepare
AGENTS.md with them. Enable it for a project in .lakedrops.yml:
docker4drupal:
ai:
enable: 1
The same key in .lakedrops.user.yml enables it for one developer only.
Alternatively, set DOCKER4DRUPAL_AI=1 in the project's .env, where the
plugin records the default DOCKER4DRUPAL_AI=0 on its first run. Then run
ahoy d4d update.
Projects that already have these files keep them: without the setting, the
plugin only stops writing them. Remove them, and the /.claude/ entry in
.gitignore, manually if they are no longer wanted.
Tests
Install dependencies without activating Composer plugins in this plugin's own checkout, then run the integration test:
composer install --no-interaction --no-scripts --no-plugins
composer --no-plugins test
composer --no-plugins validate --no-check-publish
for file in src/*.php tests/*.php; do php -l "$file" || exit; done
On an L3D host, run these commands through l3d .. The test uses the real
configuration class and Twig template, parses the generated YAML, and checks
production with and without a private domain, production staging, pipeline
previews, local development, and sandboxes. It checks the Invoice Ninja router
and APP_URL together, other auxiliary routes, and pipeline host mappings.
In the same contexts, it checks that in-db runs MariaDB with a healthcheck
that pings over TCP without healthcheck.sh, keeps its data in in/mariadb,
never in/db, and that in-app waits for a healthy in-db.
For DOCKER4DRUPAL_OPENTELEMETRY_ADDRESS, it checks the entry and the
networks of php in each of these contexts, on the log server's own host and
on another one, and that the file stays the same without the variable,
without a host in the endpoint, or with an address that is not IPv4.
The Service domain regression job runs these checks, Drupal/DrupalPractice
coding standards, and PHPStan (using phpstan.neon) in develop and
feature/* branch pipelines. Its lint tools are installed separately so
they do not alter the plugin's dependencies. The tool installation sets
COMPOSER_BIN_DIR explicitly to .qa/vendor/bin: shared Composer artifacts
export a different bin directory, which must not redirect these isolated tools
into vendor/bin.
The shared validate stage runs in branch pipelines for develop and
feature/*. Merge requests get no pipeline of their own, so a merge request
reflects the pipeline of its source branch. Use a
feature/<issue>-<description> branch and require both its full validate
stage and Service domain regression job before merging; branches outside
feature/* run neither check.
Release flow
Feature and fix merge requests target develop. The shared CI configuration
runs Publish Release on develop unless SKIP_PUBLISH_RELEASE=1; that job
clones develop and invokes the shared merge command for the release merge.
The resulting Merging develop into main merge request is separate from the
feature/fix MR.
The main pipeline's Tag Release job recognizes a commit beginning
Merge branch 'develop' into 'main' (also release into main, or a commit
containing [tag release]). It runs gen-semver and creates the generated tag
on main. [MAJOR_VERSION], [MINOR_VERSION], [PATCH_VERSION], and
[REVISION_VERSION] select an explicit increment; skip-tag suppresses tagging.
The tag pipeline's Build Package Tag job publishes the tag to GitLab's Composer
registry. Do not merge the fix or the release MR, or trigger tagging, until the
change has been reviewed.
After a reviewed release, rebuild the production Compose file and deploy it to restore the public Invoice Ninja route. Production jobs install the newest release; pipeline builds follow each project's lock file. Reverting the change would restore the private production route and break the public Invoice Ninja URL when the private domain is set.