Summary
Deploying the official ghcr.io/tchapi/davis image in a Docker Swarm setup (specifically via a named/managed volume rather than a plain docker compose bind mount on a single host) results in a 500 Internal Server Error on every request, including the login page.
Root cause
- The Dockerfile's final image has
/var/www/davis/var and /var/www/davis/vendor owned by root (composer install / cache warmup run as root during the build, after the initial COPY --chown=${FPM_USER} step).
- The container itself correctly runs as the non-root
www-data user at runtime (confirmed via php-fpm's own log: NOTICE: [pool www] 'user' directive is ignored when FPM is not running as root).
config/packages/framework.yaml registers profiler: collect_serializer_data: true unconditionally — not wrapped in a when@dev: block — so Symfony's profiler is active even with APP_ENV=prod.
- On the first request,
FileProfilerStorage tries to create var/cache/prod/profiler/, which fails because www-data cannot create new directories/files under the root-owned, mode-755 var/cache/prod/.
- Result:
RuntimeException: Unable to create the storage directory (/var/www/davis/var/cache/prod/profiler). on every single request, confirmed via the app's own log:
[...] request.CRITICAL: Uncaught PHP Exception RuntimeException: "Unable to create the storage directory (/var/www/davis/var/cache/prod/profiler)." at FileProfilerStorage.php line 41
This doesn't surface in a typical single-host docker compose up because the named volume there is usually created fresh with different default ownership/timing than in a Swarm-orchestrated deployment where a managed volume mount populates from the image's baked-in ownership.
Suggested fixes (any one of these would resolve it)
- Wrap the
profiler: key in config/packages/framework.yaml under when@dev: (the standard Symfony convention — profiler generally shouldn't run in prod anyway), or add enabled: false for the prod environment explicitly.
- In the Dockerfile,
chown -R ${FPM_USER} /var/www/davis as the very last step (after composer install), so www-data actually owns everything it needs to write to at runtime, regardless of how the deployment orchestrator manages volumes.
Environment
- Image:
ghcr.io/tchapi/davis:5.4.3
- Deployed via Docker Swarm (Easypanel), Postgres backend, nginx in front proxying to php-fpm over the network
APP_ENV=prod, APP_DEBUG unset (defaults to false)
Happy to provide more logs/repro details if useful.
Summary
Deploying the official
ghcr.io/tchapi/davisimage in a Docker Swarm setup (specifically via a named/managed volume rather than a plaindocker composebind mount on a single host) results in a 500 Internal Server Error on every request, including the login page.Root cause
/var/www/davis/varand/var/www/davis/vendorowned byroot(composer install / cache warmup run as root during the build, after the initialCOPY --chown=${FPM_USER}step).www-datauser at runtime (confirmed viaphp-fpm's own log:NOTICE: [pool www] 'user' directive is ignored when FPM is not running as root).config/packages/framework.yamlregistersprofiler: collect_serializer_data: trueunconditionally — not wrapped in awhen@dev:block — so Symfony's profiler is active even withAPP_ENV=prod.FileProfilerStoragetries to createvar/cache/prod/profiler/, which fails becausewww-datacannot create new directories/files under the root-owned, mode-755var/cache/prod/.RuntimeException: Unable to create the storage directory (/var/www/davis/var/cache/prod/profiler).on every single request, confirmed via the app's own log:This doesn't surface in a typical single-host
docker compose upbecause the named volume there is usually created fresh with different default ownership/timing than in a Swarm-orchestrated deployment where a managed volume mount populates from the image's baked-in ownership.Suggested fixes (any one of these would resolve it)
profiler:key inconfig/packages/framework.yamlunderwhen@dev:(the standard Symfony convention — profiler generally shouldn't run in prod anyway), or addenabled: falsefor the prod environment explicitly.chown -R ${FPM_USER} /var/www/davisas the very last step (aftercomposer install), sowww-dataactually owns everything it needs to write to at runtime, regardless of how the deployment orchestrator manages volumes.Environment
ghcr.io/tchapi/davis:5.4.3APP_ENV=prod,APP_DEBUGunset (defaults to false)Happy to provide more logs/repro details if useful.