1- # ####### Clients [Screen|Admin] build ########
2- FROM node:24-alpine AS client_app_builder
1+ # ###############################
2+ # Assets build
3+ # ###############################
4+ FROM node:24-alpine AS assets_builder
35LABEL maintainer="ITK Dev <itkdev@ba.aarhus.dk>"
46
57ARG APP_VERSION="develop"
6- ARG APP_RELEASE_TIMESTAMP=0
78
8- ENV APP_CLIENT_PATH=/app
9- WORKDIR ${APP_CLIENT_PATH}
9+ WORKDIR /app
1010
11- # Copy only necessary files for npm install
11+ # Lockfile + vite config first so the npm install layer caches across
12+ # unrelated source edits.
1213COPY --from=repository-root package.json package-lock.json vite.config.js ./
13-
14- # Install dependencies
1514RUN npm ci --no-audit --no-fund
1615
17- # Copy source files needed for build
16+ # Source for `npm run build`. Vite emits to /app/public/build, which the
17+ # api_app_builder stage copies out.
1818COPY --from=repository-root assets/ ./assets/
1919COPY --from=repository-root public/client/ ./public/client/
20-
21- # Build the application with version info
2220RUN npm run build
2321
2422
25- # ######## API backend build ########
23+ # ###############################
24+ # API backend build
25+ # ###############################
26+ # itkdev/php8.4-fpm publishes two parallel series: `2.x.y` (Ubuntu),
27+ # tracked by `:latest`, and `alpine-1.4.x` (Alpine), tracked by
28+ # `:alpine`. `:alpine` is therefore a moving alias to the newest
29+ # `alpine-1.4.x` — convenient, but not reproducible across time.
30+ # TODO: pin to a specific `alpine-1.4.x` tag (or a digest) once we
31+ # settle on a baseline. Same caveat applies to the final stage below.
2632FROM itkdev/php8.4-fpm:alpine AS api_app_builder
2733LABEL maintainer="ITK Dev <itkdev@ba.aarhus.dk>"
2834
2935ARG APP_VERSION="develop"
3036ARG APP_RELEASE_TIMESTAMP=0
3137ARG APP_RELEASE_TIME=""
32- ENV APP_CLIENT_PATH=/app \
33- APP_API_PATH=/var/www/html
3438
3539USER root
3640
37- # Add composer in from the official composer image (also alpine).
41+ # Pinned via the official composer image so the build doesn't depend on
42+ # whatever composer happens to ship in the FPM base.
3843COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer
3944
40- WORKDIR ${APP_API_PATH}
45+ WORKDIR /var/www/html
4146
4247USER deploy
4348
44- # Copy only composer files first for better layer caching
45- COPY --chown=deploy:deploy --from=repository-root composer.json composer.lock symfony.lock ${APP_API_PATH}/
46-
47- # Pre-install composer packages first (better image layer caching) - application code not present so have to be re-run
49+ # Two-pass composer install:
50+ # 1. Lockfile + composer.json only with --no-scripts. The vendor/ layer
51+ # then survives unrelated source-code edits.
52+ # 2. After the full source COPY, re-run with scripts to finalise the
53+ # autoloader and trigger Symfony's cache:clear hook.
54+ COPY --chown=deploy:deploy --from=repository-root composer.json composer.lock symfony.lock ./
4855RUN APP_ENV=prod composer install --no-dev -o --classmap-authoritative --no-scripts
4956
50- # Copy application source (needed for build step)
51- COPY --chown=deploy:deploy --from=repository-root ./ ${APP_API_PATH}/
57+ COPY --chown=deploy:deploy --from=repository-root ./ ./
5258
53- # Copy javascript build files. This ensures that vite manifest files are availiable when "composer insatll"
54- # triggers a "cache:clear" enabling the vite bundle to generate cache configuration files
59+ # Vite manifest must be in place before the second composer install
60+ # triggers cache:clear, otherwise the vite bundle can't generate its
61+ # cache config files.
5562# @see https://symfony-vite.pentatrion.com/guide/performance.html#caching-configuration-files-%F0%9F%8F%83
56- COPY --chown=deploy:deploy --from=client_app_builder ${APP_CLIENT_PATH} /public/build ./public/build
63+ COPY --chown=deploy:deploy --from=assets_builder /app /public/build ./public/build
5764
58- # Re-run composer install after application code copied to image to complete install
5965RUN APP_ENV=prod composer install --no-dev --optimize-autoloader --classmap-authoritative
6066
61- # Write release.json into the public root so the client (assets/shared/release-loader.js)
62- # can fetch it at /release.json. Shape matches docs/release-example.json.
67+ # The client polls / release.json to detect when a screen should refresh;
68+ # shape per docs/release-example.json.
6369RUN printf '{"releaseTimestamp": %s, "releaseTime": "%s", "releaseVersion": "%s"}\n ' \
6470 "${APP_RELEASE_TIMESTAMP}" \
6571 "${APP_RELEASE_TIME}" \
6672 "${APP_VERSION}" \
6773 > public/release.json
6874
6975
70- # ####### PHP-FPM (API) production image ########
76+ # ###############################
77+ # PHP-FPM (API) production image
78+ # ###############################
7179FROM itkdev/php8.4-fpm:alpine
7280LABEL maintainer="ITK Dev <itkdev@ba.aarhus.dk>"
7381
74- ENV APP_CLIENT_PATH=/app \
75- APP_API_PATH=/var/www/html \
76- APP_ENV=prod \
82+ ENV APP_ENV=prod \
7783 PHP_OPCACHE_ENABLED=1 \
7884 PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 \
7985 PHP_OPCACHE_MAX_ACCELERATED_FILES=20000 \
@@ -87,23 +93,25 @@ ENV APP_CLIENT_PATH=/app \
8793
8894USER root
8995
90- # Add composer needed to run optimizations after config is loaded.
96+ # Needed at container start so the entrypoint can run
97+ # `composer dump-env prod` against the operator's runtime env.
9198COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer
9299
93- # Download Prometheus php-fpm export .
100+ # Prometheus exporter for php-fpm pool stats; scraped by ops monitoring .
94101COPY --from=hipages/php-fpm_exporter:2.2.0 /php-fpm_exporter /usr/local/bin/php-fpm_exporter
95102
96103COPY --chmod=0755 docker-entrypoint.sh /usr/local/bin/
97104
98105USER deploy
99106
100- WORKDIR ${APP_API_PATH}
107+ WORKDIR /var/www/html
101108
102- # Install the api application.
103- COPY --chown=deploy:deploy --from=api_app_builder ${APP_API_PATH} .
104- RUN mkdir -p ./config/secrets
109+ COPY --chown=deploy:deploy --from=api_app_builder /var/www/html .
105110
106- WORKDIR ${APP_API_PATH}
111+ # Mount point for the Symfony secrets vault.
112+ RUN mkdir -p ./config/secrets
107113
114+ # Entrypoint compiles env vars to .env.local.php and warms the Symfony
115+ # cache before exec'ing CMD; CMD stays overridable for ad-hoc commands.
108116ENTRYPOINT ["docker-entrypoint.sh" ]
109117CMD ["php-fpm" ]
0 commit comments