# syntax=docker/dockerfile:1.12 # Multi-stage build for JMusicBot # Stage 1: Build the application # The JAR is architecture-neutral (the natives it bundles are chosen by the pom, # not by the build host), so this stage runs natively on whatever machine is # building — no QEMU for Maven when producing linux/amd64 on Apple Silicon. FROM --platform=$BUILDPLATFORM maven:3.9.12-eclipse-temurin-25-alpine AS builder ARG BUILD_TIMESTAMP ENV BUILD_TIMESTAMP=$BUILD_TIMESTAMP WORKDIR /build # Copy pom.xml first for better layer caching COPY --link pom.xml . # Download dependencies with BuildKit cache mount for Maven repository # This significantly speeds up builds by persisting dependencies between builds RUN --mount=type=cache,target=/root/.m2/repository \ mvn dependency:go-offline -B -Pdocker # Copy source code COPY --link src ./src # Build the application with BuildKit cache mount RUN --mount=type=cache,target=/root/.m2/repository \ if [ -n "$BUILD_TIMESTAMP" ]; then \ mvn clean package -DskipTests -B -Pdocker -Dproject.build.outputTimestamp="$BUILD_TIMESTAMP"; \ else \ mvn clean package -DskipTests -B -Pdocker; \ fi # Stage 2: Create custom minimal JRE using jlink # Using noble (Ubuntu 24.04) for glibc 2.39 compatibility with native libraries FROM eclipse-temurin:25-jdk-noble AS jre-builder # Create a minimal JRE with only the modules JMusicBot needs # Modules determined by: jdeps --print-module-deps --ignore-missing-deps # Plus runtime-loaded modules that jdeps can't detect: # java.base - Core Java classes # java.compiler - Annotation processing (used by some libraries) # java.desktop - GUI support (Swing/AWT, even for headless mode) # java.logging - SLF4J/Logback logging # java.naming - JNDI (required by Logback) # java.net.http - HTTP client API # java.scripting - ScriptEngine for EvalCmd (Rhino) # java.security.jgss - Kerberos/GSS-API security # java.sql - JDBC (used by some dependencies) # jdk.crypto.ec - Elliptic curve crypto for TLS/SSL (Discord API) - runtime loaded # jdk.management - JMX management extensions # jdk.unsupported - Native library access (jdave, udpqueue) RUN $JAVA_HOME/bin/jlink \ --add-modules java.base,java.compiler,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.security.jgss,java.sql,jdk.crypto.ec,jdk.management,jdk.unsupported \ --strip-debug \ --no-man-pages \ --no-header-files \ --compress=zip-6 \ --output /javaruntime # Stage 3: Runtime image # Using Bitnami minideb:trixie (Debian 13) for minimal size with glibc 2.41 # Required for jdave/udpqueue native libraries which need glibc >= 2.38 FROM bitnami/minideb:trixie # Provenance: which commit and version this image was built from. Set by # scripts/build-and-publish-unraid.sh; harmless when left empty. ARG GIT_SHA="" ARG VERSION="" # OCI image labels for better traceability and management LABEL org.opencontainers.image.title="\$uicide Music Bot" \ org.opencontainers.image.description="A cross-platform Discord music bot with a clean interface (JMusicBot fork)" \ org.opencontainers.image.url="https://gitea.milesward.dev/mward4/MusicBot" \ org.opencontainers.image.source="https://gitea.milesward.dev/mward4/MusicBot" \ org.opencontainers.image.documentation="https://gitea.milesward.dev/mward4/MusicBot/src/branch/main/README.md" \ org.opencontainers.image.revision="$GIT_SHA" \ org.opencontainers.image.version="$VERSION" \ org.opencontainers.image.vendor="Miles Ward" \ org.opencontainers.image.licenses="Apache-2.0" # Copy custom JRE from jre-builder stage ENV JAVA_HOME=/opt/java/openjdk ENV PATH="${JAVA_HOME}/bin:${PATH}" COPY --from=jre-builder /javaruntime $JAVA_HOME # Create non-root user and application directories in single layer RUN groupadd --gid 10001 jmusicbot && \ useradd --uid 10001 --gid 10001 --shell /bin/false jmusicbot && \ mkdir -p /app /musicbot && \ chown -R jmusicbot:jmusicbot /app /musicbot # Copy the built JAR from builder stage COPY --from=builder --chown=jmusicbot:jmusicbot /build/target/JMusicBot-*-All.jar /app/app.jar # Copy and set permissions for entrypoint script COPY --chown=jmusicbot:jmusicbot --chmod=755 docker/entrypoint.sh /app/entrypoint.sh WORKDIR /musicbot # Switch to non-root user for security USER jmusicbot ENTRYPOINT ["/app/entrypoint.sh"]