# syntax=docker/dockerfile:1.4 # ── Stage 1: extract layers & collect plugins ──────────────────────────── FROM eclipse-temurin:25-jre-alpine AS extractor WORKDIR /builder ARG JAR_FILE=./app/build/libs/app-*.jar COPY ${JAR_FILE} application.jar # Extract using layered layout for optimal Docker layer caching RUN java -Djarmode=tools -jar application.jar extract --layers --destination extracted # Pre-collect plugin JARs so final stage can copy them in a single layer COPY --link ./plugins/ /tmp/plugins/ RUN mkdir -p /builder/plugins \ && find /tmp/plugins -type f -path "*/build/libs/*.jar" -exec cp {} /builder/plugins/ \; \ && rm -rf /tmp/plugins # ── Stage 2: AOT cache training (must use the *runtime* JVM) ───────────── # Boot the full application, wait for readiness, fire warm-up HTTP requests # that exercise Vaadin/Hilla, Spring Security, Hibernate, and PF4J plugin # loading, then gracefully shut down FROM eclipse-temurin:25-jre AS aot-training WORKDIR /training # curl is needed by the training script to poll health and send warm-up requests RUN apt-get update && \ apt-get install -y --no-install-recommends curl && \ rm -rf /var/lib/apt/lists/* # Copy the extracted layers to reassemble the application for the training run COPY --from=extractor /builder/extracted/dependencies/ ./ COPY --from=extractor /builder/extracted/spring-boot-loader/ ./ COPY --from=extractor /builder/extracted/snapshot-dependencies/ ./ COPY --from=extractor /builder/extracted/application/ ./ # Copy plugins COPY --from=extractor /builder/plugins ./plugins # Copy and run the AOT training script # On x86-64, the script passes UseAVX=0 to restrict generated code to baseline # SSE2 so the cache is portable across all amd64 CPUs (skipped on aarch64). COPY --link ./docker/aot-training.sh ./aot-training.sh RUN chmod +x aot-training.sh && ./aot-training.sh # ── Stage 3: runtime image ─────────────────────────────────────────────── FROM eclipse-temurin:25-jre # OCI labels LABEL maintainer="grimsi" \ org.opencontainers.image.title="Gameyfin" \ org.opencontainers.image.description="Manage your video games. simple/fast/FOSS" \ org.opencontainers.image.url="https://gameyfin.org" \ org.opencontainers.image.source="https://github.com/gameyfin/gameyfin" \ org.opencontainers.image.licenses="AGPL-3.0-only" \ org.opencontainers.image.vendor="Gameyfin" \ org.opencontainers.image.authors="Gameyfin maintainers" # Indicate docker runtime environment ENV RUNTIME_ENV=docker # Install necessary packages RUN apt-get update && \ apt-get install -y --no-install-recommends tini gosu && \ rm -rf /var/lib/apt/lists/* # Fixed runtime user/group ENV USER=gameyfin ARG UID=1337 ARG GID=1337 WORKDIR /opt/gameyfin # Create user and required directories with fixed IDs RUN groupadd -g "$GID" "$USER" && \ useradd -u "$UID" -g "$GID" -M -s /usr/sbin/nologin "$USER" && \ mkdir -p plugins plugindata db data logs && \ chown -R "$UID:$GID" . # Copy entrypoint script with proper perms and ownership COPY --link --chown=${UID}:${GID} --chmod=0755 ./docker/entrypoint.ubuntu.sh /entrypoint.sh # Copy application layers from extractor stage COPY --from=extractor --link --chown=${UID}:${GID} /builder/extracted/dependencies/ ./ COPY --from=extractor --link --chown=${UID}:${GID} /builder/extracted/spring-boot-loader/ ./ COPY --from=extractor --link --chown=${UID}:${GID} /builder/extracted/snapshot-dependencies/ ./ COPY --from=extractor --link --chown=${UID}:${GID} /builder/extracted/application/ ./ COPY --from=extractor --link --chown=${UID}:${GID} /builder/plugins ./plugins # Copy AOT cache from training stage COPY --from=aot-training --link --chown=${UID}:${GID} /training/app.aot ./ EXPOSE 8080 ENTRYPOINT ["/usr/bin/tini", "--", "/entrypoint.sh"]