The container docroot was spelled out in four shell invocations and twice in the nginx vhost, where both sides must agree or nginx serves an empty server block — the 403 this role hit. A single MAGENTO_ROOT now feeds both, and the install guard's mountpoint expression moves next to it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The role could never serve. 01_setup.yml copied nginx.conf.sample inside the
bootstrap shell, but magento/project-community-edition 2.4.8's dist archive is
2583 bytes and holds exactly one file, composer.json - nginx.conf.sample ships
in magento2-base and only lands once composer install runs. Executed, the
bootstrap's own shell says: cp: cannot stat 'nginx.conf.sample'.
set -e made that fatal, and the block's creates: guard made it permanent: it
points at html/composer.json, which create-project had already written, so
every rerun skipped the block, nginx.conf never appeared, the glob include
matched nothing and / returned 403 forever.
This is a regression from the create-project --no-install split: the cp stayed
behind in a step that no longer downloads the package that carries the file.
It now runs after the install with its own creates: on html/nginx.conf, which
also heals volumes the previous code already wedged.
The rest of the topology was proven sound in the same investigation - real
images, real nginx.conf.sample, HTTP/1.1 200 OK through php:9000, and a clean
nginx -t on an empty volume - so nothing else about the vhost changes.
Also here, all found while proving the above:
- placement: manager. 01_setup.yml bootstraps through container exec gated on
IS_STACK_HOST, but the role was not manager-pinned, so in swarm the manager
bootstraps while php and nginx run on a worker roughly two times in three:
volume inspect finds no volume and resolve-container-id returns an ID that
lives on another node. 30 roles already pin this; magento was the outlier.
- --base-url without the trailing slash. lookup('tls', …, 'url.base') already
ends in one (tls.py:89), so every stored link carried // and the session
cookie path became //.
- composer install --no-dev. The run was locking 622 packages including
codeception, allure and phpmd.
- Both nginx restarts now gate on the vhost task actually having changed;
changed_when: rc == 0 made them fire on every single deploy.
- fastcgi buffers on the server block. Magento's sample sets them only on the
main entry point, so /setup/ and /update/ fell back to nginx's 4k default.
- Nine of the ten variants-static-override nochecks were inert - the base
values they claim to override are Jinja, not literals - and their pasted
justification named backup shared on services that have nothing to do with
backup. Only container_backup.shared is load-bearing.
make test green across all four targets. test-lint caught one defect while
this was being written: the new task first used ansible.builtin.command, which
passes the container_address $(...) through literally instead of evaluating
it - the same swarm failure mode the placement fix addresses.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The role could not deliver anything. The markoshust nginx image ships a
default vhost built for a different topology: fastcgi points at
unix:/sock/phpfpm.sock, port 8000 only returns 301 to https, the real
server sits on 8443 with a self-signed cert, and it references an upstream
host 'phpfpm' that does not exist here - so nginx aborted at config load
with 'host not found in upstream' and the container never started.
php-fpm listened on that same unix socket, because its
/usr/local/etc/php-fpm.conf carries no include line and php-fpm.d/*.conf
is therefore dead config.
Both are now replaced through type: config mounts. The role never had any
- that absence is why every template it previously rendered was inert.
The vhost stays minimal and includes Magento's own nginx.conf, copied from
nginx.conf.sample by the bootstrap. Its location blocks carry the .php
whitelist, the /static/version rewrites and the /media to get.php fallback
that a hand-written vhost has to reimplement - the previous attempt did
not, and would have executed every .php under pub/.
PHP moves from 8.5 to 8.4: Magento 2.4.8 requires ~8.2.0||~8.3.0||~8.4.0
and the bootstrap passes no platform-requirement override, so
composer create-project failed before nginx ever mattered.
nginx waits for the php socket before starting, for the reason given in
the erpnext frontend commit: the wildcard search domain turns a DNS miss
into a routable wrong answer, and nginx pins upstreams at config load.
Also: the oauth2 origin port named 8080, where nothing listens, and the
healthcheck proved only that some listener existed - it stayed green on a
redirect loop and on 502.
Measured in a rebuild of the role topology against Magento's real
nginx.conf.sample: HTTP 200 on / and /index.php, healthy healthcheck, and
the counter-test - without the gate and with php down, nginx exits 1.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>