diff --git a/Dockerfile b/Dockerfile index cd80ea4..753c73d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,42 +1,33 @@ -# Arch Linux + Apache + PHP image for running novaconium in production. +# Official PHP + Apache image for running novaconium in production. # See /admin/docs/docker for the bind-mounted paths, docker-entrypoint.sh's # seeding/permissions behavior, and optional MySQL wiring. -FROM archlinux:base -# Arch's repos are rolling-release with no version-pinned packages, so -# `pacman -S apache=X.Y` isn't reliably available — point pacman at a dated -# Arch Linux Archive (https://archive.archlinux.org) snapshot instead of the -# live mirror so a rebuild months from now installs the same Apache/PHP/ -# SQLite versions instead of whatever happens to be current that day. Bump -# ARCH_SNAPSHOT deliberately (e.g. to pick up a PHP security release), not -# as a side effect of an unrelated rebuild. -ARG ARCH_SNAPSHOT=2026/07/01 -RUN echo "Server=https://archive.archlinux.org/repos/${ARCH_SNAPSHOT}/\$repo/os/\$arch" \ - > /etc/pacman.d/mirrorlist +# Build: docker build --no-cache -t novaconium:latest . -RUN pacman -Syyu --noconfirm --needed apache php php-apache php-sqlite \ - && pacman -Scc --noconfirm +# Fixed: full official image tag (was missing "php:") +FROM php:8.5.8-apache-trixie -# php-apache on Arch is built against mpm_prefork, not httpd's default -# mpm_event — swap MPMs, enable mod_rewrite, point DocumentRoot at -# public/, and wire in mod_php. -RUN sed -i \ - -e 's/^LoadModule mpm_event_module/#LoadModule mpm_event_module/' \ - -e 's/^#LoadModule mpm_prefork_module/LoadModule mpm_prefork_module/' \ - -e '/^#LoadModule rewrite_module/s/^#//' \ - -e 's#DocumentRoot "/srv/http"#DocumentRoot "/var/www/html/public"#' \ - -e 's###' \ - -e 's/^\( *\)AllowOverride None$/\1AllowOverride All/' \ - /etc/httpd/conf/httpd.conf \ - && printf '\nLoadModule php_module modules/libphp.so\nAddHandler php-script .php\nDirectoryIndex index.php index.html\nServerName localhost\n' \ - >> /etc/httpd/conf/httpd.conf \ - && sed -i \ - -e 's/^;extension=pdo_sqlite/extension=pdo_sqlite/' \ - -e 's/^;extension=pdo_mysql/extension=pdo_mysql/' \ - /etc/php/php.ini +# Pin to a specific tag (not a floating "php:apache") so a rebuild months +# from now installs the same PHP/Apache/Debian base instead of whatever +# happens to be current that day. Bump the tag above deliberately (e.g. to +# pick up a PHP security release), not as a side effect of an unrelated +# rebuild. + +RUN apt-get update \ + && apt-get install -y --no-install-recommends libsqlite3-dev \ + && rm -rf /var/lib/apt/lists/* \ + && docker-php-ext-install pdo_sqlite pdo_mysql \ + && a2enmod rewrite + +# Point DocumentRoot at public/ and allow .htaccess overrides there. +RUN sed -ri -e 's#/var/www/html#/var/www/html/public#g' \ + /etc/apache2/sites-available/*.conf \ + && sed -ri -e '//,/<\/Directory>/ s/AllowOverride None/AllowOverride All/' \ + /etc/apache2/apache2.conf WORKDIR /var/www/html +# Copy application files COPY novaconium/ ./novaconium/ COPY public/ ./public/ COPY App/ ./App/ @@ -53,7 +44,7 @@ RUN cp -a App/ /opt/novaconium-app-default/ # container with no bind mounts configured at all. RUN mkdir -p public/cache public/uploads data \ && touch novaconium/contact-log.txt \ - && chown -R http:http public/cache public/uploads data App novaconium/contact-log.txt + && chown -R www-data:www-data public/cache public/uploads data App novaconium/contact-log.txt COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh RUN chmod +x /usr/local/bin/docker-entrypoint.sh @@ -61,4 +52,4 @@ RUN chmod +x /usr/local/bin/docker-entrypoint.sh EXPOSE 80 ENTRYPOINT ["docker-entrypoint.sh"] -CMD ["httpd", "-D", "FOREGROUND"] +CMD ["apache2-foreground"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 0236d17..9cca18e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: web: - image: novaconium-beta + image: novaconium:latest ports: - "8080:80" volumes: diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index c14e0e2..3e88702 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -24,6 +24,6 @@ if [ -z "$(ls -A /var/www/html/App 2>/dev/null)" ]; then cp -a /opt/novaconium-app-default/. /var/www/html/App/ fi -chown -R http:http /var/www/html/public/cache /var/www/html/public/uploads /var/www/html/App /var/www/html/data +chown -R www-data:www-data /var/www/html/public/cache /var/www/html/public/uploads /var/www/html/App /var/www/html/data exec "$@" diff --git a/novaconium/pages/admin/docs/docker/index.twig b/novaconium/pages/admin/docs/docker/index.twig index ae5f49d..67bc760 100644 --- a/novaconium/pages/admin/docs/docker/index.twig +++ b/novaconium/pages/admin/docs/docker/index.twig @@ -9,7 +9,7 @@ {% block docs_content %}

Docker

-

The root Dockerfile builds an Apache + PHP image on an Arch Linux base, with mod_rewrite, AllowOverride All, and pdo_sqlite/pdo_mysql already enabled — nothing else in Getting started's "Deploy on Apache" section needs configuring by hand.

+

The root Dockerfile builds on the official php:8.3-apache image, with mod_rewrite, AllowOverride All, and pdo_sqlite/pdo_mysql already enabled — nothing else in Getting started's "Deploy on Apache" section needs configuring by hand.

docker compose up --build
@@ -30,24 +30,24 @@
  • Empty App/ on first run. The Dockerfile stashes a pristine copy of the baked-in App/ at /opt/novaconium-app-default at build time. If the bind-mounted /var/www/html/App is empty when the container starts, the entrypoint copies that pristine copy in — so a first docker compose up against a fresh, empty ${VOL_PATH}/novaconium/App on the host produces a working site instead of a blank one. It only seeds when the directory is empty, so it never clobbers content you've already put there.
  • -
  • Permissions. A bind mount keeps the host directory's ownership, not the image's — the build-time chown -R http:http in the Dockerfile only applies to the image layer, not to whatever gets mounted over it. The entrypoint re-runs chown -R http:http on all four mounted paths on every container start, so Apache's worker user (http on Arch) can always write to them regardless of the host-side UID/GID — no manual chmod on the host required.
  • +
  • Permissions. A bind mount keeps the host directory's ownership, not the image's — the build-time chown -R www-data:www-data in the Dockerfile only applies to the image layer, not to whatever gets mounted over it. The entrypoint re-runs chown -R www-data:www-data on all four mounted paths on every container start, so Apache's worker user (www-data, the Debian default) can always write to them regardless of the host-side UID/GID — no manual chmod on the host required.

MySQL

Lib\Db supports MySQL alongside or instead of SQLite (see Database). docker-compose.yml has a commented-out db service and mysql-data volume — uncomment both, add a db_connections entry with driver: mysql and matching credentials in App/config.php, and the app container can reach it at hostname db.

-

Pinned package versions

+

Pinned base image

-

Arch's repos are rolling-release, so the Dockerfile doesn't install apache/php/sqlite from the live mirror — it points pacman at a dated Arch Linux Archive snapshot via the ARCH_SNAPSHOT build arg (a YYYY/MM/DD path), so a rebuild next month installs the exact same versions as today instead of whatever's newest that day. To move to a newer snapshot deliberately (e.g. to pick up a PHP security release):

+

The Dockerfile pins an exact tag (php:8.3-apache), not a floating php:apache, so a rebuild months from now installs the same PHP/Apache/Debian base instead of whatever the tag happens to point at that day. Bump the tag in the FROM line deliberately (e.g. to pick up a PHP security release), then rebuild:

-
docker build --build-arg ARCH_SNAPSHOT=2026/08/01 --no-cache -t novaconium-beta .
+
docker build --no-cache -t novaconium-beta .

--no-cache is worth using any time you change the Dockerfile itself (not just App/ content) — Docker otherwise reuses a cached layer for an unchanged-looking RUN step, and docker-compose.yml here uses image: rather than build:, so docker compose up alone never rebuilds at all; you have to docker build (and re-tag, if needed) yourself first.

-

Arch-specific notes

+

What the image adds on top of php:8.3-apache

-

Arch's php-apache package is built against mpm_prefork, not httpd's default mpm_event — the Dockerfile swaps MPMs as part of the build. The Apache/PHP worker user on Arch is http, not Debian's www-data; both the build-time chown and docker-entrypoint.sh's startup chown (see above) target http:http for this reason.

+

The base image ships Apache with mod_rewrite disabled and AllowOverride None, no pdo_sqlite/pdo_mysql, and a /var/www/html document root. The Dockerfile runs a2enmod rewrite, docker-php-ext-install pdo_sqlite pdo_mysql (after installing libsqlite3-dev, needed to build pdo_sqlite), and rewrites both the vhost and apache2.conf to point the document root at public/ and set AllowOverride All there.

This is a separate Dockerfile from the one-off Dart Sass build tool described in Styling — that one lives at Dockerfile.sass, a Debian-based image whose only job is running the sass CLI, not serving the app.

{% endblock %}