Rebuild the Docker image on Alpine: ~735MB -> ~89MB

Stage 2 was php:8.3-apache (Debian), which compiles PHP from source
with --with-apxs2 for mod_php -- that base image alone is 719MB of
our 735MB, before any app code. Replaced with alpine:3.24 + apk's own
prebuilt php83/php83-apache2/apache2 packages: same architecture (one
process, mod_php, .htaccess-driven rewriting), no fpm/nginx rewrite
needed.

- docker/apache.conf: rewritten for Alpine's apache2 (mod_rewrite ships
  but isn't loaded by default; a different default document root/log
  paths). Logs redirected to stdout/stderr so `docker logs` still shows
  them -- Alpine's own defaults write to a real file under ServerRoot,
  unlike the official Debian image's symlinked paths.
- docker/entrypoint.sh: su-exec instead of su -- BusyBox's su doesn't
  take the same -c/user argument order as the GNU one the previous
  entrypoint relied on. Also moved earlier in the Dockerfile (with the
  other rarely-changing setup, before COPY . .) so it no longer re-runs
  on every build for a file that essentially never changes.
- Composer's binary is still borrowed from the official composer:2
  image via multi-stage COPY, not apk's own `composer` package, which
  turned out to pull in an entire second PHP interpreter (php85) as a
  dependency just to run itself.
- ext-iconv needed adding explicitly (symfony/polyfill-mbstring depends
  on it; the official Debian image bundles it by default, apk doesn't).

Verified against the real compose stack, not just that it builds: apk
install; composer install; migrations on startup; PHPUnit 88/88 (runs
on the host, but confirms nothing else broke); and by hand, all
through the actual container -- health check, SPA fallback for unknown
routes, static assets served directly, the API's 401 guard, and a full
magic-link -> verify -> JWT -> authenticated project create/list round
trip via the real Mailpit catcher.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-05 02:15:39 +01:00
co-authored by Claude Sonnet 5
parent 82e8ee6c36
commit f1309b4c10
6 changed files with 75 additions and 35 deletions
+6 -5
View File
@@ -5,10 +5,11 @@ set -e
# development (see docker-compose.yml); the plain image falls back to ./storage.
STORAGE_DIR="${STORAGE_PATH:-storage}"
mkdir -p "$STORAGE_DIR"
chown -R www-data:www-data "$STORAGE_DIR"
chown -R apache:apache "$STORAGE_DIR"
# Apply pending migrations as www-data so the SQLite file it creates stays
# writable by Apache.
su -s /bin/sh -c 'php bin/migrate.php' www-data
# Apply pending migrations as apache so the SQLite file it creates stays
# writable by the web server. su-exec, not su -- BusyBox's su (this is an
# Alpine image) doesn't take the same -c/user argument order as the GNU one.
su-exec apache php bin/migrate.php
exec docker-php-entrypoint "$@"
exec "$@"